Version 3.0.0.
[libpwmd.git] / libpwmd.c
blob8e321e7139ae6c3b32b726a54ca05dab664cd3f0
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-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 <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <ctype.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <time.h>
35 #include <libpwmd.h>
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
41 #ifdef HAVE_ASSUAN_H
42 #include <assuan.h>
43 #endif
45 #ifdef HAVE_SETLOCALE
46 #include <locale.h>
47 #endif
49 #include "mem.h"
50 #include "asshelp.h"
52 #ifdef USE_PINENTRY
53 #define PINENTRY_PATH "/usr/local/bin/pinentry"
55 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd);
56 #endif
58 typedef struct {
59 size_t len;
60 void *buf;
61 } membuf_t;
63 struct inquire_s {
64 assuan_context_t ctx;
65 const char *buf;
66 size_t len;
69 const char *pwmd_strerror(gpg_error_t pwmd_errno)
71 gpg_err_code_t code = gpg_err_code(pwmd_errno);
73 if (code >= GPG_ERR_USER_1 && code < gpg_err_code(EPWMD_MAX)) {
74 switch (code) {
75 case GPG_ERR_USER_1:
76 default:
77 return "Unknown error";
78 case GPG_ERR_USER_2:
79 return "No cache slots available";
80 case GPG_ERR_USER_3:
81 return "Element not found";
82 case GPG_ERR_USER_4:
83 return "Trailing element";
84 case GPG_ERR_USER_5:
85 return "Invalid character in element";
86 case GPG_ERR_USER_6:
87 return "Empty";
88 case GPG_ERR_USER_7:
89 return "Will not overwrite existing account";
90 case GPG_ERR_USER_8:
91 return "File not found";
92 case GPG_ERR_USER_9:
93 return "No file is open";
94 case GPG_ERR_USER_10:
95 return "General LibXML error";
96 case GPG_ERR_USER_11:
97 return "File not found in cache";
98 case GPG_ERR_USER_12:
99 return "Attribute not found";
100 case GPG_ERR_USER_13:
101 return "Invalid filename or link";
102 case GPG_ERR_USER_14:
103 return "File modified";
107 return gpg_strerror(pwmd_errno);
110 int pwmd_init()
112 gpg_err_init();
113 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
114 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
116 return PWMD_OK;
119 pwm_t *pwmd_connect(const char *path, gpg_error_t *error)
121 pwm_t *pwm = NULL;
122 char *socketpath = NULL;
123 time_t now;
124 struct passwd *pw;
125 assuan_context_t ctx;
126 int rc;
128 if (!path) {
129 pw = getpwuid(getuid());
130 socketpath = (char *)xmalloc(strlen(pw->pw_dir) + strlen("/.pwmd/socket") + 1);
131 sprintf(socketpath, "%s/.pwmd/socket", pw->pw_dir);
133 else
134 socketpath = xstrdup(path);
136 rc = assuan_socket_connect_ext(&ctx, socketpath, -1, 0);
137 xfree(socketpath);
139 if (rc) {
140 *error = rc;
141 return NULL;
144 if ((pwm = (pwm_t *)xcalloc(1, sizeof(pwm_t))) == NULL) {
145 *error = errno;
146 assuan_disconnect(ctx);
147 return NULL;
150 pwm->ctx = ctx;
151 time(&now);
152 srandom(now);
153 return pwm;
156 void pwmd_close(pwm_t *pwm)
158 if (!pwm)
159 return;
161 if (pwm->ctx)
162 assuan_disconnect(pwm->ctx);
164 if (pwm->password)
165 xfree(pwm->password);
167 if (pwm->title)
168 xfree(pwm->title);
170 if (pwm->desc)
171 xfree(pwm->desc);
173 if (pwm->prompt)
174 xfree(pwm->prompt);
176 if (pwm->pinentry_tty)
177 xfree(pwm->pinentry_tty);
179 if (pwm->pinentry_display)
180 xfree(pwm->pinentry_display);
182 if (pwm->pinentry_term)
183 xfree(pwm->pinentry_term);
185 if (pwm->filename)
186 xfree(pwm->filename);
188 xfree(pwm);
191 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
193 membuf_t *mem = (membuf_t *)data;
194 void *p;
196 if (!buffer)
197 return 0;
199 if ((p = xrealloc(mem->buf, mem->len + len)) == NULL)
200 return 1;
202 mem->buf = p;
203 memcpy((char *)mem->buf + mem->len, buffer, len);
204 mem->len += len;
205 return 0;
208 static int inquire_cb(void *data, const char *keyword)
210 struct inquire_s *inquire = (struct inquire_s *)data;
212 return assuan_send_data(inquire->ctx, inquire->buf, inquire->len);
215 void pwmd_free_result(void *data)
217 xfree(data);
220 static gpg_error_t assuan_command(assuan_context_t ctx, char **result, const char *cmd)
222 membuf_t data;
223 gpg_error_t rc;
225 data.len = 0;
226 data.buf = NULL;
229 * This is needed because assuan only accepts 1000 byte command strings.
230 * If the line is more than this the command will fail. So we use an
231 * INQUIRE on the server which waits for an END that assuan_transact()
232 * fulfills.
234 * Other commands shouldn't need the INQUIRE. Let me know if you have an
235 * element path that's greater than 1000 bytes and I'll fix it.
237 if (strncasecmp(cmd, "STORE", 5) == 0) {
238 const char *p = cmd + 5;
239 struct inquire_s *inq = (struct inquire_s *)xmalloc(sizeof(struct inquire_s));
241 if (*p == ' ')
242 p++;
244 inq->ctx = ctx;
245 inq->buf = p;
246 inq->len = strlen(p);
247 rc = assuan_transact(ctx, "STORE", mem_realloc_cb, &data, inquire_cb, inq, NULL, NULL);
248 xfree(inq);
250 else
251 rc = assuan_transact(ctx, cmd, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
253 // FIXME error codes are wrong for returned (failed) commands.
254 if (rc) {
255 if (data.buf) {
256 xfree(data.buf);
257 data.buf = NULL;
260 else {
261 if (data.buf) {
262 mem_realloc_cb(&data, "", 1);
263 *result = (char *)data.buf;
267 return rc;
270 #ifdef USE_PINENTRY
271 static gpg_error_t launch_pinentry(pwm_t *pwm)
273 int rc;
274 assuan_context_t ctx;
275 int child_list[] = {-1};
276 char *display = getenv("DISPLAY");
277 const char *argv[6];
278 char *buf;
279 int have_display = 0;
280 char *tty = NULL;
282 if (pwm->pinentry_display || display)
283 have_display = 1;
284 else {
285 tty = pwm->pinentry_tty ? pwm->pinentry_tty : ttyname(STDOUT_FILENO);
287 if (!tty)
288 return gpg_error_from_errno(errno);
291 if (!display && !tty)
292 return GPG_ERR_ENOTTY;
294 argv[0] = "pinentry";
295 argv[1] = have_display ? "--display" : "--ttyname";
296 argv[2] = have_display ? pwm->pinentry_display ? pwm->pinentry_display : display : tty;
297 argv[3] = NULL;
299 if (!have_display) {
300 argv[3] = "--ttytype";
301 argv[4] = pwm->pinentry_term ? pwm->pinentry_term : getenv("TERM");
302 argv[5] = NULL;
305 rc = assuan_pipe_connect(&ctx, pwm->pinentry_path ? pwm->pinentry_path : PINENTRY_PATH, argv, child_list);
307 if (rc)
308 return rc;
310 pwm->pctx = ctx;
312 if (!pwm->title)
313 pwm->title = xstrdup("LibPWMD");
315 if (!pwm->prompt)
316 pwm->prompt = xstrdup("Password:");
318 if (!pwm->desc)
319 pwm->desc = xstrdup("Enter a password.");
321 buf = (char *)xmalloc(strlen("SETPROMPT ") + strlen(pwm->prompt) + 1);
322 sprintf(buf, "SETPROMPT %s", pwm->prompt);
323 pinentry_command(pwm, NULL, buf);
324 xfree(buf);
326 buf = (char *)xmalloc(strlen("SETDESC ") + strlen(pwm->title) + 1);
327 sprintf(buf, "SETDESC %s", pwm->title);
328 pinentry_command(pwm, NULL, buf);
329 xfree(buf);
331 buf = (char *)xmalloc(strlen("SETERROR ") + strlen(pwm->desc) + 1);
332 sprintf(buf, "SETERROR %s", pwm->desc);
333 pinentry_command(pwm, NULL, buf);
334 xfree(buf);
335 return 0;
338 static gpg_error_t pinentry_command(pwm_t *pwm, char **result, const char *cmd)
340 gpg_error_t n;
342 if (!pwm->pctx) {
343 n = launch_pinentry(pwm);
345 if (n)
346 return n;
349 return assuan_command(pwm->pctx, result, cmd);
352 static void pinentry_disconnect(pwm_t *pwm)
354 assuan_disconnect(pwm->pctx);
355 pwm->pctx = NULL;
358 static char *percent_escape(const char *atext)
360 const unsigned char *s;
361 int len = strlen(atext) * 3 + 1;
362 char *buf = (char *)xmalloc(len), *p = buf;
364 if (!buf)
365 return NULL;
367 for (s=(const unsigned char *)atext; *s; s++) {
368 if (*s < ' ') {
369 sprintf (p, "%%%02X", *s);
370 p += 3;
372 else
373 *p++ = *s;
376 *p = 0;
377 return buf;
379 #endif
381 static gpg_error_t send_command(pwm_t *pwm, char **result, const char *cmd)
383 if (!cmd)
384 return EPWMD_ERROR;
386 return assuan_command(pwm->ctx, result, cmd);
389 int pwmd_command(pwm_t *pwm, char **result, gpg_error_t *error, const char *cmd, ...)
391 va_list ap;
392 char *buf;
393 size_t len;
395 if (!pwm)
396 return PWMD_ERROR;
398 if (!cmd) {
399 *error = EPWMD_COMMAND_SYNTAX;
400 return PWMD_ERROR;
403 *result = NULL;
404 *error = EPWMD_ERROR;
406 va_start(ap, cmd);
408 * C99
410 len = vsnprintf(NULL, 0, cmd, ap);
411 buf = xmalloc(len + 1);
412 len = vsnprintf(buf, len + 1, cmd, ap);
413 va_end(ap);
414 *error = send_command(pwm, result, buf);
415 xfree(buf);
416 return *error ? PWMD_ERROR : 0;
419 int pwmd_open(pwm_t *pwm, gpg_error_t *error, const char *filename)
421 gpg_error_t n;
422 char *result = NULL;
423 char *password = NULL;
424 char *p;
425 static char buf[1000];
427 if (!pwm || !filename || !*filename) {
428 *error = EPWMD_ERROR;
429 return PWMD_ERROR;
433 * Avoid calling pinentry if the password is cached on the server.
435 n = pwmd_command(pwm, &result, error, "ISCACHED %s", filename);
437 if (n && (*error == EPWMD_CACHE_NOT_FOUND || *error == EPWMD_FILE_NOT_FOUND)) {
438 if (pwm->passfunc) {
439 password = (*pwm->passfunc)(pwm->passdata);
441 if (!password || !*password) {
442 *error = EPWMD_KEY;
443 return PWMD_ERROR;
446 password = xstrdup(password);
447 goto gotpassword;
450 if (*error == EPWMD_CACHE_NOT_FOUND) {
451 #ifdef USE_PINENTRY
453 * Get the password from pinentry.
455 if (pwm->use_pinentry) {
456 n = pinentry_command(pwm, &password, "GETPIN");
458 if (n) {
459 *error = n;
461 if (pwm->pctx)
462 pinentry_disconnect(pwm);
464 return PWMD_ERROR;
467 pinentry_disconnect(pwm);
469 if (!password) {
470 *error = EPWMD_KEY;
471 return PWMD_ERROR;
474 p = xstrdup(password);
475 xfree(password);
476 password = p;
478 else {
479 #endif
481 * Not using pinentry and the file was not found
482 * in the cache.
484 if (pwm->password == NULL) {
485 *error = EPWMD_KEY;
486 return PWMD_ERROR;
489 password = pwm->password;
490 #ifdef USE_PINENTRY
492 #endif
494 else if (*error != EPWMD_FILE_NOT_FOUND)
495 return PWMD_ERROR;
498 gotpassword:
499 snprintf(buf, sizeof(buf), "OPEN %s %s", filename, password ? password : "");
500 *error = send_command(pwm, NULL, buf);
501 memset(&buf, 0, sizeof(buf));
502 xfree(password);
504 if (!pwm->use_pinentry && pwm->password)
505 pwm->password = NULL;
507 if (!*error) {
508 if (pwm->filename)
509 xfree(pwm->filename);
511 pwm->filename = xstrdup(filename);
514 return *error ? PWMD_ERROR : PWMD_OK;
517 int pwmd_save(pwm_t *pwm, gpg_error_t *error)
519 char *result = NULL;
520 gpg_error_t n;
521 char *password = NULL;
522 char *p;
523 static char buf[1000];
525 if (!pwm)
526 return PWMD_ERROR;
528 #ifdef USE_PINENTRY
529 if (pwm->use_pinentry || pwm->passfunc) {
530 #else
531 if (pwm->passfunc) {
532 #endif
533 n = pwmd_command(pwm, &result, error, "ISCACHED %s", pwm->filename);
535 if (n == PWMD_ERROR && (*error == EPWMD_CACHE_NOT_FOUND || *error == EPWMD_FILE_NOT_FOUND)) {
536 #ifdef USE_PINENTRY
537 if (pwm->use_pinentry) {
538 n = pinentry_command(pwm, &password, "GETPIN");
540 if (n) {
541 *error = n;
543 if (pwm->pctx)
544 pinentry_disconnect(pwm);
546 return PWMD_ERROR;
549 pinentry_disconnect(pwm);
551 else {
552 #endif
553 if (pwm->passfunc) {
554 password = (*pwm->passfunc)(pwm->passdata);
556 if (!password) {
557 *error = EPWMD_KEY;
558 return PWMD_ERROR;
561 #ifdef USE_PINENTRY
563 #endif
565 if (!password || !*password) {
566 *error = EPWMD_KEY;
567 return PWMD_ERROR;
570 p = xstrdup(password);
572 #ifdef USE_PINENTRY
573 if (!pwm->use_pinentry) {
574 memset(password, 0, strlen(password));
575 free(password);
577 else
578 xfree(password);
580 #else
581 memset(password, 0, strlen(password));
582 free(password);
583 #endif
584 password = p;
586 else {
587 if (n != PWMD_OK)
588 return PWMD_ERROR;
591 else
592 password = pwm->password;
594 snprintf(buf, sizeof(buf), "SAVE %s", password ? password : "");
595 *error = send_command(pwm, NULL, buf);
596 memset(&buf, 0, sizeof(buf));
597 xfree(password);
598 pwm->password = NULL;
599 return *error ? PWMD_ERROR : 0;
602 int pwmd_setopt(pwm_t *pwm, gpg_error_t *error, pwmd_option opt, ...)
604 va_list ap;
605 #ifdef USE_PINENTRY
606 int n = va_arg(ap, int);
607 #endif
608 char *arg1;
610 if (!pwm)
611 return PWMD_ERROR;
613 va_start(ap, opt);
615 switch (opt) {
616 case PWMD_OPTION_PASSWORD_FUNC:
617 pwm->passfunc = va_arg(ap, pwmd_password_func *);
618 break;
619 case PWMD_OPTION_PASSWORD_DATA:
620 pwm->passdata = va_arg(ap, void *);
621 break;
622 #ifdef USE_PINENTRY
623 case PWMD_OPTION_PINENTRY:
624 n = va_arg(ap, int);
626 if (n != 0 && n != 1) {
627 va_end(ap);
628 return PWMD_ERROR;
631 pwm->use_pinentry = n;
632 break;
633 case PWMD_OPTION_PINENTRY_PATH:
634 if (pwm->pinentry_path)
635 xfree(pwm->pinentry_path);
637 pwm->pinentry_path = xstrdup(va_arg(ap, char *));
638 break;
639 case PWMD_OPTION_PINENTRY_TTY:
640 if (pwm->pinentry_tty)
641 xfree(pwm->pinentry_tty);
643 pwm->pinentry_tty = xstrdup(va_arg(ap, char *));
644 break;
645 case PWMD_OPTION_PINENTRY_DISPLAY:
646 if (pwm->pinentry_display)
647 xfree(pwm->pinentry_display);
649 pwm->pinentry_display = xstrdup(va_arg(ap, char *));
650 break;
651 case PWMD_OPTION_PINENTRY_TERM:
652 if (pwm->pinentry_term)
653 xfree(pwm->pinentry_term);
655 pwm->pinentry_term = xstrdup(va_arg(ap, char *));
656 break;
657 #else
658 case PWMD_OPTION_PINENTRY:
659 case PWMD_OPTION_PINENTRY_PATH:
660 case PWMD_OPTION_PINENTRY_TTY:
661 case PWMD_OPTION_PINENTRY_DISPLAY:
662 case PWMD_OPTION_PINENTRY_TERM:
663 *error = EPWMD_ERROR;
664 return PWMD_ERROR;
665 #endif
666 case PWMD_OPTION_PASSWORD:
667 arg1 = va_arg(ap, char *);
669 if (pwm->password)
670 xfree(pwm->password);
672 pwm->password = xstrdup(arg1);
673 break;
674 #ifdef USE_PINENTRY
675 case PWMD_OPTION_TITLE:
676 if (pwm->title)
677 xfree(pwm->title);
678 pwm->title = percent_escape(va_arg(ap, char *));
679 break;
680 case PWMD_OPTION_PROMPT:
681 if (pwm->prompt)
682 xfree(pwm->prompt);
683 pwm->prompt = percent_escape(va_arg(ap, char *));
684 break;
685 case PWMD_OPTION_DESC:
686 if (pwm->desc)
687 xfree(pwm->desc);
688 pwm->desc = percent_escape(va_arg(ap, char *));
689 break;
690 #endif
691 default:
692 *error = EPWMD_ERROR;
693 va_end(ap);
694 return PWMD_ERROR;
697 va_end(ap);
698 return PWMD_OK;
701 int pwmd_get_password(pwm_t *pwm, gpg_error_t *error, const char *filename)
703 int p[2];
704 pid_t pid;
705 char *password = NULL;
706 gpg_error_t n;
707 char *result;
708 pwmd_password_s pw;
710 memset(&pw, 0, sizeof(pw));
712 if (!pwm) {
713 *error = EPWMD_ERROR;
714 return PWMD_ERROR;
717 if (filename) {
718 n = pwmd_command(pwm, &result, error, "ISCACHED %s", filename);
720 if (n != PWMD_OK && *error != EPWMD_CACHE_NOT_FOUND && *error != EPWMD_FILE_NOT_FOUND)
721 return -1;
723 * Don't need a password for non-existant files.
725 else if ((n != PWMD_OK && *error == EPWMD_FILE_NOT_FOUND) || n == PWMD_OK) {
726 *error = 0;
727 return -2;
731 if (pipe(p) == -1) {
732 *error = gpg_error_from_syserror();
733 return -1;
736 pid = fork();
738 switch (pid) {
739 case 0:
740 close(p[0]);
741 n = pinentry_command(pwm, &password, "GETPIN");
743 if (n) {
744 close(p[1]);
746 if (pwm->pctx)
747 pinentry_disconnect(pwm);
749 pw.error = n;
750 write(p[1], &pw, sizeof(pw));
751 _exit(1);
754 if (!password || !*password)
755 pw.error = EPWMD_KEY;
756 else
757 strncpy(pw.password, password, sizeof(pw.password));
759 write(p[1], &pw, sizeof(pw));
760 memset(&pw.password, 0, sizeof(pw.password));
761 xfree(password);
762 close(p[1]);
763 _exit(0);
764 case -1:
765 *error = gpg_error_from_syserror();
766 close(p[0]);
767 close(p[1]);
768 return -1;
769 default:
770 break;
773 close(p[1]);
774 return p[0];