1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006-2009 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 <sys/types.h>
29 static int gelapsed
, gtimeout
;
30 static gpg_error_t grc
;
32 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, int which
);
34 static void update_pinentry_settings(pwm_t
*pwm
)
40 char *pwbuf
= _getpwuid(&pw
);
45 snprintf(buf
, sizeof(buf
), "%s/.pwmd/pinentry.conf", pw
.pw_dir
);
47 if ((fp
= fopen(buf
, "r")) == NULL
) {
52 while ((p
= fgets(buf
, sizeof(buf
), fp
)) != NULL
) {
53 char name
[32], val
[256];
55 if (sscanf(p
, " %31[a-zA-Z] = %255s", name
, val
) != 2)
58 if (strcasecmp(name
, "TTYNAME") == 0) {
59 if (pwm
->pinentry_tty
)
60 pwmd_free(pwm
->pinentry_tty
);
61 pwm
->pinentry_tty
= pwmd_strdup(val
);
63 if (!pwm
->pinentry_tty
)
66 else if (strcasecmp(name
, "TTYTYPE") == 0) {
67 if (pwm
->pinentry_term
)
68 pwmd_free(pwm
->pinentry_term
);
69 pwm
->pinentry_term
= pwmd_strdup(val
);
71 if (!pwm
->pinentry_term
)
74 else if (strcasecmp(name
, "DISPLAY") == 0) {
75 if (pwm
->pinentry_display
)
76 pwmd_free(pwm
->pinentry_display
);
77 pwm
->pinentry_display
= pwmd_strdup(val
);
79 if (!pwm
->pinentry_display
)
82 else if (strcasecmp(name
, "PATH") == 0) {
83 if (pwm
->pinentry_path
)
84 pwmd_free(pwm
->pinentry_path
);
85 pwm
->pinentry_path
= _expand_homedir(val
, &pw
);
87 if (pwm
->pinentry_path
)
97 static gpg_error_t
launch_pinentry(pwm_t
*pwm
)
100 assuan_context_t ctx
;
101 int child_list
[] = {-1};
102 char *display
= getenv("DISPLAY");
103 const char *argv
[10];
104 const char **p
= argv
;
105 int have_display
= 0;
109 update_pinentry_settings(pwm
);
111 if (pwm
->pinentry_display
|| display
)
114 if (!pwm
->pinentry_tty
) {
115 ttybuf
= pwmd_malloc(255);
118 return gpg_error_from_errno(ENOMEM
);
120 if (!ttyname_r(STDOUT_FILENO
, ttybuf
, 255))
126 tty
= pwm
->pinentry_tty
;
129 if (!have_display
&& !tty
)
130 return GPG_ERR_ENOTTY
;
133 *p
++ = have_display
? "--display" : "--ttyname";
134 *p
++ = have_display
? pwm
->pinentry_display
? pwm
->pinentry_display
: display
: tty
;
141 if (pwm
->lcmessages
) {
142 *p
++ = "--lc-messages";
143 *p
++ = pwm
->lcmessages
;
150 *p
++ = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
154 rc
= assuan_pipe_connect(&ctx
,
155 pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
,
164 pwm
->pid
= assuan_get_pid(ctx
);
166 return set_pinentry_strings(pwm
, 0);
169 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, const char *cmd
)
172 gpg_error_t rc
= launch_pinentry(pwm
);
175 return gpg_err_code(rc
);
178 return _assuan_command(pwm
, pwm
->pctx
, result
, cmd
);
181 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, int which
)
183 char *tmp
, *desc
= NULL
;
186 tmp
= pwmd_malloc(ASSUAN_LINELENGTH
+1);
189 return gpg_error_from_errno(ENOMEM
);
192 pwm
->title
= pwmd_strdup_printf(N_("Password Manager Daemon: %s"),
193 pwm
->name
? pwm
->name
: "libpwmd");
199 pwm
->prompt
= pwmd_strdup(N_("Passphrase:"));
204 if (!pwm
->desc
&& (which
== PINENTRY_OPEN
|| which
== PINENTRY_SAVE
)) {
205 if (which
== PINENTRY_OPEN
)
206 desc
= pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please%%0Aenter the passphrase below."), pwm
->filename
);
208 desc
= pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm
->filename
);
220 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETERROR %s", desc
);
222 if (pwm
->desc
!= desc
)
225 case PINENTRY_OPEN_FAILED
:
226 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETERROR %s",
227 N_("Invalid passphrase, please try again."));
229 case PINENTRY_SAVE_CONFIRM
:
230 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETERROR %s",
231 N_("Please type the passphrase again for confirmation."));
235 rc
= pinentry_command(pwm
, NULL
, tmp
);
242 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETPROMPT %s", pwm
->prompt
);
243 rc
= pinentry_command(pwm
, NULL
, tmp
);
250 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETDESC %s", pwm
->title
);
251 rc
= pinentry_command(pwm
, NULL
, tmp
);
257 return gpg_error_from_errno(ENOMEM
);
260 static gpg_error_t
terminate_pinentry(pwm_t
*pwm
)
262 pid_t pid
= pwm
->pid
;
266 if (!pwm
|| pid
== -1)
267 return GPG_ERR_INV_ARG
;
269 if (kill(pid
, 0) == 0) {
270 if (kill(pid
, SIGTERM
) == -1) {
271 if (kill(pid
, SIGKILL
) == -1)
272 return gpg_error_from_errno(errno
);
276 return gpg_error_from_errno(errno
);
281 void _pinentry_disconnect(pwm_t
*pwm
)
284 assuan_disconnect(pwm
->pctx
);
291 * Only called from a child process.
293 static void catchsig(int sig
)
297 if (++gelapsed
>= gtimeout
) {
298 terminate_pinentry(gpwm
);
299 grc
= GPG_ERR_TIMEOUT
;
309 static gpg_error_t
do_getpin(pwm_t
*pwm
, char **result
)
312 signal(SIGALRM
, catchsig
);
317 return pinentry_command(pwm
, result
, "GETPIN");
320 gpg_error_t
_getpin(pwm_t
*pwm
, char **result
, int which
)
323 gpg_error_t rc
= set_pinentry_strings(pwm
, which
);
326 _pinentry_disconnect(pwm
);
330 rc
= do_getpin(pwm
, result
);
333 * Since there was input cancel any timeout setting.
336 signal(SIGALRM
, SIG_DFL
);
339 _pinentry_disconnect(pwm
);
341 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. Note
342 * that it is not thread safe do to the global variables. */
343 if (rc
== GPG_ERR_EOF
)
344 rc
= grc
== GPG_ERR_TIMEOUT
? grc
: GPG_ERR_CANCELED
;
350 gpg_error_t
_do_save_getpin(pwm_t
*pwm
, char **password
)
357 rc
= _getpin(pwm
, &result
, confirm
? PINENTRY_SAVE_CONFIRM
: PINENTRY_SAVE
);
360 _pinentry_disconnect(pwm
);
363 pwmd_free(*password
);
373 if (strcmp(*password
, result
)) {
374 pwmd_free(*password
);
382 _pinentry_disconnect(pwm
);
386 gpg_error_t
_pinentry_open(pwm_t
*pwm
, const char *filename
, char **password
,
392 pwm
->filename
= pwmd_strdup(filename
);
395 return gpg_error_from_errno(ENOMEM
);
397 /* Get the passphrase using the LOCAL pinentry. */
404 return gpg_error_from_syserror();
417 if (pwm
->pinentry_timeout
!= 0) {
419 gtimeout
= abs(pwm
->pinentry_timeout
);
423 pw
.error
= _getpin(pwm
, password
, PINENTRY_OPEN
);
424 _pinentry_disconnect(pwm
);
426 if (gtimeout
&& gelapsed
>= gtimeout
)
427 pw
.error
= GPG_ERR_TIMEOUT
;
430 snprintf(pw
.password
, sizeof(pw
.password
), "%s",
432 pwmd_free(*password
);
436 pth_write(p
[1], &pw
, sizeof(pw
));
438 write(p
[1], &pw
, sizeof(pw
));
440 memset(&pw
, 0, sizeof(pw
));
445 rc
= gpg_error_from_syserror();
459 /* Another handle is using this pinentry method. Allowing this instance
460 * would reset the timeout and global handle which wouldn't be good. */
462 return GPG_ERR_PIN_BLOCKED
;
464 if (pwm
->pinentry_timeout
!= 0) {
466 gtimeout
= abs(pwm
->pinentry_timeout
);
470 rc
= _getpin(pwm
, password
, PINENTRY_OPEN
);
472 /* Don't timeout when an invalid passphrase was entered. */