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/>.
25 #include <sys/types.h>
39 static int gelapsed
, gtimeout
;
40 static gpg_error_t grc
;
42 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, pwmd_pinentry_t which
);
44 static gpg_error_t
launch_pinentry(pwm_t
*pwm
)
48 int child_list
[] = {-1};
49 char *display
= getenv("DISPLAY");
51 const char **p
= argv
;
55 static struct assuan_malloc_hooks mhooks
= {
56 pwmd_malloc
, pwmd_realloc
, pwmd_free
59 update_pinentry_settings(pwm
);
61 if (pwm
->pinentry_display
|| display
)
64 if (!pwm
->pinentry_tty
) {
65 ttybuf
= pwmd_malloc(255);
68 return gpg_error_from_errno(ENOMEM
);
70 if (!ttyname_r(STDOUT_FILENO
, ttybuf
, 255))
76 tty
= pwm
->pinentry_tty
;
79 if (!have_display
&& !tty
)
80 return GPG_ERR_ENOTTY
;
83 *p
++ = have_display
? "--display" : "--ttyname";
84 *p
++ = have_display
? pwm
->pinentry_display
? pwm
->pinentry_display
: display
: tty
;
91 if (pwm
->lcmessages
) {
92 *p
++ = "--lc-messages";
93 *p
++ = pwm
->lcmessages
;
100 *p
++ = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
104 rc
= assuan_new_ext(&ctx
, GPG_ERR_SOURCE_PINENTRY
, &mhooks
, NULL
, NULL
);
108 rc
= assuan_pipe_connect(ctx
,
109 pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
,
110 child_list
, NULL
, NULL
, 0);
118 pwm
->pid
= assuan_get_pid(ctx
);
120 return set_pinentry_strings(pwm
, 0);
128 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, size_t *len
,
132 gpg_error_t rc
= launch_pinentry(pwm
);
138 return _assuan_command(pwm
, pwm
->pctx
, result
, len
, cmd
);
142 static int quality_cb(void *data
, const char *line
)
149 if (strncmp(line
, "QUALITY ", 8))
150 return GPG_ERR_INV_ARG
;
152 if (!(tmp
= FascistCheck(line
+8, CRACKLIB_DICT
)))
153 return assuan_send_data(pwm
->pctx
, "100", 3);
155 if (!strcmp(tmp
, N_("it is WAY too short")))
157 else if (!strcmp(tmp
, N_("it is too short")))
159 else if (!strcmp(tmp
, N_("it is all whitespace")))
161 else if (!strcmp(tmp
, N_("it is based on your username")))
163 else if (!strcmp(tmp
, N_("it does not contain enough DIFFERENT characters")))
165 else if (!strcmp(tmp
, N_("it is based on a dictionary word")))
167 else if (!strcmp(tmp
, N_("it is based upon your password entry")))
169 else if (!strcmp(tmp
, N_("it's derived from your password entry")))
171 else if (!strcmp(tmp
, N_("it is derived from your password entry")))
173 else if (!strcmp(tmp
, N_("it is based on a (reversed) dictionary word")))
175 else if (!strcmp(tmp
, N_("it is derivable from your password entry")))
177 else if (!strcmp(tmp
, N_("it's derivable from your password entry")))
179 else if (!strcmp(tmp
, N_("it looks like a National Insurance number.")))
181 else if (!strcmp(tmp
, N_("it is too simplistic/systematic")))
186 snprintf(buf
, sizeof(buf
), "%i", score
);
187 return assuan_send_data(pwm
->pctx
, buf
, strlen(buf
));
191 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, pwmd_pinentry_t which
)
193 char *tmp
, *desc
= NULL
;
196 tmp
= pwmd_malloc(ASSUAN_LINELENGTH
+1);
198 return gpg_error_from_errno(ENOMEM
);
201 pwm
->prompt
= pwmd_strdup(N_("Passphrase:"));
205 if (!pwm
->desc
&& (which
== PWMD_PINENTRY_OPEN
|| which
== PWMD_PINENTRY_SAVE
)) {
206 if (which
== PWMD_PINENTRY_OPEN
)
207 desc
= pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please enter the passphrase below."), pwm
->filename
);
209 desc
= pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please enter the passphrase below."), pwm
->filename
);
219 case PWMD_PINENTRY_CONFIRM
:
220 case PWMD_PINENTRY_OPEN
:
221 case PWMD_PINENTRY_SAVE
:
222 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETDESC %s", desc
);
224 if (pwm
->desc
!= desc
)
227 case PWMD_PINENTRY_OPEN_FAILED
:
228 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETERROR %s",
229 N_("Invalid passphrase, please try again."));
231 case PWMD_PINENTRY_SAVE_CONFIRM
:
232 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETERROR %s",
233 N_("Please type the passphrase again for confirmation."));
236 case PWMD_PINENTRY_DEFAULT
:
237 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETDESC %s",
238 pwm
->desc
? pwm
->desc
: "");
242 rc
= pinentry_command(pwm
, NULL
, NULL
, tmp
);
248 snprintf(tmp
, ASSUAN_LINELENGTH
+1, "SETPROMPT %s", pwm
->prompt
);
249 rc
= pinentry_command(pwm
, NULL
, NULL
, tmp
);
256 if (which
== PWMD_PINENTRY_SAVE
) {
257 rc
= pinentry_command(pwm
, NULL
, NULL
, "SETQUALITYBAR");
263 pwm
->_inquire_func
= quality_cb
;
264 pwm
->_inquire_data
= pwm
;
272 return gpg_error_from_errno(ENOMEM
);
275 static gpg_error_t
terminate_pinentry(pwm_t
*pwm
)
277 pid_t pid
= pwm
->pid
;
281 if (!pwm
|| pid
== -1)
282 return GPG_ERR_INV_ARG
;
284 if (kill(pid
, 0) == 0) {
285 if (kill(pid
, SIGTERM
) == -1) {
286 if (kill(pid
, SIGKILL
) == -1)
287 return gpg_error_from_errno(errno
);
291 return gpg_error_from_errno(errno
);
296 void _pinentry_disconnect(pwm_t
*pwm
)
299 assuan_release(pwm
->pctx
);
306 * Only called from a child process.
308 static void catchsig(int sig
)
312 if (++gelapsed
>= gtimeout
) {
313 terminate_pinentry(gpwm
);
314 grc
= gpg_err_make(GPG_ERR_SOURCE_PINENTRY
, GPG_ERR_TIMEOUT
);
324 static gpg_error_t
do_getpin(pwm_t
*pwm
, char **result
, size_t *len
,
325 pwmd_pinentry_t which
)
328 signal(SIGALRM
, catchsig
);
335 return pinentry_command(pwm
, result
, len
,
336 which
== PWMD_PINENTRY_CONFIRM
? "CONFIRM" : "GETPIN");
339 gpg_error_t
_getpin(pwm_t
*pwm
, char **result
, size_t *len
,
340 pwmd_pinentry_t which
)
343 gpg_error_t rc
= set_pinentry_strings(pwm
, which
);
346 _pinentry_disconnect(pwm
);
353 rc
= do_getpin(pwm
, result
, len
, which
);
356 * Since there was input cancel any timeout setting.
359 signal(SIGALRM
, SIG_DFL
);
362 _pinentry_disconnect(pwm
);
364 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. Note
365 * that it is not thread safe do to the global variables. */
366 if (gpg_err_code(rc
) == GPG_ERR_EOF
)
367 rc
= gpg_err_code(grc
) == GPG_ERR_TIMEOUT
? grc
: GPG_ERR_CANCELED
;
373 gpg_error_t
_do_save_getpin(pwm_t
*pwm
, char **password
, size_t *len
)
380 rc
= _getpin(pwm
, &result
, len
,
381 confirm
? PWMD_PINENTRY_SAVE_CONFIRM
: PWMD_PINENTRY_SAVE
);
384 _pinentry_disconnect(pwm
);
387 pwmd_free(*password
);
397 if (strcmp(*password
, result
)) {
398 pwmd_free(*password
);
406 _pinentry_disconnect(pwm
);
410 static gpg_error_t
do_local_getpin(pwm_t
*pwm
, char **password
, size_t *len
,
411 pwmd_pinentry_t which
)
415 /* Another handle is using this pinentry method. Allowing this instance
416 * would reset the timeout and global handle which wouldn't be good. */
418 return GPG_ERR_PIN_BLOCKED
;
420 if (pwm
->pinentry_timeout
!= 0) {
422 gtimeout
= abs(pwm
->pinentry_timeout
);
426 rc
= _getpin(pwm
, password
, len
, which
);
428 /* Don't timeout when an invalid passphrase was entered. */
434 gpg_error_t
_pinentry_open(pwm_t
*pwm
, const char *filename
, char **password
,
438 pwmd_free(pwm
->filename
);
440 pwm
->filename
= pwmd_strdup(filename
);
443 return gpg_error_from_errno(ENOMEM
);
445 return do_local_getpin(pwm
, password
, len
, PWMD_PINENTRY_OPEN
);
448 gpg_error_t
_pwmd_getpin(pwm_t
*pwm
, const char *filename
, char **result
,
449 size_t *len
, pwmd_pinentry_t which
)
452 return GPG_ERR_INV_ARG
;
454 if (which
== PWMD_PINENTRY_CLOSE
) {
455 _pinentry_disconnect(pwm
);
459 if (!result
&& which
!= PWMD_PINENTRY_CONFIRM
)
460 return GPG_ERR_INV_ARG
;
462 char *p
= pwm
->filename
;
463 pwm
->filename
= (char *)filename
;
464 gpg_error_t rc
= do_local_getpin(pwm
, result
, len
, which
);