1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
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>
43 static int gelapsed
, gtimeout
;
44 static gpg_error_t grc
;
46 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, pwmd_pinentry_t which
);
48 static gpg_error_t
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
) {
69 *p
++ = pwm
->pinentry_lcctype
;
72 if (pwm
->pinentry_lcmessages
) {
73 *p
++ = "--lc-messages";
74 *p
++ = pwm
->pinentry_lcmessages
;
79 if (!pwm
->pinentry_display
) {
81 *p
++ = pwm
->pinentry_term
? pwm
->pinentry_term
: getenv("TERM");
85 rc
= assuan_new_ext(&ctx
, GPG_ERR_SOURCE_PINENTRY
, &mhooks
, NULL
, NULL
);
89 rc
= assuan_pipe_connect(ctx
,
90 pwm
->pinentry_path
? pwm
->pinentry_path
: PINENTRY_PATH
, argv
,
91 child_list
, NULL
, NULL
, 0);
95 pwm
->pinentry_pid
= assuan_get_pid(ctx
);
97 return set_pinentry_strings(pwm
, 0);
100 static gpg_error_t
pinentry_command(pwm_t
*pwm
, char **result
, size_t *len
,
104 gpg_error_t rc
= launch_pinentry(pwm
);
110 return _assuan_command(pwm
, pwm
->pctx
, result
, len
, cmd
);
114 static gpg_error_t
quality_cb(void *data
, const char *line
)
121 if (strncmp(line
, "QUALITY ", 8))
122 return GPG_ERR_INV_ARG
;
124 if (!(tmp
= FascistCheck(line
+8, CRACKLIB_DICT
)))
125 return assuan_send_data(pwm
->pctx
, "100", 3);
127 if (!strcmp(tmp
, N_("it is WAY too short")))
129 else if (!strcmp(tmp
, N_("it is too short")))
131 else if (!strcmp(tmp
, N_("it is all whitespace")))
133 else if (!strcmp(tmp
, N_("it is based on your username")))
135 else if (!strcmp(tmp
, N_("it does not contain enough DIFFERENT characters")))
137 else if (!strcmp(tmp
, N_("it is based on a dictionary word")))
139 else if (!strcmp(tmp
, N_("it is based upon your password entry")))
141 else if (!strcmp(tmp
, N_("it's derived from your password entry")))
143 else if (!strcmp(tmp
, N_("it is derived from your password entry")))
145 else if (!strcmp(tmp
, N_("it is based on a (reversed) dictionary word")))
147 else if (!strcmp(tmp
, N_("it is derivable from your password entry")))
149 else if (!strcmp(tmp
, N_("it's derivable from your password entry")))
151 else if (!strcmp(tmp
, N_("it looks like a National Insurance number.")))
153 else if (!strcmp(tmp
, N_("it is too simplistic/systematic")))
158 snprintf(buf
, sizeof(buf
), "%i", score
);
159 return assuan_send_data(pwm
->pctx
, buf
, strlen(buf
));
163 static gpg_error_t
set_pinentry_strings(pwm_t
*pwm
, pwmd_pinentry_t which
)
165 char *tmp
, *desc
= NULL
;
168 if (which
!= PWMD_PINENTRY_USER
) {
169 rc
= pinentry_command(pwm
, NULL
, NULL
, "SETERROR ");
174 tmp
= pwmd_malloc(ASSUAN_LINELENGTH
+1);
176 return gpg_error_from_errno(ENOMEM
);
178 if (!pwm
->pinentry_prompt
) {
179 pwm
->pinentry_prompt
= pwmd_strdup(N_("Passphrase:"));
180 if (!pwm
->pinentry_prompt
) {
182 return GPG_ERR_ENOMEM
;
186 if (!pwm
->pinentry_desc
) {
187 if (which
== PWMD_PINENTRY_OPEN
|| which
== PWMD_PINENTRY_OPEN_FAILED
)
188 desc
= pwmd_strdup_printf(N_("A passphrase is required to unlock the secret key for the encrypted data file \"%s\". Please enter the passphrase below."), pwm
->filename
);
189 else if (which
== PWMD_PINENTRY_SAVE
190 || which
== PWMD_PINENTRY_SAVE_FAILED
)
191 desc
= pwmd_strdup_printf(N_("Please enter the passphrase to use to encrypt the data file \"%s\"."), pwm
->filename
);
192 else if (which
== PWMD_PINENTRY_SAVE_CONFIRM
)
193 desc
= pwmd_strdup_printf(N_("Please re-enter the passphrase for confirmation."), pwm
->filename
);
197 return GPG_ERR_ENOMEM
;
201 if (pwm
->pinentry_desc
)
202 desc
= pwm
->pinentry_desc
;
204 if (which
== PWMD_PINENTRY_USER
) {
205 snprintf(tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
206 pwm
->pinentry_error
? pwm
->pinentry_error
: "");
207 rc
= pinentry_command(pwm
, NULL
, NULL
, tmp
);
213 snprintf(tmp
, ASSUAN_LINELENGTH
, "SETDESC %s",
214 pwm
->pinentry_desc
? pwm
->pinentry_desc
: "");
217 if (which
== PWMD_PINENTRY_SAVE_FAILED
218 || which
== PWMD_PINENTRY_OPEN_FAILED
) {
219 if (which
== PWMD_PINENTRY_SAVE_FAILED
)
220 snprintf(tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
221 N_("Passphrases do not match, try again."));
223 if (pwm
->pinentry_tries
) {
224 strcpy(tmp
, "SETERROR ");
225 snprintf(tmp
+strlen("SETERROR "), ASSUAN_LINELENGTH
,
226 N_("Bad passphrase (try %i of %i)"),
227 pwm
->pinentry_try
+1, pwm
->pinentry_tries
);
230 snprintf(tmp
, ASSUAN_LINELENGTH
, "SETERROR %s",
231 N_("Bad passphrase, try again"));
234 rc
= pinentry_command(pwm
, NULL
, NULL
, tmp
);
241 snprintf(tmp
, ASSUAN_LINELENGTH
, "SETDESC %s", desc
);
243 if (pwm
->pinentry_desc
!= desc
)
247 rc
= pinentry_command(pwm
, NULL
, NULL
, tmp
);
253 snprintf(tmp
, ASSUAN_LINELENGTH
, "SETPROMPT %s", pwm
->pinentry_prompt
);
254 rc
= pinentry_command(pwm
, NULL
, NULL
, tmp
);
258 if (!rc
&& (which
== PWMD_PINENTRY_SAVE
||
259 which
== PWMD_PINENTRY_SAVE_FAILED
)) {
260 rc
= pinentry_command(pwm
, NULL
, NULL
, "SETQUALITYBAR");
262 pwm
->_inquire_func
= quality_cb
;
263 pwm
->_inquire_data
= pwm
;
271 static gpg_error_t
terminate_pinentry(pwm_t
*pwm
)
273 pid_t pid
= pwm
->pinentry_pid
;
275 pwm
->pinentry_pid
= -1;
277 if (!pwm
|| pid
== -1)
278 return GPG_ERR_INV_ARG
;
280 if (kill(pid
, 0) == 0) {
281 if (kill(pid
, SIGTERM
) == -1) {
282 if (kill(pid
, SIGKILL
) == -1)
283 return gpg_error_from_errno(errno
);
287 return gpg_error_from_errno(errno
);
292 void _pinentry_disconnect(pwm_t
*pwm
)
295 assuan_release(pwm
->pctx
);
298 pwm
->pinentry_pid
= -1;
302 * Only called from a child process.
304 static void catchsig(int sig
)
308 if (++gelapsed
>= gtimeout
) {
309 terminate_pinentry(gpwm
);
310 grc
= gpg_err_make(GPG_ERR_SOURCE_PINENTRY
, GPG_ERR_CANCELED
);
320 gpg_error_t
_getpin(pwm_t
*pwm
, char **result
, size_t *len
,
321 pwmd_pinentry_t which
)
324 gpg_error_t rc
= set_pinentry_strings(pwm
, which
);
327 _pinentry_disconnect(pwm
);
335 signal(SIGALRM
, catchsig
);
342 rc
= pinentry_command(pwm
, result
, len
,
343 which
== PWMD_PINENTRY_CONFIRM
? "CONFIRM" : "GETPIN");
346 * Since there was input cancel any timeout setting.
349 signal(SIGALRM
, SIG_DFL
);
352 _pinentry_disconnect(pwm
);
354 /* This lets PWMD_OPTION_PINENTRY_TIMEOUT work. Note that it is not
355 * thread safe do to the global variables. */
356 if (gpg_err_code(rc
) == GPG_ERR_EOF
)
357 rc
= GPG_ERR_CANCELED
;
363 gpg_error_t
_pwmd_getpin(pwm_t
*pwm
, const char *filename
, char **result
,
364 size_t *len
, pwmd_pinentry_t which
)
367 char *p
= pwm
->filename
;
370 return GPG_ERR_INV_ARG
;
372 if (which
== PWMD_PINENTRY_CLOSE
) {
373 _pinentry_disconnect(pwm
);
377 if (!result
&& which
!= PWMD_PINENTRY_CONFIRM
)
378 return GPG_ERR_INV_ARG
;
380 /* Another handle is using this pinentry method. Allowing this instance
381 * would reset the timeout and global handle which wouldn't be good. */
383 return GPG_ERR_PIN_BLOCKED
;
385 if (pwm
->pinentry_timeout
!= 0) {
387 gtimeout
= abs(pwm
->pinentry_timeout
);
391 pwm
->filename
= (char *)filename
;
392 rc
= _getpin(pwm
, result
, len
, which
);
395 /* Don't timeout when an invalid passphrase was entered. */