2 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013
3 Ben Kibbey <bjk@luxsci.net>
5 This file is part of libpwmd.
7 Libpwmd is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 2 of the License, or
10 (at your option) any later version.
12 Libpwmd is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Libpwmd. If not, see <http://www.gnu.org/licenses/>.
24 #include <sys/types.h>
42 static int gelapsed
, gtimeout
;
43 static gpg_error_t grc
;
45 static gpg_error_t
set_pinentry_strings (pwm_t
* pwm
, pwmd_pinentry_t which
);
48 launch_pinentry (pwm_t
* pwm
)
52 int child_list
[] = { -1 };
54 const char **p
= argv
;
55 static struct assuan_malloc_hooks mhooks
= {
56 pwmd_malloc
, pwmd_realloc
, pwmd_free
59 update_pinentry_settings (pwm
);
60 if (!pwm
->pinentry_display
&& !pwm
->pinentry_tty
)
61 return GPG_ERR_ENOTTY
;
64 *p
++ = pwm
->pinentry_display
? "--display" : "--ttyname";
65 *p
++ = pwm
->pinentry_display
? pwm
->pinentry_display
: pwm
->pinentry_tty
;
67 if (pwm
->pinentry_lcctype
)
70 *p
++ = pwm
->pinentry_lcctype
;
73 if (pwm
->pinentry_lcmessages
)
75 *p
++ = "--lc-messages";
76 *p
++ = pwm
->pinentry_lcmessages
;
81 if (!pwm
->pinentry_display
)
84 *p
++ = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv ("TERM");
88 rc
= assuan_new_ext (&ctx
, GPG_ERR_SOURCE_PINENTRY
, &mhooks
, NULL
, NULL
);
92 rc
= assuan_pipe_connect (ctx
,
93 pwm
->pinentry_path
? pwm
->
94 pinentry_path
: PINENTRY_PATH
, argv
, child_list
,
102 pwm
->pinentry_pid
= assuan_get_pid (ctx
);
104 rc
= set_pinentry_strings (pwm
, 0);
106 _pinentry_disconnect (pwm
);
112 pinentry_command (pwm_t
* pwm
, char **result
, size_t * len
, const char *cmd
)
122 gpg_error_t rc
= launch_pinentry (pwm
);
128 return _assuan_command (pwm
, pwm
->pctx
, result
, len
, cmd
);
133 quality_cb (void *data
, const char *line
)
140 if (strncmp (line
, "QUALITY ", 8))
141 return GPG_ERR_INV_ARG
;
143 if (!(tmp
= FascistCheck (line
+ 8, CRACKLIB_DICT
)))
144 return assuan_send_data (pwm
->pctx
, "100", 3);
146 if (!strcmp (tmp
, N_("it is WAY too short")))
148 else if (!strcmp (tmp
, N_("it is too short")))
150 else if (!strcmp (tmp
, N_("it is all whitespace")))
152 else if (!strcmp (tmp
, N_("it is based on your username")))
155 if (!strcmp (tmp
, N_("it does not contain enough DIFFERENT characters")))
157 else if (!strcmp (tmp
, N_("it is based on a dictionary word")))
159 else if (!strcmp (tmp
, N_("it is based upon your password entry")))
161 else if (!strcmp (tmp
, N_("it's derived from your password entry")))
163 else if (!strcmp (tmp
, N_("it is derived from your password entry")))
165 else if (!strcmp (tmp
, N_("it is based on a (reversed) dictionary word")))
167 else if (!strcmp (tmp
, N_("it is derivable from your password entry")))
169 else if (!strcmp (tmp
, N_("it's derivable from your password entry")))
171 else if (!strcmp (tmp
, N_("it looks like a National Insurance number.")))
173 else if (!strcmp (tmp
, N_("it is too simplistic/systematic")))
178 snprintf (buf
, sizeof (buf
), "%i", score
);
179 return assuan_send_data (pwm
->pctx
, buf
, strlen (buf
));
184 set_pinentry_strings (pwm_t
* pwm
, pwmd_pinentry_t which
)
186 char *tmp
, *desc
= NULL
;
189 if (which
!= PWMD_PINENTRY_USER
)
191 rc
= pinentry_command (pwm
, NULL
, NULL
, "SETERROR ");
196 tmp
= pwmd_malloc (ASSUAN_LINELENGTH
+ 1);
198 return gpg_error_from_errno (ENOMEM
);
200 if (!pwm
->pinentry_prompt
)
202 pwm
->pinentry_prompt
= pwmd_strdup (N_("Passphrase:"));
203 if (!pwm
->pinentry_prompt
)
206 return GPG_ERR_ENOMEM
;
210 if (!pwm
->pinentry_desc
)
212 if (which
== PWMD_PINENTRY_OPEN
|| which
== PWMD_PINENTRY_OPEN_FAILED
)
214 pwmd_strdup_printf (N_
215 ("A passphrase is required to unlock the secret key for the encrypted data file \"%s\". Please enter the passphrase below."),
217 else if (which
== PWMD_PINENTRY_SAVE
218 || which
== PWMD_PINENTRY_SAVE_FAILED
)
220 pwmd_strdup_printf (N_
221 ("Please enter the passphrase to use to encrypt the data file \"%s\"."),
223 else if (which
== PWMD_PINENTRY_SAVE_CONFIRM
)
225 pwmd_strdup_printf (N_
226 ("Please re-enter the passphrase for confirmation."),
232 return GPG_ERR_ENOMEM
;
236 if (pwm
->pinentry_desc
)
237 desc
= pwm
->pinentry_desc
;
239 if (which
== PWMD_PINENTRY_USER
)
241 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
242 pwm
->pinentry_error
? pwm
->pinentry_error
: "");
243 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
250 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETDESC %s",
251 pwm
->pinentry_desc
? pwm
->pinentry_desc
: "");
255 if (which
== PWMD_PINENTRY_SAVE_FAILED
256 || which
== PWMD_PINENTRY_OPEN_FAILED
)
258 if (which
== PWMD_PINENTRY_SAVE_FAILED
)
259 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
260 N_("Passphrases do not match, try again."));
263 if (pwm
->pinentry_tries
)
265 strcpy (tmp
, "SETERROR ");
266 snprintf (tmp
+ strlen ("SETERROR "), ASSUAN_LINELENGTH
,
267 N_("Bad passphrase (try %i of %i)"),
268 pwm
->pinentry_try
+ 1, pwm
->pinentry_tries
);
271 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
272 N_("Bad passphrase, try again"));
275 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
283 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETDESC %s", desc
);
285 if (pwm
->pinentry_desc
!= desc
)
289 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
296 snprintf (tmp
, ASSUAN_LINELENGTH
, "SETPROMPT %s", pwm
->pinentry_prompt
);
297 rc
= pinentry_command (pwm
, NULL
, NULL
, tmp
);
301 if (!rc
&& (which
== PWMD_PINENTRY_SAVE
||
302 which
== PWMD_PINENTRY_SAVE_FAILED
))
304 rc
= pinentry_command (pwm
, NULL
, NULL
, "SETQUALITYBAR");
307 pwm
->_inquire_func
= quality_cb
;
308 pwm
->_inquire_data
= pwm
;
317 terminate_pinentry (pwm_t
* pwm
)
321 if (!pwm
|| pwm
->pinentry_pid
== -1)
322 return GPG_ERR_INV_ARG
;
324 pid
= pwm
->pinentry_pid
;
325 pwm
->pinentry_pid
= -1;
327 if (kill (pid
, 0) == 0)
329 if (kill (pid
, SIGTERM
) == -1)
331 if (kill (pid
, SIGKILL
) == -1)
332 return gpg_error_from_errno (errno
);
336 return gpg_error_from_errno (errno
);
342 _pinentry_disconnect (pwm_t
* pwm
)
345 assuan_release (pwm
->pctx
);
348 pwm
->pinentry_pid
= -1;
352 * Only called from a child process.
360 if (++gelapsed
>= gtimeout
)
362 terminate_pinentry (gpwm
);
363 grc
= gpg_err_make (GPG_ERR_SOURCE_PINENTRY
, GPG_ERR_CANCELED
);
374 _getpin (pwm_t
* pwm
, char **result
, size_t * len
, pwmd_pinentry_t which
)
377 gpg_error_t rc
= set_pinentry_strings (pwm
, which
);
381 _pinentry_disconnect (pwm
);
387 signal (SIGALRM
, catchsig
);
391 rc
= pinentry_command (pwm
, result
, len
,
392 which
== PWMD_PINENTRY_CONFIRM
? "CONFIRM" : "GETPIN");
395 * Since there was input cancel any timeout setting.
398 signal (SIGALRM
, SIG_DFL
);
402 _pinentry_disconnect (pwm
);
404 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
405 * thread safe do to the global variables. */
406 if (gpg_err_code (rc
) == GPG_ERR_EOF
)
407 rc
= GPG_ERR_CANCELED
;
409 else if (which
!= PWMD_PINENTRY_CONFIRM
&& len
&& result
)
412 *len
= strlen (*result
); // remove the null byte
415 /* pwmd allows for an empty passphrase when not using gpg-agent. */
416 *result
= pwmd_malloc (1);
426 _pwmd_getpin (pwm_t
* pwm
, const char *filename
, char **result
,
427 size_t * len
, pwmd_pinentry_t which
)
433 return GPG_ERR_INV_ARG
;
436 if (which
== PWMD_PINENTRY_CLOSE
)
438 _pinentry_disconnect (pwm
);
442 if (!result
&& which
!= PWMD_PINENTRY_CONFIRM
)
443 return GPG_ERR_INV_ARG
;
445 /* Another handle is using this pinentry method. Allowing this instance
446 * would reset the timeout and global handle which wouldn't be good. */
448 return GPG_ERR_PIN_BLOCKED
;
450 if (pwm
->pinentry_timeout
!= 0)
453 gtimeout
= abs (pwm
->pinentry_timeout
);
457 pwm
->filename
= (char *) filename
;
458 rc
= _getpin (pwm
, result
, len
, which
);
461 /* Don't timeout when an invalid passphrase was entered. */