Dont include assuan.h in libpwmd.h and change pwm->ctx to void *.
[pwmd.git] / libpwmd / libpwmd.c
blob1b19bf04450dc8ecc5a89081c209071b43ffdf83
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006 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 <glib.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 "asshelp.h"
51 const char *pwmd_strerror(int pwmd_errno)
53 const char *pwmd_error_str[] = {
54 "Unknown error",
55 "Invalid characters in filename",
56 "No cache slots available",
57 "Key required",
58 "Invalid key",
59 "Invalid command or command syntax",
60 "Element not found",
61 "Trailing element",
62 "Invalid character in element",
63 "Text element in account root element not allowed",
64 "Empty",
65 "Invalid attribute name or syntax",
66 "Will not overwrite existing account",
67 "File not found",
68 "No file is open",
69 "Existing file already open",
70 "General LibXML error",
71 "File not found in cache",
74 if (pwmd_errno < 0 || pwmd_errno >= EPWMD_MAX)
75 return "Invalid pwmd_errno";
77 return pwmd_error_str[pwmd_errno];
80 char *pwmd_base64_decode(const char *str)
82 gsize len;
83 guchar *buf = g_base64_decode(str, &len);
84 char *dst;
86 dst = strdup((char *)buf);
87 memset(buf, 0, len);
88 g_free(buf);
89 return dst;
92 char *pwmd_base64_encode(const char *str, size_t len)
94 gchar *buf = g_base64_encode((const guchar *)str, len);
95 char *dst;
97 if (!buf)
98 return NULL;
100 dst = strdup((char *)buf);
101 memset(buf, 0, strlen((char *)buf));
102 g_free(buf);
103 return dst;
106 static int parse_protocol(char *data, char **result)
108 char *p;
109 int i;
110 size_t total = strlen(data);
113 * Set the pointer to the end of the buffer.
115 p = data + strlen(data) - 1;
117 if (*p != '\n' || total < 3)
118 return -2;
120 p--;
123 * "Rewind" the pointer to the second to last newline character.
125 for (i = total - 1; i; i--, p--) {
126 if (*p == '\n')
127 break;
130 p++;
132 if (strncmp(p, "ERR ", 4) == 0) {
133 *result = strdup(p);
134 return PWMD_PERROR;
137 if (strncmp(p, "OK ", 3) == 0) {
138 p--;
139 *p = 0;
140 p = data;
142 if (strncmp(p, "BEGIN ", 6) == 0) {
143 p += 6;
145 while (*p && isdigit(*p))
146 p++;
148 if (*p)
149 p++;
152 *result = strdup(p);
153 return PWMD_OK;
156 return -2;
159 static int get_daemon_result(pwm_t *pwm, char **result)
161 fd_set fds;
162 int n;
163 char *data = NULL;
164 size_t total = 0;
166 while (1) {
167 FD_ZERO(&fds);
168 FD_SET(pwm->fd, &fds);
169 n = select(pwm->fd + 1, &fds, NULL, NULL, NULL);
171 if (n == -1)
172 return PWMD_ERROR;
174 if (FD_ISSET(pwm->fd, &fds)) {
175 char buf[4096];
176 ssize_t len = recv(pwm->fd, buf, sizeof(buf), 0);
178 if (len == 0) {
179 if (data)
180 free(data);
182 return PWMD_OK;
185 if (len == -1) {
186 if (data) {
187 memset(data, 0, total);
188 free(data);
191 return PWMD_ERROR;
195 * Keep appending to data until a valid protocol code is found.
197 data = realloc(data, len + total + 1);
198 memcpy(&(data[total]), buf, len);
199 total += len;
200 data[total] = 0;
202 switch (parse_protocol(data, result)) {
203 case -2:
204 continue;
205 case PWMD_ERROR:
206 free(data);
207 return PWMD_ERROR;
208 case PWMD_PERROR:
209 memset(data, 0, total);
210 free(data);
211 return PWMD_PERROR;
212 default:
213 break;
216 break;
220 memset(data, 0, total);
221 free(data);
222 return PWMD_OK;
225 pwm_t *pwmd_connect(const char *path, int *error)
227 int fd;
228 struct sockaddr_un addr;
229 pwm_t *pwm = NULL;
230 char *result = NULL;
231 char cwd[PATH_MAX];
232 char *socketdir = NULL, *socketname = NULL, *socketarg;
233 char *p = NULL;
234 time_t now;
235 struct passwd *pw;
237 if (!path) {
238 pw = getpwuid(getuid());
239 snprintf(cwd, sizeof(cwd), "%s/.pwmd/socket", pw->pw_dir);
240 socketarg = strdup(cwd);
242 else
243 socketarg = strdup(path);
245 if (getcwd(cwd, sizeof(cwd)) == NULL) {
246 free(socketarg);
247 *error = errno;
248 return NULL;
251 if (strchr(socketarg, '/') == NULL) {
252 socketdir = strdup(cwd);
253 p = strdup(socketarg);
254 socketname = p;
256 else {
257 p = strdup(strrchr(socketarg, '/'));
258 socketname = p + 1;
259 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
260 socketdir = strdup(socketarg);
263 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
264 *error = errno;
265 free(socketdir);
266 free(socketarg);
267 free(p);
268 return NULL;
271 if (chdir(socketdir) == -1) {
272 close(fd);
273 *error = errno;
274 free(socketdir);
275 free(socketarg);
276 free(p);
277 return NULL;
280 addr.sun_family = AF_UNIX;
281 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
283 if (connect(fd, (struct sockaddr *)&addr,
284 sizeof(struct sockaddr)) == -1) {
285 close(fd);
286 *error = errno;
287 free(socketdir);
288 free(p);
289 free(socketarg);
290 return NULL;
293 free(socketdir);
294 free(socketarg);
295 free(p);
297 if ((pwm = calloc(1, sizeof(pwm_t))) == NULL) {
298 close(fd);
299 *error = errno;
300 return NULL;
303 pwm->fd = fd;
304 time(&now);
305 srandom(now);
308 * Clear the initial OK response
310 get_daemon_result(pwm, &result);
311 return pwm;
314 void pwmd_close(pwm_t *pwm)
316 if (!pwm || pwm->fd < 0)
317 return;
319 shutdown(pwm->fd, SHUT_RDWR);
320 close(pwm->fd);
321 free(pwm);
324 static void secure_free(void *p, size_t len)
326 memset(p, 0, len);
327 free(p);
330 static int send_to_daemon(pwm_t *pwm, char *fmt, ...)
332 va_list ap;
333 char *buf;
334 fd_set fds;
335 int n;
336 size_t sent = 0;
337 size_t bufsize = 0;
339 va_start(ap, fmt);
340 bufsize = vasprintf(&buf, fmt, ap);
341 va_end(ap);
342 bufsize++;
344 while (1) {
345 FD_ZERO(&fds);
346 FD_SET(pwm->fd, &fds);
347 n = select(pwm->fd + 1, NULL, &fds, NULL, NULL);
349 if (n == -1) {
350 secure_free(buf, bufsize);
351 return 1;
354 if (FD_ISSET(pwm->fd, &fds)) {
355 size_t t = strlen(buf);
356 ssize_t len = send(pwm->fd, buf, t, 0);
358 if (len == -1) {
359 secure_free(buf, bufsize);
360 return 1;
363 sent += len;
366 * Keep sending data to the socket until the entire buffer has
367 * been sent.
369 if (sent < t) {
370 memmove(&(buf[0]), buf + len, t - len);
371 buf[t - len] = 0;
372 continue;
375 break;
379 secure_free(buf, bufsize);
380 return 0;
383 static char **parse_list_command(char *str)
385 char **buf = NULL;
386 char *p;
387 int n = 0;
389 while ((p = strsep(&str, "\n")) != NULL) {
390 buf = realloc(buf, (n + 2) * sizeof(char *));
391 buf[n++] = strdup(p);
392 buf[n] = NULL;
395 return buf;
398 static char *attr_to_str(int opt)
400 switch (opt) {
401 case PWMD_ATTR_NAME:
402 return "name";
403 default:
404 return NULL;
407 return NULL;
410 static void init_cache_id(pwm_t *pwm)
412 char t[16];
413 long n = random();
415 snprintf(t, sizeof(t), "%li", n);
417 if (strncmp(t, pwm->cache_id, sizeof(t)) == 0)
418 init_cache_id(pwm);
420 strncpy(pwm->cache_id, t, sizeof(pwm->cache_id));
423 static gpg_error_t connect_to_agent(pwm_t *pwm)
425 char *env;
426 char *p;
427 pid_t pid;
428 int rc;
429 char path[PATH_MAX], *t;
430 assuan_context_t ctx;
432 if ((env = getenv("GPG_AGENT_INFO")) == NULL)
433 return 1;
435 env = strdup(env);
437 for (p = env, t = path; *p; p++) {
438 if (*p == ':') {
439 *t = 0;
440 p++;
441 pid = atoi(p);
442 break;
445 *t++ = *p;
448 free(env);
449 rc = assuan_socket_connect(&ctx, path, pid);
450 pwm->ctx = ctx;
452 if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED)
453 return 1;
455 init_cache_id(pwm);
457 if (!pwm->title)
458 pwm->title = strdup("LibPWMD");
460 if (!pwm->prompt)
461 pwm->prompt = strdup("Password:");
463 if (!pwm->desc)
464 pwm->desc = strdup("Enter a password.");
466 return send_pinentry_environment (pwm->ctx, GPG_ERR_SOURCE_DEFAULT,
467 NULL, NULL, NULL, NULL, NULL);
470 /* From GnuPG (g10/call-agent.c) */
471 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
472 escaping. Note that the provided buffer needs to be 3 times the
473 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
474 static char *
475 percent_plus_escape (char *p, const char *atext)
477 const unsigned char *s;
479 for (s=atext; *s; s++)
481 if (*s < ' ' || *s == '+')
483 sprintf (p, "%%%02X", *s);
484 p += 3;
486 else if (*s == ' ')
487 *p++ = '+';
488 else
489 *p++ = *s;
491 *p = 0;
492 return p;
495 typedef struct {
496 size_t len;
497 void *buf;
498 } membuf_t;
500 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
502 membuf_t *mem = data;
503 void *p;
505 if (!buffer)
506 return 0;
508 if ((p = realloc(mem->buf, mem->len + len)) == NULL)
509 return 1;
511 mem->buf = p;
512 memcpy(mem->buf + mem->len, buffer, len);
513 mem->len += len;
514 return 0;
518 static int get_agent_password(pwm_t *pwm, char **password)
520 char cmd[] = "GET_PASSPHRASE --data -- ";
521 char *line;
522 char *p;
523 membuf_t data;
524 int rc;
526 if (!pwm->ctx) {
527 if (connect_to_agent(pwm))
528 return 1;
531 line = malloc(strlen(cmd) + 1
532 + (3 * sizeof(pwm->cache_id) + 1)
533 + (3 * strlen(pwm->title) + 1)
534 + (3 * strlen(pwm->desc) + 1)
535 + (3 * strlen(pwm->prompt) + 1) + 1);
536 p = stpcpy(line, cmd);
537 p = percent_plus_escape(p, pwm->cache_id);
538 *p++ = ' ';
539 p = percent_plus_escape(p, pwm->desc);
540 *p++ = ' ';
541 p = percent_plus_escape(p, pwm->prompt);
542 *p++ = ' ';
543 p = percent_plus_escape(p, pwm->title);
544 data.len = 0;
545 data.buf = NULL;
546 rc = assuan_transact(pwm->ctx, line, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
548 if (rc) {
549 if (data.buf) {
550 memset(data.buf, 0, data.len);
551 free(data.buf);
554 else {
555 mem_realloc_cb(&data, "", 1);
556 *password = (char *)data.buf;
559 free(line);
560 return 0;
563 static int clear_agent_password(pwm_t *pwm)
565 char *buf = "CLEAR_PASSPHRASE";
566 char *line;
567 size_t len = strlen(buf) + sizeof(pwm->cache_id) + 2;
568 int rc;
570 if (pwm->ctx) {
571 if ((line = malloc(len)) == NULL)
572 return 1;
574 sprintf(line, "%s %s", buf, pwm->cache_id);
575 rc = assuan_transact(pwm->ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
576 free(line);
578 if (rc)
579 return 1;
582 return 0;
585 int pwmd_command(pwm_t *pwm, void **result, int *error, pwmd_cmd cmd, ...)
587 va_list ap;
588 char *res = NULL;
589 int n;
590 char *filename = NULL;
591 char *p = NULL;
592 char *password = NULL;
593 char *account = NULL;
594 char *opt = NULL;
595 char *value = NULL;
596 void *tresult = NULL;
597 int terror = 0;
599 if (!pwm)
600 return PWMD_ERROR;
602 *result = NULL;
603 *error = EPWMD_ERROR;
604 va_start(ap, cmd);
606 switch (cmd) {
607 case PWMD_OPEN:
608 filename = va_arg(ap, char *);
611 * Avoid calling gpg-agent if the password is cached on the
612 * server.
614 n = pwmd_command(pwm, &tresult, &terror, PWMD_CACHE,
615 PWMD_CACHE_ISCACHED, filename);
617 switch (n) {
618 case PWMD_ERROR:
619 *error = terror;
620 return PWMD_ERROR;
621 case PWMD_PERROR:
622 if (terror == EPWMD_CACHE_NOT_FOUND) {
624 * Get the password from gpg-agent/pinentry.
626 if (pwm->use_agent) {
627 if (get_agent_password(pwm, &password)) {
628 *error = EPWMD_ERROR;
629 return PWMD_AGENT_ERROR;
632 if (!password) {
633 *error = EPWMD_KEY;
634 return PWMD_PERROR;
637 p = pwmd_base64_encode(password, strlen(password));
638 secure_free(password, strlen(password) + 1);
639 password = p;
642 * We use pwmd's cache from now on.
644 clear_agent_password(pwm);
645 break;
647 else {
649 * Not using gpg-agent and the file was not found
650 * in the cache.
652 if (pwm->password == NULL) {
653 *error = EPWMD_KEY;
654 return PWMD_PERROR;
657 password = strdup(pwm->password);
660 else if (terror != EPWMD_FILE_NOT_FOUND) {
661 *error = terror;
662 return PWMD_PERROR;
664 break;
665 case PWMD_OK:
666 break;
669 if (send_to_daemon(pwm, "OPEN %s %s\n", filename, (password) ? password : "")) {
670 if (password)
671 secure_free(password, strlen(password) + 1);
672 *error = errno;
673 return PWMD_ERROR;
676 if (password)
677 secure_free(password, strlen(password) + 1);
678 break;
679 case PWMD_LIST:
680 if (send_to_daemon(pwm, "LIST\n")) {
681 *error = errno;
682 return PWMD_ERROR;
684 break;
685 case PWMD_LIST_ACCOUNT:
686 p = va_arg(ap, char *);
688 if (send_to_daemon(pwm, "LIST %s\n", p)) {
689 *error = errno;
690 return PWMD_ERROR;
692 break;
693 case PWMD_GET:
694 if (send_to_daemon(pwm, "GET %s\n", va_arg(ap, char *))) {
695 *error = errno;
696 return PWMD_ERROR;
698 break;
699 case PWMD_STORE:
700 if (send_to_daemon(pwm, "STORE %s\n", va_arg(ap, char *))) {
701 *error = errno;
702 return PWMD_ERROR;
704 break;
705 case PWMD_DELETE:
706 if (send_to_daemon(pwm, "DELETE %s\n", va_arg(ap, char *))) {
707 *error = errno;
708 return PWMD_ERROR;
710 break;
711 case PWMD_SETATTR:
712 account = va_arg(ap, char *);
713 opt = attr_to_str(va_arg(ap, int));
714 value = va_arg(ap, char *);
716 if (!opt)
717 return PWMD_PERROR;
719 if (send_to_daemon(pwm, "SETATTR %s %s %s\n", account, opt, value)) {
720 *error = errno;
721 return PWMD_ERROR;
723 break;
724 case PWMD_CACHE:
725 n = va_arg(ap, int);
727 switch (n) {
728 case PWMD_CACHE_ISCACHED:
729 p = va_arg(ap, char *);
731 if (send_to_daemon(pwm, "CACHE ISCACHED %s\n", p)) {
732 *error = errno;
733 return PWMD_ERROR;
735 break;
736 case PWMD_CACHE_CLEAR:
737 p = va_arg(ap, char *);
739 if (send_to_daemon(pwm, "CACHE CLEAR %s\n", p)) {
740 *error = errno;
741 return PWMD_ERROR;
743 break;
744 case PWMD_CACHE_CLEARALL:
745 if (send_to_daemon(pwm, "CACHE CLEARALL\n")) {
746 *error = errno;
747 return PWMD_ERROR;
749 break;
751 break;
752 case PWMD_SETOPT:
753 n = va_arg(ap, int);
755 switch (n) {
756 case PWMD_OPTION_USEAGENT:
757 n = va_arg(ap, int);
759 if (n != 0 && n != 1)
760 return PWMD_ERROR;
762 pwm->use_agent = n;
763 break;
764 case PWMD_OPTION_PASSWORD:
765 if (pwm->password) {
766 memset(pwm->password, 0, strlen(pwm->password));
767 free(pwm->password);
770 p = va_arg(ap, char *);
771 pwm->password = pwmd_base64_encode(p, strlen(p));
772 break;
773 case PWMD_OPTION_TITLE:
774 if (pwm->title)
775 free(pwm->title);
776 pwm->title = strdup(va_arg(ap, char *));
777 break;
778 case PWMD_OPTION_PROMPT:
779 if (pwm->prompt)
780 free(pwm->prompt);
781 pwm->prompt = strdup(va_arg(ap, char *));
782 break;
783 case PWMD_OPTION_DESC:
784 if (pwm->desc)
785 free(pwm->desc);
786 pwm->desc = strdup(va_arg(ap, char *));
787 break;
788 default:
789 *error = 0;
790 return PWMD_ERROR;
793 return PWMD_OK;
794 case PWMD_SAVE:
795 if (pwm->use_agent) {
796 if (get_agent_password(pwm, &password)) {
797 *error = 0;
798 return PWMD_AGENT_ERROR;
801 if (!password || !*password) {
802 *error = EPWMD_KEY;
803 return PWMD_PERROR;
806 p = pwmd_base64_encode(password, strlen(password));
807 secure_free(password, strlen(password) + 1);
808 password = p;
811 * We use pwmd's cache from now on.
813 clear_agent_password(pwm);
815 else
816 password = pwm->password;
818 if (send_to_daemon(pwm, "SAVE %s\n", password)) {
819 if (password)
820 secure_free(password, strlen(password) + 1);
822 *error = errno;
823 return PWMD_ERROR;
826 if (password)
827 secure_free(password, strlen(password) + 1);
828 break;
829 case PWMD_QUIT:
830 if (send_to_daemon(pwm, "QUIT\n")) {
831 *error = errno;
832 return PWMD_ERROR;
834 break;
835 default:
836 return PWMD_ERROR;
839 va_end(ap);
840 *result = NULL;
841 n = get_daemon_result(pwm, &res);
843 switch (n) {
844 case PWMD_PERROR:
845 p = res + 4;
846 *error = atoi(p);
847 free(res);
848 return PWMD_PERROR;
849 case PWMD_ERROR:
850 *error = errno;
851 return PWMD_ERROR;
852 case PWMD_OK:
853 switch (cmd) {
854 case PWMD_LIST:
855 case PWMD_LIST_ACCOUNT:
856 *result = parse_list_command(res);
857 break;
858 case PWMD_GET:
859 *result = res;
860 break;
861 default:
862 break;
864 default:
865 break;
868 return PWMD_OK;