When the pwmd client process does with SIGTERM, set the pinentry error
[pwmd.git] / src / pinentry.c
blob03874d7a4fe89e03f1be53ef9fef9f7c1b803442
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2007 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 02111-1307 USA
19 #include <stdlib.h>
20 #include <gcrypt.h>
21 #include <glib.h>
22 #include <errno.h>
23 #include <pth.h>
25 #define _ASSUAN_ONLY_GPG_ERRORS 1
26 #include <assuan.h>
28 #ifdef HAVE_CONFIG_H
29 #include <config.h>
30 #endif
32 #include "mem.h"
33 #include "common.h"
34 #include "pinentry.h"
35 #include "pwmd_error.h"
36 #include "assuan-errors.h"
38 typedef struct {
39 size_t len;
40 void *buf;
41 } membuf_t;
43 static struct client_s *global_client;
44 static gint send_fd;
46 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which);
48 /* Borrowed from libassuan 1.0.4 svn 277 */
49 /* Helper to map old style Assuan error codes to gpg-error codes.
50 This is used internally to keep an compatible ABI. */
51 static assuan_error_t
52 _assuan_error (int oldcode)
54 unsigned int n;
55 int err_source = 0;
57 switch (oldcode)
59 case ASSUAN_General_Error: n = 257; break;
60 case ASSUAN_Accept_Failed: n = 258; break;
61 case ASSUAN_Connect_Failed: n = 259; break;
62 case ASSUAN_Invalid_Response: n = 260; break;
63 case ASSUAN_Invalid_Value: n = 261; break;
64 case ASSUAN_Line_Not_Terminated: n = 262; break;
65 case ASSUAN_Line_Too_Long: n = 263; break;
66 case ASSUAN_Nested_Commands: n = 264; break;
67 case ASSUAN_No_Data_Callback: n = 265; break;
68 case ASSUAN_No_Inquire_Callback: n = 266; break;
69 case ASSUAN_Not_A_Server: n = 267; break;
70 case ASSUAN_Not_Implemented: n = 69; break;
71 case ASSUAN_Parameter_Conflict: n = 280; break;
72 case ASSUAN_Problem_Starting_Server: n = 269; break;
73 case ASSUAN_Server_Fault: n = 80; break;
74 case ASSUAN_Syntax_Error: n = 276; break;
75 case ASSUAN_Too_Much_Data: n = 273; break;
76 case ASSUAN_Unexpected_Command: n = 274; break;
77 case ASSUAN_Unknown_Command: n = 275; break;
78 case ASSUAN_Canceled: n = 277; break;
79 case ASSUAN_No_Secret_Key: n = 17; break;
80 case ASSUAN_Not_Confirmed: n = 114; break;
82 case ASSUAN_Read_Error:
83 switch (errno)
85 case 0: n = 16381; /*GPG_ERR_MISSING_ERRNO*/ break;
86 case EAGAIN:
87 n = (6 | (1 << 15));
88 break;
89 default: n = 270; /*GPG_ERR_ASS_READ_ERROR*/ break;
91 break;
93 case ASSUAN_Write_Error:
94 switch (errno)
96 case 0: n = 16381; /*GPG_ERR_MISSING_ERRNO*/ break;
97 case EAGAIN:
98 n = (6 | (1 << 15));
99 break;
100 default: n = 271; /*GPG_ERR_ASS_WRITE_ERROR*/ break;
102 break;
104 case ASSUAN_Out_Of_Core:
105 switch (errno)
107 case 0: /* Should not happen but a user might have provided
108 an incomplete implemented malloc function. Give
109 him a chance to correct this fault but make sure
110 an error is indeed returned. */
111 n = 16381; /*GPG_ERR_MISSING_ERRNO*/
112 break;
113 case ENOMEM:
114 n = (86 | (1 << 15));
115 break;
116 default:
117 n = 16382; /*GPG_ERR_UNKNOWN_ERRNO*/
118 break;
120 break;
122 case -1: n = 16383 /*GPG_ERR_EOF*/; break;
124 default:
125 n = 257;
126 break;
129 return ((err_source << 24) | (n & 0x00ffffff));
132 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
134 membuf_t *mem = (membuf_t *)data;
135 void *p;
137 if (!buffer)
138 return 0;
140 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
141 return 1;
143 mem->buf = p;
144 memcpy((char *)mem->buf + mem->len, buffer, len);
145 mem->len += len;
146 return 0;
149 static gpg_error_t assuan_command(struct pinentry_s *pin, char **result,
150 const char *cmd)
152 membuf_t data;
153 gpg_error_t rc;
155 data.len = 0;
156 data.buf = NULL;
158 rc = assuan_transact(pin->ctx, cmd, mem_realloc_cb, &data, NULL, NULL,
159 NULL, NULL);
161 if (rc) {
162 if (data.buf) {
163 xfree(data.buf);
164 data.buf = NULL;
167 else {
168 if (data.buf) {
169 mem_realloc_cb(&data, "", 1);
170 *result = (gchar *)data.buf;
174 return rc;
177 static gpg_error_t launch_pinentry(struct pinentry_s *pin)
179 int rc;
180 assuan_context_t ctx;
181 int child_list[] = {-1};
182 char *display = getenv("DISPLAY");
183 const char *argv[6];
184 int have_display = 0;
185 char *tty = NULL;
187 //update_pinentry_settings(pin);
189 if (pin->display || display)
190 have_display = 1;
191 else {
192 tty = pin->ttyname ? pin->ttyname : ttyname(STDOUT_FILENO);
194 if (!tty)
195 return _assuan_error(-1);
199 if (!display && !tty)
200 return _assuan_error(-1);
202 argv[0] = "pinentry";
203 argv[1] = have_display ? "--display" : "--ttyname";
204 argv[2] = have_display ? pin->display ? pin->display : display : tty;
205 argv[3] = NULL;
207 if (!have_display) {
208 argv[3] = "--ttytype";
209 argv[4] = pin->ttytype ? pin->ttytype : getenv("TERM");
210 argv[5] = NULL;
213 rc = assuan_pipe_connect(&ctx, pin->path ? pin->path : PINENTRY_PATH,
214 argv, child_list);
216 if (rc)
217 return _assuan_error(rc);
219 pin->pid = assuan_get_pid(ctx);
220 pin->ctx = ctx;
221 return set_pinentry_strings(pin, 0);
224 static gpg_error_t pinentry_command(struct pinentry_s *pin, gchar **result,
225 const gchar *cmd)
227 gpg_error_t error = 0;
229 if (!pin->ctx)
230 error = launch_pinentry(pin);
232 return error ? error : assuan_command(pin, result, cmd);
235 static gpg_error_t set_pinentry_strings(struct pinentry_s *pin, int which)
237 char *buf;
238 char tmp[ASSUAN_LINELENGTH];
239 gpg_error_t error;
241 if (!pin->title)
242 pin->title = xstrdup(N_("Password Manager Daemon"));
244 if (!pin->prompt)
245 pin->prompt = xstrdup(N_("Password:"));
247 if (!pin->desc && !which)
248 pin->desc = xstrdup(N_("Enter a password."));
250 if (which == 1) {
251 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Invalid password, please try again."));
252 buf = xstrdup(tmp);
254 else if (which == 2) {
255 snprintf(tmp, sizeof(tmp), "SETERROR %s", N_("Please type the password again for confirmation."));
256 buf = xstrdup(tmp);
258 else {
259 buf = (char *)xmalloc(strlen("SETERROR ") + strlen(pin->desc) + 1);
260 sprintf(buf, "SETERROR %s", pin->desc);
263 error = pinentry_command(pin, NULL, buf);
264 xfree(buf);
266 if (error)
267 return error;
269 buf = (char *)xmalloc(strlen("SETPROMPT ") + strlen(pin->prompt) + 1);
270 sprintf(buf, "SETPROMPT %s", pin->prompt);
271 error = pinentry_command(pin, NULL, buf);
272 xfree(buf);
274 if (error)
275 return error;
277 buf = (char *)xmalloc(strlen("SETDESC ") + strlen(pin->title) + 1);
278 sprintf(buf, "SETDESC %s", pin->title);
279 error = pinentry_command(pin, NULL, buf);
280 xfree(buf);
281 return error;
284 static void pinentry_disconnect(struct pinentry_s *pin)
286 if (!pin)
287 return;
289 if (pin->ctx)
290 assuan_disconnect(pin->ctx);
292 pin->ctx = NULL;
293 pin->pid = 0;
296 static gpg_error_t do_getpin(struct pinentry_s *pin, char **result)
298 gpg_error_t error;
300 *result = NULL;
301 error = pinentry_command(pin, result, "GETPIN");
303 if (error)
304 error = _assuan_error(error);
306 if (!error && !*result)
307 error = EPWMD_KEY;
309 return error;
312 static gpg_error_t getpin(struct pinentry_s *pin, gchar **result, int which)
314 gpg_error_t error = set_pinentry_strings(pin, which);
316 if (error) {
317 pinentry_disconnect(pin);
318 return error;
321 error = do_getpin(pin, result);
322 pinentry_disconnect(pin);
323 return error;
326 static void catchsig(gint sig)
328 pinentry_key_s pk;
330 switch (sig) {
331 case SIGTERM:
332 case SIGALRM:
333 if (global_client->pinentry->pid) {
334 kill(SIGTERM, global_client->pinentry->pid);
335 global_client->pinentry->pid = 0;
338 memset(&pk, 0, sizeof(pk));
339 pk.error = sig == SIGTERM ? GPG_ERR_ASS_CANCELED : GPG_ERR_TIMEOUT;
340 pth_write(send_fd, &pk, sizeof(pk));
341 cleanup_pinentry(global_client->pinentry);
342 free_client(global_client);
343 _exit(1);
344 break;
348 gpg_error_t pinentry_fork(assuan_context_t ctx)
350 struct client_s *client = assuan_get_pointer(ctx);
351 gpg_error_t error;
352 gint p[2];
353 pid_t pid;
354 pinentry_key_s pk;
355 gsize len;
356 gchar *result;
358 if (pipe(p) == -1) {
359 error = gpg_error_from_syserror();
360 return send_error(ctx, error);
363 pid = pth_fork();
365 switch (pid) {
366 case -1:
367 error = gpg_error_from_syserror();
368 close(p[0]);
369 close(p[1]);
370 return send_error(ctx, error);
371 case 0:
372 signal(SIGALRM, catchsig);
373 signal(SIGTERM, catchsig);
374 close(p[0]);
375 send_fd = p[1];
376 global_client = client;
377 alarm(client->pinentry->timeout);
378 pk.error = getpin(client->pinentry, &result, 0);
379 cleanup_pinentry(client->pinentry);
380 free_client(client);
382 if (pk.error) {
383 len = pth_write(p[1], &pk, sizeof(pk));
384 memset(&pk, 0, sizeof(pk));
385 close(p[1]);
387 if (len != sizeof(pk))
388 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
390 _exit(1);
393 pk.error = 0;
394 strncpy(pk.key, result, sizeof(pk.key));
395 xfree(result);
396 len = pth_write(p[1], &pk, sizeof(pk));
397 memset(&pk, 0, sizeof(pk));
398 close(p[1]);
400 if (len != sizeof(pk))
401 log_write("%s(%i): write: len != sizeof(pk)", __FUNCTION__, __LINE__);
403 _exit(0);
404 default:
405 client->pinentry->fd = p[0];
406 client->pinentry->pid = pid;
407 client->pinentry->status = PINENTRY_INIT;
408 break;
412 * Don't call assuan_process_done() here. That should be done in
413 * open_command_finalize() after the key has been read().
415 return 0;
418 void cleanup_pinentry(struct pinentry_s *pin)
420 if (!pin)
421 return;
423 if (pin->ctx && pin->pid)
424 pinentry_disconnect(pin);
426 xfree(pin->ttyname);
427 xfree(pin->ttytype);
428 xfree(pin->desc);
429 xfree(pin->title);
430 xfree(pin->prompt);
431 xfree(pin->path);
432 xfree(pin->display);
433 xfree(pin);