Created src/misc.[ch],ssh.[ch],pinentry.[ch].
[libpwmd.git] / src / pinentry.c
blob8b31c06653806cea4511c9b3d4d97ff5e50d0fff
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 gpg_error_t set_pinentry_strings(pwm_t *pwm, int which);
30 static void update_pinentry_settings(pwm_t *pwm)
32 FILE *fp;
33 char buf[LINE_MAX];
34 char *p;
35 struct passwd pw;
36 char *pwbuf = _getpwuid(&pw);
38 if (!pwbuf)
39 return;
41 snprintf(buf, sizeof(buf), "%s/.pwmd/pinentry.conf", pw.pw_dir);
43 if ((fp = fopen(buf, "r")) == NULL) {
44 pwmd_free(pwbuf);
45 return;
48 while ((p = fgets(buf, sizeof(buf), fp)) != NULL) {
49 char name[32], val[256];
51 if (sscanf(p, " %31[a-zA-Z] = %255s", name, val) != 2)
52 continue;
54 if (strcasecmp(name, "TTYNAME") == 0) {
55 pwmd_free(pwm->pinentry_tty);
56 pwm->pinentry_tty = pwmd_strdup(val);
58 else if (strcasecmp(name, "TTYTYPE") == 0) {
59 pwmd_free(pwm->pinentry_term);
60 pwm->pinentry_term = pwmd_strdup(val);
62 else if (strcasecmp(name, "DISPLAY") == 0) {
63 pwmd_free(pwm->pinentry_display);
64 pwm->pinentry_display = pwmd_strdup(val);
66 else if (strcasecmp(name, "PATH") == 0) {
67 pwmd_free(pwm->pinentry_path);
68 pwm->pinentry_path = _expand_homedir(val, &pw);
72 pwmd_free(pwbuf);
73 fclose(fp);
76 static gpg_error_t launch_pinentry(pwm_t *pwm)
78 int rc;
79 assuan_context_t ctx;
80 int child_list[] = {-1};
81 char *display = getenv("DISPLAY");
82 const char *argv[10];
83 const char **p = argv;
84 int have_display = 0;
85 char *tty = NULL;
86 char *ttybuf = NULL;
88 update_pinentry_settings(pwm);
90 if (pwm->pinentry_display || display)
91 have_display = 1;
92 else {
93 if (!pwm->pinentry_tty) {
94 ttybuf = pwmd_malloc(255);
96 if (!ttybuf)
97 return gpg_error_from_errno(ENOMEM);
99 rc = ttyname_r(STDOUT_FILENO, ttybuf, 255);
101 if (rc) {
102 pwmd_free(ttybuf);
103 return gpg_error_from_errno(rc);
106 tty = ttybuf;
108 else
109 tty = pwm->pinentry_tty;
112 if (!have_display && !tty)
113 return GPG_ERR_ENOTTY;
115 *p++ = "pinentry";
116 *p++ = have_display ? "--display" : "--ttyname";
117 *p++ = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
119 if (pwm->lcctype) {
120 *p++ = "--lc-ctype";
121 *p++ = pwm->lcctype;
124 if (pwm->lcmessages) {
125 *p++ = "--lc-messages";
126 *p++ = pwm->lcmessages;
129 *p = NULL;
131 if (!have_display) {
132 *p++ = "--ttytype";
133 *p++ = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
134 *p = NULL;
137 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
139 if (ttybuf)
140 pwmd_free(ttybuf);
142 if (rc)
143 return rc;
145 pwm->pid = assuan_get_pid(ctx);
146 pwm->pctx = ctx;
147 return set_pinentry_strings(pwm, 0);
150 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
152 if (!pwm->pctx) {
153 gpg_error_t rc = launch_pinentry(pwm);
155 if (rc)
156 return gpg_err_code(rc);
159 return _assuan_command(pwm, pwm->pctx, result, cmd);
162 static gpg_error_t set_pinentry_strings(pwm_t *pwm, int which)
164 char *tmp, *desc = NULL;
165 gpg_error_t error;
167 tmp = pwmd_malloc(ASSUAN_LINELENGTH+1);
169 if (!tmp)
170 return gpg_error_from_errno(ENOMEM);
172 if (!pwm->title)
173 pwm->title = pwmd_strdup_printf(N_("Password Manager Daemon: %s"),
174 pwm->name ? pwm->name : "libpwmd");
176 if (!pwm->title)
177 goto fail_no_mem;
179 if (!pwm->prompt)
180 pwm->prompt = pwmd_strdup(N_("Passphrase:"));
182 if (!pwm->prompt)
183 goto fail_no_mem;
185 if (!pwm->desc && (which == PINENTRY_OPEN || which == PINENTRY_SAVE)) {
186 if (which == PINENTRY_OPEN)
187 desc = pwmd_strdup_printf(N_("A passphrase is required to open the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
188 else
189 desc = pwmd_strdup_printf(N_("A passphrase is required to save to the file \"%s\". Please%%0Aenter the passphrase below."), pwm->filename);
191 if (!desc)
192 goto fail_no_mem;
195 if (pwm->desc)
196 desc = pwm->desc;
198 switch (which) {
199 case PINENTRY_OPEN:
200 case PINENTRY_SAVE:
201 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s", desc);
203 if (pwm->desc != desc)
204 pwmd_free(desc);
205 break;
206 case PINENTRY_OPEN_FAILED:
207 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
208 N_("Invalid passphrase, please try again."));
209 break;
210 case PINENTRY_SAVE_CONFIRM:
211 snprintf(tmp, ASSUAN_LINELENGTH, "SETERROR %s",
212 N_("Please type the passphrase again for confirmation."));
213 break;
216 error = pinentry_command(pwm, NULL, tmp);
218 if (error) {
219 pwmd_free(tmp);
220 return error;
223 snprintf(tmp, ASSUAN_LINELENGTH, "SETPROMPT %s", pwm->prompt);
224 error = pinentry_command(pwm, NULL, tmp);
226 if (error) {
227 pwmd_free(tmp);
228 return error;
231 snprintf(tmp, ASSUAN_LINELENGTH, "SETDESC %s", pwm->title);
232 error = pinentry_command(pwm, NULL, tmp);
233 pwmd_free(tmp);
234 return error;
236 fail_no_mem:
237 pwmd_free(tmp);
238 return gpg_error_from_errno(ENOMEM);
241 static gpg_error_t terminate_pinentry(pwm_t *pwm)
243 pid_t pid = pwm->pid;
245 pwm->pid = -1;
247 if (!pwm || pid == -1)
248 return GPG_ERR_INV_ARG;
250 if (kill(pid, 0) == 0) {
251 if (kill(pid, SIGTERM) == -1) {
252 if (kill(pid, SIGKILL) == -1)
253 return gpg_error_from_errno(errno);
256 else
257 return gpg_error_from_errno(errno);
259 return 0;
262 void _pinentry_disconnect(pwm_t *pwm)
264 if (pwm->pctx)
265 assuan_disconnect(pwm->pctx);
267 pwm->pctx = NULL;
268 pwm->pid = -1;
272 * Only called from a child process.
274 static void catchsig(int sig)
276 switch (sig) {
277 case SIGALRM:
278 if (gelapsed++ >= gtimeout) {
279 terminate_pinentry(gpwm);
280 gerror = GPG_ERR_TIMEOUT;
282 else
283 alarm(1);
284 break;
285 default:
286 break;
290 static gpg_error_t do_getpin(pwm_t *pwm, char **result)
292 if (gtimeout) {
293 signal(SIGALRM, catchsig);
294 alarm(1);
297 *result = NULL;
298 return pinentry_command(pwm, result, "GETPIN");
301 gpg_error_t _getpin(pwm_t *pwm, char **result, int which)
303 gpg_error_t rc;
305 gerror = 0;
306 rc = set_pinentry_strings(pwm, which);
308 if (rc) {
309 _pinentry_disconnect(pwm);
310 return rc;
313 rc = do_getpin(pwm, result);
316 * Since there was input cancel any timeout setting.
318 alarm(0);
319 signal(SIGALRM, SIG_DFL);
321 if (rc) {
322 if (pwm->pctx)
323 _pinentry_disconnect(pwm);
325 /* This lets pwmd_open2() with PWMD_OPTION_PINENTRY_TIMEOUT work. */
326 if (rc == GPG_ERR_EOF && gerror == GPG_ERR_TIMEOUT)
327 return gerror;
329 return rc;
332 return 0;
335 gpg_error_t _do_save_getpin(pwm_t *pwm, char **password)
337 int confirm = 0;
338 gpg_error_t error;
339 char *result = NULL;
341 again:
342 error = _getpin(pwm, &result, confirm ? PINENTRY_SAVE_CONFIRM : PINENTRY_SAVE);
344 if (error) {
345 if (pwm->pctx)
346 _pinentry_disconnect(pwm);
348 if (*password)
349 pwmd_free(*password);
351 return error;
354 if (!confirm++) {
355 *password = result;
356 goto again;
359 if (strcmp(*password, result)) {
360 pwmd_free(*password);
361 pwmd_free(result);
362 confirm = 0;
363 *password = NULL;
364 goto again;
367 pwmd_free(result);
368 _pinentry_disconnect(pwm);
369 return 0;