Always send pinentry options in pwmd_open().
[libpwmd.git] / src / pinentry.c
blobac2024cf525eda27751f794ce14e634dcc70e0e8
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
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/>.
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
25 #include <sys/types.h>
26 #include <signal.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <limits.h>
31 #include "pinentry.h"
32 #include "misc.h"
34 #ifdef WITH_QUALITY
35 #include <crack.h>
36 #endif
38 static pwm_t *gpwm;
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)
46 int rc;
47 assuan_context_t ctx;
48 int child_list[] = {-1};
49 char *display = getenv("DISPLAY");
50 const char *argv[10];
51 const char **p = argv;
52 int have_display = 0;
53 char *tty = NULL;
54 char *ttybuf = NULL;
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)
62 have_display = 1;
63 else {
64 if (!pwm->pinentry_tty) {
65 ttybuf = pwmd_malloc(255);
67 if (!ttybuf)
68 return gpg_error_from_errno(ENOMEM);
70 if (!ttyname_r(STDOUT_FILENO, ttybuf, 255))
71 tty = ttybuf;
72 else
73 pwmd_free(ttybuf);
75 else
76 tty = pwm->pinentry_tty;
79 if (!have_display && !tty)
80 return GPG_ERR_ENOTTY;
82 *p++ = "pinentry";
83 *p++ = have_display ? "--display" : "--ttyname";
84 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
86 if (pwm->lcctype) {
87 *p++ = "--lc-ctype";
88 *p++ = pwm->lcctype;
91 if (pwm->lcmessages) {
92 *p++ = "--lc-messages";
93 *p++ = pwm->lcmessages;
96 *p = NULL;
98 if (!have_display) {
99 *p++ = "--ttytype";
100 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
101 *p = NULL;
104 rc = assuan_new_ext(&ctx, GPG_ERR_SOURCE_PINENTRY, &mhooks, NULL, NULL);
105 if (rc)
106 goto fail;
108 rc = assuan_pipe_connect(ctx,
109 pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv,
110 child_list, NULL, NULL, 0);
112 if (ttybuf)
113 pwmd_free(ttybuf);
115 if (rc)
116 return rc;
118 pwm->pid = assuan_get_pid(ctx);
119 pwm->pctx = ctx;
120 return set_pinentry_strings(pwm, 0);
122 fail:
123 if (ttybuf)
124 pwmd_free(ttybuf);
125 return rc;
128 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, size_t *len,
129 const char *cmd)
131 if (!pwm->pctx) {
132 gpg_error_t rc = launch_pinentry(pwm);
134 if (rc)
135 return rc;
138 return _assuan_command(pwm, pwm->pctx, result, len, cmd);
141 #ifdef WITH_QUALITY
142 static int quality_cb(void *data, const char *line)
144 pwm_t *pwm = data;
145 int score = 0;
146 char buf[5];
147 const char *tmp;
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")))
156 score = 10;
157 else if (!strcmp(tmp, N_("it is too short")))
158 score = 20;
159 else if (!strcmp(tmp, N_("it is all whitespace")))
160 score = 25;
161 else if (!strcmp(tmp, N_("it is based on your username")))
162 score = 30;
163 else if (!strcmp(tmp, N_("it does not contain enough DIFFERENT characters")))
164 score = 35;
165 else if (!strcmp(tmp, N_("it is based on a dictionary word")))
166 score = 40;
167 else if (!strcmp(tmp, N_("it is based upon your password entry")))
168 score = 50;
169 else if (!strcmp(tmp, N_("it's derived from your password entry")))
170 score = 50;
171 else if (!strcmp(tmp, N_("it is derived from your password entry")))
172 score = 50;
173 else if (!strcmp(tmp, N_("it is based on a (reversed) dictionary word")))
174 score = 60;
175 else if (!strcmp(tmp, N_("it is derivable from your password entry")))
176 score = 70;
177 else if (!strcmp(tmp, N_("it's derivable from your password entry")))
178 score = 70;
179 else if (!strcmp(tmp, N_("it looks like a National Insurance number.")))
180 score = 80;
181 else if (!strcmp(tmp, N_("it is too simplistic/systematic")))
182 score = 90;
183 else
184 score = 0;
186 snprintf(buf, sizeof(buf), "%i", score);
187 return assuan_send_data(pwm->pctx, buf, strlen(buf));
189 #endif
191 static gpg_error_t set_pinentry_strings(pwm_t *pwm, pwmd_pinentry_t which)
193 char *tmp, *desc = NULL;
194 gpg_error_t rc;
196 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
197 if (!tmp)
198 return gpg_error_from_errno(ENOMEM);
200 if (!pwm->prompt)
201 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
202 if (!pwm->prompt)
203 goto fail_no_mem;
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);
208 else
209 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please enter the passphrase below."), pwm->filename);
211 if (!desc)
212 goto fail_no_mem;
215 if (pwm->desc)
216 desc = pwm->desc;
218 switch (which) {
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)
225 pwmd_free(desc);
226 break;
227 case PWMD_PINENTRY_OPEN_FAILED:
228 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETERROR %s",
229 N_("Invalid passphrase, please try again."));
230 break;
231 case PWMD_PINENTRY_SAVE_CONFIRM:
232 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETERROR %s",
233 N_("Please type the passphrase again for confirmation."));
234 break;
235 default:
236 case PWMD_PINENTRY_DEFAULT:
237 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETDESC %s",
238 pwm->desc ? pwm->desc : "");
239 break;
242 rc = pinentry_command(pwm, NULL, NULL, tmp);
243 if (rc) {
244 pwmd_free(tmp);
245 return rc;
248 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETPROMPT %s", pwm->prompt);
249 rc = pinentry_command(pwm, NULL, NULL, tmp);
250 if (rc) {
251 pwmd_free(tmp);
252 return rc;
255 #ifdef WITH_QUALITY
256 if (which == PWMD_PINENTRY_SAVE) {
257 rc = pinentry_command(pwm, NULL, NULL, "SETQUALITYBAR");
258 if (rc) {
259 pwmd_free(tmp);
260 return rc;
263 pwm->_inquire_func = quality_cb;
264 pwm->_inquire_data = pwm;
266 #endif
268 return rc;
270 fail_no_mem:
271 pwmd_free(tmp);
272 return gpg_error_from_errno(ENOMEM);
275 static gpg_error_t terminate_pinentry(pwm_t *pwm)
277 pid_t pid = pwm->pid;
279 pwm->pid = -1;
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);
290 else
291 return gpg_error_from_errno(errno);
293 return 0;
296 void _pinentry_disconnect(pwm_t *pwm)
298 if (pwm->pctx)
299 assuan_release(pwm->pctx);
301 pwm->pctx = NULL;
302 pwm->pid = -1;
306 * Only called from a child process.
308 static void catchsig(int sig)
310 switch (sig) {
311 case SIGALRM:
312 if (++gelapsed >= gtimeout) {
313 terminate_pinentry(gpwm);
314 grc = gpg_err_make(GPG_ERR_SOURCE_PINENTRY, GPG_ERR_TIMEOUT);
316 else
317 alarm(1);
318 break;
319 default:
320 break;
324 static gpg_error_t do_getpin(pwm_t *pwm, char **result, size_t *len,
325 pwmd_pinentry_t which)
327 if (gtimeout) {
328 signal(SIGALRM, catchsig);
329 alarm(1);
332 if (result)
333 *result = NULL;
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)
342 grc = 0;
343 gpg_error_t rc = set_pinentry_strings(pwm, which);
345 if (rc) {
346 _pinentry_disconnect(pwm);
347 return rc;
350 if (result)
351 *result = NULL;
353 rc = do_getpin(pwm, result, len, which);
356 * Since there was input cancel any timeout setting.
358 alarm(0);
359 signal(SIGALRM, SIG_DFL);
361 if (rc) {
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;
370 return rc;
373 gpg_error_t _do_save_getpin(pwm_t *pwm, char **password, size_t *len)
375 int confirm = 0;
376 gpg_error_t rc;
377 char *result = NULL;
379 again:
380 rc = _getpin(pwm, &result, len,
381 confirm ? PWMD_PINENTRY_SAVE_CONFIRM : PWMD_PINENTRY_SAVE);
383 if (rc) {
384 _pinentry_disconnect(pwm);
386 if (*password)
387 pwmd_free(*password);
389 return rc;
392 if (!confirm++) {
393 *password = result;
394 goto again;
397 if (strcmp(*password, result)) {
398 pwmd_free(*password);
399 pwmd_free(result);
400 confirm = 0;
401 *password = NULL;
402 goto again;
405 pwmd_free(result);
406 _pinentry_disconnect(pwm);
407 return 0;
410 static gpg_error_t do_local_getpin(pwm_t *pwm, char **password, size_t *len,
411 pwmd_pinentry_t which)
413 gpg_error_t rc;
415 /* Another handle is using this pinentry method. Allowing this instance
416 * would reset the timeout and global handle which wouldn't be good. */
417 if (gpwm)
418 return GPG_ERR_PIN_BLOCKED;
420 if (pwm->pinentry_timeout != 0) {
421 gpwm = pwm;
422 gtimeout = abs(pwm->pinentry_timeout);
423 gelapsed = 0;
426 rc = _getpin(pwm, password, len, which);
428 /* Don't timeout when an invalid passphrase was entered. */
429 gtimeout = 0;
430 gpwm = NULL;
431 return rc;
434 gpg_error_t _pinentry_open(pwm_t *pwm, const char *filename, char **password,
435 size_t *len)
437 if (pwm->filename)
438 pwmd_free(pwm->filename);
440 pwm->filename = pwmd_strdup(filename);
442 if (!pwm->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)
451 if (!pwm)
452 return GPG_ERR_INV_ARG;
454 if (which == PWMD_PINENTRY_CLOSE) {
455 _pinentry_disconnect(pwm);
456 return 0;
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);
465 pwm->filename = p;
466 return rc;