Fixed a segfault during an SSH connection failure.
[libpwmd.git] / src / pinentry.c
blobef28a0851d455c0f24435c6610752e315247da89
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
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>
20 #include <signal.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <limits.h>
25 #include "pinentry.h"
26 #include "misc.h"
28 static pwm_t *gpwm;
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)
36 FILE *fp;
37 char buf[LINE_MAX];
38 char *p;
39 struct passwd pw;
40 char *pwbuf = _getpwuid(&pw);
42 if (!pwbuf)
43 return;
45 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
47 if ((fp = fopen(buf, "r")) == NULL) {
48 pwmd_free(pwbuf);
49 return;
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)
56 continue;
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)
64 goto fail;
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)
72 goto fail;
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)
80 goto fail;
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)
88 goto fail;
92 fail:
93 pwmd_free(pwbuf);
94 fclose(fp);
97 static gpg_error_t launch_pinentry(pwm_t *pwm)
99 int rc;
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;
106 char *tty = NULL;
107 char *ttybuf = NULL;
109 update_pinentry_settings(pwm);
111 if (pwm->pinentry_display || display)
112 have_display = 1;
113 else {
114 if (!pwm->pinentry_tty) {
115 ttybuf = pwmd_malloc(255);
117 if (!ttybuf)
118 return gpg_error_from_errno(ENOMEM);
120 if (!ttyname_r(STDOUT_FILENO, ttybuf, 255))
121 tty = ttybuf;
122 else
123 pwmd_free(ttybuf);
125 else
126 tty = pwm->pinentry_tty;
129 if (!have_display && !tty)
130 return GPG_ERR_ENOTTY;
132 *p++ = "pinentry";
133 *p++ = have_display ? "--display" : "--ttyname";
134 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
136 if (pwm->lcctype) {
137 *p++ = "--lc-ctype";
138 *p++ = pwm->lcctype;
141 if (pwm->lcmessages) {
142 *p++ = "--lc-messages";
143 *p++ = pwm->lcmessages;
146 *p = NULL;
148 if (!have_display) {
149 *p++ = "--ttytype";
150 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
151 *p = NULL;
154 rc = assuan_pipe_connect(&ctx,
155 pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv,
156 child_list);
158 if (ttybuf)
159 pwmd_free(ttybuf);
161 if (rc)
162 return rc;
164 pwm->pid = assuan_get_pid(ctx);
165 pwm->pctx = ctx;
166 return set_pinentry_strings(pwm, 0);
169 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
171 if (!pwm->pctx) {
172 gpg_error_t rc = launch_pinentry(pwm);
174 if (rc)
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;
184 gpg_error_t rc;
186 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
188 if (!tmp)
189 return gpg_error_from_errno(ENOMEM);
191 if (!pwm->title)
192 pwm->title = pwmd_strdup_printf(N_("Password Manager Daemon: %s"),
193 pwm->name ? pwm->name : "libpwmd");
195 if (!pwm->title)
196 goto fail_no_mem;
198 if (!pwm->prompt)
199 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
201 if (!pwm->prompt)
202 goto fail_no_mem;
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);
207 else
208 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
210 if (!desc)
211 goto fail_no_mem;
214 if (pwm->desc)
215 desc = pwm->desc;
217 switch (which) {
218 case PINENTRY_OPEN:
219 case PINENTRY_SAVE:
220 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETERROR %s", desc);
222 if (pwm->desc != desc)
223 pwmd_free(desc);
224 break;
225 case PINENTRY_OPEN_FAILED:
226 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETERROR %s",
227 N_("Invalid passphrase, please try again."));
228 break;
229 case PINENTRY_SAVE_CONFIRM:
230 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETERROR %s",
231 N_("Please type the passphrase again for confirmation."));
232 break;
235 rc = pinentry_command(pwm, NULL, tmp);
237 if (rc) {
238 pwmd_free(tmp);
239 return rc;
242 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETPROMPT %s", pwm->prompt);
243 rc = pinentry_command(pwm, NULL, tmp);
245 if (rc) {
246 pwmd_free(tmp);
247 return rc;
250 snprintf(tmp, ASSUAN_LINELENGTH+1, "SETDESC %s", pwm->title);
251 rc = pinentry_command(pwm, NULL, tmp);
252 pwmd_free(tmp);
253 return rc;
255 fail_no_mem:
256 pwmd_free(tmp);
257 return gpg_error_from_errno(ENOMEM);
260 static gpg_error_t terminate_pinentry(pwm_t *pwm)
262 pid_t pid = pwm->pid;
264 pwm->pid = -1;
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);
275 else
276 return gpg_error_from_errno(errno);
278 return 0;
281 void _pinentry_disconnect(pwm_t *pwm)
283 if (pwm->pctx)
284 assuan_disconnect(pwm->pctx);
286 pwm->pctx = NULL;
287 pwm->pid = -1;
291 * Only called from a child process.
293 static void catchsig(int sig)
295 switch (sig) {
296 case SIGALRM:
297 if (++gelapsed >= gtimeout) {
298 terminate_pinentry(gpwm);
299 grc = GPG_ERR_TIMEOUT;
301 else
302 alarm(1);
303 break;
304 default:
305 break;
309 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
311 if (gtimeout) {
312 signal(SIGALRM, catchsig);
313 alarm(1);
316 *result = NULL;
317 return pinentry_command(pwm, result, "GETPIN");
320 gpg_error_t _getpin(pwm_t *pwm, char **result, int which)
322 grc = 0;
323 gpg_error_t rc = set_pinentry_strings(pwm, which);
325 if (rc) {
326 _pinentry_disconnect(pwm);
327 return rc;
330 rc = do_getpin(pwm, result);
333 * Since there was input cancel any timeout setting.
335 alarm(0);
336 signal(SIGALRM, SIG_DFL);
338 if (rc) {
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;
347 return rc;
350 gpg_error_t _do_save_getpin(pwm_t *pwm, char **password)
352 int confirm = 0;
353 gpg_error_t rc;
354 char *result = NULL;
356 again:
357 rc = _getpin(pwm, &result, confirm ? PINENTRY_SAVE_CONFIRM : PINENTRY_SAVE);
359 if (rc) {
360 _pinentry_disconnect(pwm);
362 if (*password)
363 pwmd_free(*password);
365 return rc;
368 if (!confirm++) {
369 *password = result;
370 goto again;
373 if (strcmp(*password, result)) {
374 pwmd_free(*password);
375 pwmd_free(result);
376 confirm = 0;
377 *password = NULL;
378 goto again;
381 pwmd_free(result);
382 _pinentry_disconnect(pwm);
383 return 0;
386 gpg_error_t _pinentry_open(pwm_t *pwm, const char *filename, char **password,
387 int nb)
389 gpg_error_t rc;
391 if (!pwm->filename)
392 pwm->filename = pwmd_strdup(filename);
394 if (!pwm->filename)
395 return gpg_error_from_errno(ENOMEM);
397 /* Get the passphrase using the LOCAL pinentry. */
398 if (nb) {
399 int p[2];
400 pid_t pid;
401 pwmd_nb_status_t pw;
403 if (pipe(p) == -1)
404 return gpg_error_from_syserror();
406 #ifdef WITH_LIBPTH
407 pid = pth_fork();
408 #else
409 pid = fork();
410 #endif
412 switch (pid) {
413 case 0:
414 close(p[0]);
415 pw.fd = p[0];
417 if (pwm->pinentry_timeout != 0) {
418 gpwm = pwm;
419 gtimeout = abs(pwm->pinentry_timeout);
420 gelapsed = 0;
423 pw.error = _getpin(pwm, password, PINENTRY_OPEN);
424 _pinentry_disconnect(pwm);
426 if (gtimeout && gelapsed >= gtimeout)
427 pw.error = GPG_ERR_TIMEOUT;
429 if (!pw.error) {
430 snprintf(pw.password, sizeof(pw.password), "%s",
431 *password);
432 pwmd_free(*password);
435 #ifdef WITH_LIBPTH
436 pth_write(p[1], &pw, sizeof(pw));
437 #else
438 write(p[1], &pw, sizeof(pw));
439 #endif
440 memset(&pw, 0, sizeof(pw));
441 close(p[1]);
442 _exit(0);
443 break;
444 case -1:
445 rc = gpg_error_from_syserror();
446 close(p[0]);
447 close(p[1]);
448 return rc;
449 default:
450 break;
453 close(p[1]);
454 pwm->nb_fd = p[0];
455 pwm->nb_pid = pid;
456 return 0;
459 /* Another handle is using this pinentry method. Allowing this instance
460 * would reset the timeout and global handle which wouldn't be good. */
461 if (gpwm)
462 return GPG_ERR_PIN_BLOCKED;
464 if (pwm->pinentry_timeout != 0) {
465 gpwm = pwm;
466 gtimeout = abs(pwm->pinentry_timeout);
467 gelapsed = 0;
470 rc = _getpin(pwm, password, PINENTRY_OPEN);
472 /* Don't timeout when an invalid passphrase was entered. */
473 gtimeout = 0;
474 gpwm = NULL;
475 return rc;