1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
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/>.
32 #include <sys/socket.h>
41 #include <sys/types.h>
43 #include <sys/select.h>
67 #define FINISH(rc) (gpg_err_source(rc) == GPG_ERR_SOURCE_UNKNOWN) \
75 ssize_t
hook_read(assuan_context_t ctx
, assuan_fd_t fd
, void *data
,
79 pwm_t
*pwm
= assuan_get_pointer(ctx
);
81 if (pwm
&& pwm
->tcp_conn
)
82 return read_hook_ssh(pwm
, fd
, data
, len
);
85 return read((int)fd
, data
, len
);
88 ssize_t
hook_write(assuan_context_t ctx
, assuan_fd_t fd
, const void *data
,
93 pwm_t
*pwm
= assuan_get_pointer(ctx
);
95 if (pwm
&& pwm
->tcp_conn
)
96 return write_hook_ssh(pwm
, fd
, data
, len
);
99 /* libassuan cannot handle EAGAIN when doing writes. */
101 wrote
= write((int)fd
, data
, len
);
103 if (wrote
== -1 && errno
== EAGAIN
) {
106 } while (wrote
== -1 && errno
== EAGAIN
);
111 pid_t
hook_waitpid(assuan_context_t ctx
, pid_t pid
, int action
, int *status
,
114 return waitpid(pid
, status
, options
);
117 gpg_error_t
pwmd_init()
119 static int initialized
;
128 bindtextdomain("libpwmd", LOCALEDIR
);
138 gpg_error_t
_connect_finalize(pwm_t
*pwm
)
142 int n
= assuan_get_active_fds(pwm
->ctx
, 0, active
, N_ARRAY(active
));
145 return GPG_ERR_EBADFD
;
149 pwm
->pinentry_pid
= -1;
153 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "OPTION NAME=%s", pwm
->name
);
158 static gpg_error_t
connect_uds(pwm_t
*pwm
, const char *path
)
160 char *socketpath
= NULL
;
166 return GPG_ERR_INV_ARG
;
168 pwbuf
= _getpwuid(&pw
);
170 return gpg_error_from_syserror();
173 socketpath
= pwmd_strdup_printf("%s/.pwmd/socket", pw
.pw_dir
);
175 socketpath
= _expand_homedir((char *)path
, &pw
);
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
)
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
,
201 NULL
, //sendmsg both are used for FD passing
209 rc
= assuan_new_ext(&pwm
->ctx
, GPG_ERR_SOURCE_DEFAULT
, &mhooks
, NULL
, NULL
);
213 assuan_set_pointer(pwm
->ctx
, pwm
);
214 assuan_ctx_set_system_hooks(pwm
->ctx
, &shooks
);
219 gpg_error_t
pwmd_connect(pwm_t
*pwm
, const char *url
, const char *identity
,
220 const char *knownhosts
)
222 char *p
= (char *)url
;
226 return FINISH(GPG_ERR_INV_ARG
);
227 else if (!pwm
->ctx
) {
228 rc
= init_handle(pwm
);
233 rc
= GPG_ERR_UNSUPPORTED_PROTOCOL
;
236 rc
= connect_uds(pwm
, p
);
237 else if (!p
|| !strncmp(p
, "file://", 7)) {
240 rc
= connect_uds(pwm
, p
);
242 else if (!strncmp(p
, "ssh://", 6) || !strncmp(p
, "ssh6://", 7) ||
243 !strncmp(p
, "ssh4://", 7)) {
245 return FINISH(GPG_ERR_NOT_IMPLEMENTED
);
249 char *username
= NULL
;
251 if (!strncmp(p
, "ssh6://", 7)) {
252 pwm
->prot
= PWMD_IPV6
;
255 else if (!strncmp(p
, "ssh4://", 7)) {
256 pwm
->prot
= PWMD_IPV4
;
260 pwm
->prot
= PWMD_IP_ANY
;
264 /* X11 forwarding is not supported. */
265 pwmd_setopt(pwm
, PWMD_OPTION_NO_PINENTRY
, 1);
266 rc
= _parse_ssh_url(p
, &host
, &port
, &username
);
268 rc
= _do_ssh_connect(pwm
, host
, port
, identity
, username
, knownhosts
);
270 rc
= _connect_finalize(pwm
);
272 _free_ssh_conn(pwm
->tcp_conn
);
273 pwm
->tcp_conn
= NULL
;
287 static void disconnect(pwm_t
*pwm
)
289 if (!pwm
|| !pwm
->ctx
)
292 assuan_release(pwm
->ctx
);
294 _ssh_disconnect(pwm
);
300 void pwmd_close(pwm_t
*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
);
319 _free_ssh_conn(pwm
->tcp_conn
);
324 _pinentry_disconnect(pwm
);
330 static gpg_error_t
inquire_realloc_cb(void *data
, const void *buffer
,
333 membuf_t
*mem
= (membuf_t
*)data
;
339 if ((p
= pwmd_realloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
340 return gpg_error(GPG_ERR_ENOMEM
);
343 memcpy((char *)mem
->buf
+ mem
->len
, buffer
, len
);
348 static gpg_error_t
get_password(pwm_t
*pwm
, char **result
, pwmd_pinentry_t w
,
351 char buf
[LINE_MAX
] = {0}, *p
;
352 struct termios told
, tnew
;
357 if (!isatty(STDIN_FILENO
)) {
358 fprintf(stderr
, N_("Input is not from a terminal! Failing.\n"));
359 return GPG_ERR_ENOTTY
;
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) {
373 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
374 return gpg_error_from_errno(n
);
379 case PWMD_PINENTRY_OPEN
:
380 fprintf(stderr
, N_("Password for %s: "), pwm
->filename
);
382 case PWMD_PINENTRY_OPEN_FAILED
:
383 fprintf(stderr
, N_("Invalid password. Password for %s: "),
386 case PWMD_PINENTRY_SAVE
:
387 fprintf(stderr
, N_("New password for %s: "), pwm
->filename
);
389 case PWMD_PINENTRY_SAVE_CONFIRM
:
390 fprintf(stderr
, N_("Confirm password: "));
396 if ((p
= fgets(buf
, sizeof(buf
), stdin
)) == NULL
) {
397 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
402 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
406 return GPG_ERR_CANCELED
;
409 p
[strlen(p
) - 1] = 0;
412 key
= pwmd_strdup_printf("%s", p
);
413 memset(&buf
, 0, sizeof(buf
));
416 return GPG_ERR_ENOMEM
;
423 gpg_error_t
pwmd_password(pwm_t
*pwm
, const char *keyword
, char **data
,
427 int new_password
= 0;
429 char *password
= NULL
, *newpass
= NULL
;
432 if (!strcmp(keyword
, "NEW_PASSPHRASE"))
435 if (!new_password
&& pwm
->pinentry_try
)
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);
446 pwmd_pinentry_t which
;
449 which
= new_password
? PWMD_PINENTRY_SAVE_FAILED
: PWMD_PINENTRY_OPEN_FAILED
;
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"));
467 password
= newpass
= NULL
;
473 (void)pwmd_getpin(pwm
, pwm
->filename
, NULL
, NULL
, PWMD_PINENTRY_CLOSE
);
476 // An empty passphrase on a protected key is not allowed by gpg-agent.
477 if (!password
&& !new_password
)
478 rc
= GPG_ERR_CANCELED
;
481 *size
= password
? strlen(password
) : 0;
488 static gpg_error_t
inquire_cb(void *data
, const char *keyword
)
490 pwm_t
*pwm
= (pwm_t
*)data
;
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
);
503 int new_password
= 0;
507 if (!strcmp(keyword
, "PASSPHRASE"))
509 else if (!strcmp(keyword
, "NEW_PASSPHRASE"))
512 if (!pwm
->override_inquire
&& (is_password
|| new_password
)) {
514 rc
= pwmd_password(data
, keyword
, &result
, &len
);
519 rc
= pwm
->inquire_func(pwm
->inquire_data
, keyword
, rc
, &result
,
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);
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.
549 if (gpg_err_code(rc
) == GPG_ERR_EOF
|| !rc
) {
550 if (len
<= 0 && !result
) {
554 else if ((len
<= 0 && result
) || (len
&& !result
)) {
555 rc
= gpg_error(GPG_ERR_INV_ARG
);
559 arc
= assuan_send_data(pwm
->ctx
, result
, len
);
560 if (gpg_err_code(rc
) == GPG_ERR_EOF
) {
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
,
578 rc
= pwm
->status_func(pwm
->status_data
, buf
);
591 static gpg_error_t
parse_assuan_line(pwm_t
*pwm
)
597 rc
= assuan_read_line(pwm
->ctx
, &line
, &len
);
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] == ' ')) {
620 static void reset_handle_state(pwm_t
*pwm
, int done
)
624 pwm
->tcp_conn
->rc
= 0;
626 if (done
&& pwm
->tcp_conn
) {
627 _free_ssh_conn(pwm
->tcp_conn
);
628 pwm
->tcp_conn
= NULL
;
633 static void reset_handle(pwm_t
*h
)
638 _pinentry_disconnect(h
);
640 reset_handle_state(h
, 0);
643 gpg_error_t
pwmd_disconnect(pwm_t
*pwm
)
646 return FINISH(GPG_ERR_INV_ARG
);
649 if (pwm
->fd
== -1 && pwm
->tcp_conn
&& pwm
->tcp_conn
->fd
== -1)
653 return FINISH(GPG_ERR_INV_STATE
);
659 _ssh_disconnect(pwm
);
666 /* Note that this should only be called when not in a command. */
667 gpg_error_t
pwmd_process(pwm_t
*pwm
)
671 struct timeval tv
= {0, 0};
675 return FINISH(GPG_ERR_INV_ARG
);
677 return FINISH(GPG_ERR_INV_STATE
);
681 int n
= libssh2_keepalive_send(pwm
->tcp_conn
->session
, NULL
);
684 return FINISH(GPG_ERR_ETIMEDOUT
);
689 FD_SET(pwm
->fd
, &fds
);
690 n
= select(pwm
->fd
+1, &fds
, NULL
, NULL
, &tv
);
693 return FINISH(gpg_error_from_syserror());
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
);
706 gpg_error_t
_assuan_command(pwm_t
*pwm
, assuan_context_t ctx
,
707 char **result
, size_t *len
, const char *cmd
)
713 return FINISH(GPG_ERR_INV_ARG
);
715 if (strlen(cmd
) >= ASSUAN_LINELENGTH
+1)
716 return FINISH(GPG_ERR_LINE_TOO_LONG
);
720 rc
= assuan_transact(ctx
, cmd
, inquire_realloc_cb
, &data
,
722 pwm
->pctx
== ctx
? pwm
->_inquire_func
: inquire_cb
,
723 pwm
->pctx
== ctx
? pwm
->_inquire_data
: pwm
,
727 pwm
->status_func
, pwm
->status_data
);
737 inquire_realloc_cb(&data
, "", 1);
740 *result
= (char *)data
.buf
;
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
)
760 return FINISH(GPG_ERR_INV_ARG
);
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.
769 len
= vsnprintf(NULL
, 0, cmd
, ap
)+1;
770 buf
= (char *)pwmd_malloc(len
);
773 return FINISH(GPG_ERR_ENOMEM
);
776 len
= vsnprintf(buf
, len
, cmd
, 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
);
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
, ...)
798 return FINISH(GPG_ERR_INV_ARG
);
800 return FINISH(GPG_ERR_INV_STATE
);
806 gpg_error_t rc
= pwmd_command_ap(pwm
, result
, len
, func
, user
, cmd
, ap
);
811 static gpg_error_t
send_pinentry_options(pwm_t
*pwm
)
815 if (pwm
->pinentry_tty
) {
816 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "OPTION TTYNAME=%s", pwm
->pinentry_tty
);
821 if (pwm
->pinentry_term
) {
822 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "OPTION TTYTYPE=%s", pwm
->pinentry_term
);
827 if (pwm
->pinentry_display
) {
828 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "OPTION DISPLAY=%s",
829 pwm
->pinentry_display
);
834 if (pwm
->pinentry_desc
) {
835 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "OPTION DESC=%s",
841 if (pwm
->pinentry_lcctype
) {
842 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "OPTION LC_CTYPE=%s",
843 pwm
->pinentry_lcctype
);
848 if (pwm
->pinentry_lcmessages
) {
849 rc
= pwmd_command(pwm
, NULL
, NULL
, NULL
, NULL
, "OPTION LC_MESSAGES=%s",
850 pwm
->pinentry_lcmessages
);
858 gpg_error_t
pwmd_socket_type(pwm_t
*pwm
, pwmd_socket_t
*result
)
861 return FINISH(GPG_ERR_INV_ARG
);
864 if ((pwm
->fd
== -1 && !pwm
->tcp_conn
) ||
865 (pwm
->fd
== -1 && pwm
->tcp_conn
&& pwm
->tcp_conn
->fd
== -1))
869 return FINISH(GPG_ERR_INV_STATE
);
872 *result
= pwm
->tcp_conn
? PWMD_SOCKET_SSH
: PWMD_SOCKET_LOCAL
;
874 *result
= PWMD_SOCKET_LOCAL
;
879 gpg_error_t
pwmd_open(pwm_t
*pwm
, const char *filename
, pwmd_inquire_cb_t cb
,
884 int no_pinentry
= pwm
->disable_pinentry
|| pwm
->tcp_conn
|| pwm
->local_pinentry
;
886 int no_pinentry
= pwm
->disable_pinentry
|| pwm
->local_pinentry
;
889 if (!pwm
|| !filename
|| !*filename
)
890 return FINISH(GPG_ERR_INV_ARG
);
893 return FINISH(GPG_ERR_INV_STATE
);
896 rc
= send_pinentry_options(pwm
);
899 pwm
->pinentry_try
= 0;
900 pwmd_free(pwm
->filename
);
901 pwm
->filename
= pwmd_strdup(filename
);
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;
913 pwmd_free(pwm
->filename
);
914 pwm
->filename
= NULL
;
921 gpg_error_t
pwmd_save(pwm_t
*pwm
, const char *args
, pwmd_inquire_cb_t cb
,
927 return FINISH(GPG_ERR_INV_ARG
);
929 return FINISH(GPG_ERR_INV_STATE
);
931 rc
= pwmd_command(pwm
, NULL
, NULL
, cb
, data
, "SAVE %s",
936 gpg_error_t
pwmd_setopt(pwm_t
*pwm
, pwmd_option_t opt
, ...)
944 return FINISH(GPG_ERR_INV_ARG
);
949 case PWMD_OPTION_LOCK_ON_OPEN
:
953 rc
= GPG_ERR_INV_VALUE
;
956 pwm
->opts
|= OPT_LOCK_ON_OPEN
;
958 pwm
->opts
&= ~OPT_LOCK_ON_OPEN
;
961 case PWMD_OPTION_INQUIRE_TOTAL
:
962 pwm
->inquire_total
= va_arg(ap
, size_t);
964 case PWMD_OPTION_STATUS_CB
:
965 pwm
->status_func
= va_arg(ap
, pwmd_status_cb_t
);
967 case PWMD_OPTION_STATUS_DATA
:
968 pwm
->status_data
= va_arg(ap
, void *);
970 case PWMD_OPTION_NO_PINENTRY
:
974 rc
= GPG_ERR_INV_VALUE
;
976 pwm
->disable_pinentry
= n
;
979 case PWMD_OPTION_LOCAL_PINENTRY
:
983 rc
= GPG_ERR_INV_VALUE
;
985 pwm
->local_pinentry
= n
;
988 case PWMD_OPTION_PINENTRY_TIMEOUT
:
992 rc
= GPG_ERR_INV_VALUE
;
994 pwm
->pinentry_timeout
= n
;
997 case PWMD_OPTION_PINENTRY_TRIES
:
999 pwm
->pinentry_tries
= n
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
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
;
1047 case PWMD_OPTION_KNOWNHOST_CB
:
1048 pwm
->kh_cb
= va_arg(ap
, pwmd_knownhost_cb_t
);
1050 case PWMD_OPTION_KNOWNHOST_DATA
:
1051 pwm
->kh_data
= va_arg(ap
, void *);
1053 case PWMD_OPTION_SSH_AGENT
:
1054 pwm
->use_agent
= va_arg(ap
, int);
1056 if (pwm
->use_agent
< 0 || pwm
->use_agent
> 1) {
1058 rc
= GPG_ERR_INV_VALUE
;
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
;
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
;
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
;
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
;
1097 rc
= GPG_ERR_UNKNOWN_OPTION
;
1105 gpg_error_t
pwmd_new(const char *name
, pwm_t
**pwm
)
1107 pwm_t
*h
= pwmd_calloc(1, sizeof(pwm_t
));
1111 return FINISH(GPG_ERR_ENOMEM
);
1114 h
->name
= pwmd_strdup(name
);
1117 return FINISH(GPG_ERR_ENOMEM
);
1122 h
->pinentry_timeout
= -30;
1123 h
->pinentry_tries
= 3;
1125 h
->prot
= PWMD_IP_ANY
;
1128 if (ttyname(STDOUT_FILENO
)) {
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
;
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
;
1147 if (getenv("DISPLAY")) {
1148 h
->pinentry_display
= pwmd_strdup(getenv("DISPLAY"));
1149 if (!h
->pinentry_display
) {
1150 rc
= GPG_ERR_ENOMEM
;
1155 update_pinentry_settings(h
);
1164 void pwmd_free(void *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
, ...)
1200 len
= vsnprintf(NULL
, 0, fmt
, ap
);
1202 buf
= pwmd_malloc(++len
);
1204 vsnprintf(buf
, len
, fmt
, ap2
);
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
);
1216 gpg_error_t rc
= _pwmd_getpin(pwm
, filename
, result
, len
, which
);
1222 const char *pwmd_version()
1224 return LIBPWMD_VERSION_STR
;
1227 unsigned int pwmd_features()
1231 #ifdef WITH_PINENTRY
1232 n
|= PWMD_FEATURE_PINENTRY
;
1235 n
|= PWMD_FEATURE_SSH
;
1238 n
|= PWMD_FEATURE_CRACK
;