Update copyright.
[pwmd.git] / libpwmd / libpwmd.c
blob90f9f4607155ac954e5c30ff9cb85f2ff18f2c98
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 <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 void init_cache_id(pwm_t *pwm)
400 char t[16];
401 long n = random();
403 snprintf(t, sizeof(t), "%li", n);
405 if (strncmp(t, pwm->cache_id, sizeof(t)) == 0)
406 init_cache_id(pwm);
408 strncpy(pwm->cache_id, t, sizeof(pwm->cache_id));
411 static gpg_error_t connect_to_agent(pwm_t *pwm)
413 char *env;
414 char *p;
415 pid_t pid;
416 int rc;
417 char path[PATH_MAX], *t;
418 assuan_context_t ctx;
420 if ((env = getenv("GPG_AGENT_INFO")) == NULL)
421 return 1;
423 env = strdup(env);
425 for (p = env, t = path; *p; p++) {
426 if (*p == ':') {
427 *t = 0;
428 p++;
429 pid = atoi(p);
430 break;
433 *t++ = *p;
436 free(env);
437 rc = assuan_socket_connect(&ctx, path, pid);
438 pwm->ctx = ctx;
440 if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED)
441 return 1;
443 init_cache_id(pwm);
445 if (!pwm->title)
446 pwm->title = strdup("LibPWMD");
448 if (!pwm->prompt)
449 pwm->prompt = strdup("Password:");
451 if (!pwm->desc)
452 pwm->desc = strdup("Enter a password.");
454 return send_pinentry_environment (pwm->ctx, GPG_ERR_SOURCE_DEFAULT,
455 NULL, NULL, NULL, NULL, NULL);
458 /* From GnuPG (g10/call-agent.c) */
459 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
460 escaping. Note that the provided buffer needs to be 3 times the
461 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
462 static char *
463 percent_plus_escape (char *p, const char *atext)
465 const unsigned char *s;
467 for (s=atext; *s; s++)
469 if (*s < ' ' || *s == '+')
471 sprintf (p, "%%%02X", *s);
472 p += 3;
474 else if (*s == ' ')
475 *p++ = '+';
476 else
477 *p++ = *s;
479 *p = 0;
480 return p;
483 typedef struct {
484 size_t len;
485 void *buf;
486 } membuf_t;
488 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
490 membuf_t *mem = data;
491 void *p;
493 if (!buffer)
494 return 0;
496 if ((p = realloc(mem->buf, mem->len + len)) == NULL)
497 return 1;
499 mem->buf = p;
500 memcpy(mem->buf + mem->len, buffer, len);
501 mem->len += len;
502 return 0;
506 static int get_agent_password(pwm_t *pwm, char **password)
508 char cmd[] = "GET_PASSPHRASE --data -- ";
509 char *line;
510 char *p;
511 membuf_t data;
512 int rc;
514 if (!pwm->ctx) {
515 if (connect_to_agent(pwm))
516 return 1;
519 line = malloc(strlen(cmd) + 1
520 + (3 * sizeof(pwm->cache_id) + 1)
521 + (3 * strlen(pwm->title) + 1)
522 + (3 * strlen(pwm->desc) + 1)
523 + (3 * strlen(pwm->prompt) + 1) + 1);
524 p = stpcpy(line, cmd);
525 p = percent_plus_escape(p, pwm->cache_id);
526 *p++ = ' ';
527 p = percent_plus_escape(p, pwm->desc);
528 *p++ = ' ';
529 p = percent_plus_escape(p, pwm->prompt);
530 *p++ = ' ';
531 p = percent_plus_escape(p, pwm->title);
532 data.len = 0;
533 data.buf = NULL;
534 rc = assuan_transact(pwm->ctx, line, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
536 if (rc) {
537 if (data.buf) {
538 memset(data.buf, 0, data.len);
539 free(data.buf);
542 else {
543 mem_realloc_cb(&data, "", 1);
544 *password = (char *)data.buf;
547 free(line);
548 return 0;
551 static int clear_agent_password(pwm_t *pwm)
553 char *buf = "CLEAR_PASSPHRASE";
554 char *line;
555 size_t len = strlen(buf) + sizeof(pwm->cache_id) + 2;
556 int rc;
558 if (pwm->ctx) {
559 if ((line = malloc(len)) == NULL)
560 return 1;
562 sprintf(line, "%s %s", buf, pwm->cache_id);
563 rc = assuan_transact(pwm->ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
564 free(line);
566 if (rc)
567 return 1;
570 return 0;
573 int pwmd_command(pwm_t *pwm, void **result, int *error, pwmd_cmd cmd, ...)
575 va_list ap;
576 char *res = NULL;
577 int n;
578 char *filename = NULL;
579 char *p = NULL;
580 char *password = NULL;
581 void *tresult = NULL;
582 int terror = 0;
583 char *arg1, *arg2, *arg3;
585 if (!pwm)
586 return PWMD_ERROR;
588 *result = NULL;
589 *error = EPWMD_ERROR;
590 va_start(ap, cmd);
592 switch (cmd) {
593 case PWMD_OPEN:
594 filename = va_arg(ap, char *);
597 * Avoid calling gpg-agent if the password is cached on the
598 * server.
600 n = pwmd_command(pwm, &tresult, &terror, PWMD_CACHE,
601 PWMD_CACHE_ISCACHED, filename);
603 switch (n) {
604 case PWMD_ERROR:
605 *error = terror;
606 return PWMD_ERROR;
607 case PWMD_PERROR:
608 if (terror == EPWMD_CACHE_NOT_FOUND) {
610 * Get the password from gpg-agent/pinentry.
612 if (pwm->use_agent) {
613 if (get_agent_password(pwm, &password)) {
614 *error = EPWMD_ERROR;
615 return PWMD_AGENT_ERROR;
618 if (!password) {
619 *error = EPWMD_KEY;
620 return PWMD_PERROR;
623 p = pwmd_base64_encode(password, strlen(password));
624 secure_free(password, strlen(password) + 1);
625 password = p;
628 * We use pwmd's cache from now on.
630 clear_agent_password(pwm);
631 break;
633 else {
635 * Not using gpg-agent and the file was not found
636 * in the cache.
638 if (pwm->password == NULL) {
639 *error = EPWMD_KEY;
640 return PWMD_PERROR;
643 password = strdup(pwm->password);
646 else if (terror != EPWMD_FILE_NOT_FOUND) {
647 *error = terror;
648 return PWMD_PERROR;
650 break;
651 case PWMD_OK:
652 break;
655 if (send_to_daemon(pwm, "OPEN %s %s\n", filename, (password) ? password : "")) {
656 if (password)
657 secure_free(password, strlen(password) + 1);
658 *error = errno;
659 return PWMD_ERROR;
662 if (password)
663 secure_free(password, strlen(password) + 1);
664 break;
665 case PWMD_LIST:
666 if (send_to_daemon(pwm, "LIST\n")) {
667 *error = errno;
668 return PWMD_ERROR;
670 break;
671 case PWMD_LIST_ACCOUNT:
672 p = va_arg(ap, char *);
674 if (send_to_daemon(pwm, "LIST %s\n", p)) {
675 *error = errno;
676 return PWMD_ERROR;
678 break;
679 case PWMD_GET:
680 arg1 = va_arg(ap, char *);
682 if (send_to_daemon(pwm, "GET %s\n", arg1)) {
683 *error = errno;
684 return PWMD_ERROR;
686 break;
687 case PWMD_STORE:
688 arg1 = va_arg(ap, char *);
690 if (send_to_daemon(pwm, "STORE %s\n", arg1)) {
691 *error = errno;
692 return PWMD_ERROR;
694 break;
695 case PWMD_DELETE:
696 arg1 = va_arg(ap, char *);
698 if (send_to_daemon(pwm, "DELETE %s\n", arg1)) {
699 *error = errno;
700 return PWMD_ERROR;
702 break;
703 case PWMD_ATTR_SET:
704 arg1 = va_arg(ap, char *);
705 arg2 = va_arg(ap, char *);
706 arg3 = va_arg(ap, char *);
708 if (send_to_daemon(pwm, "ATTR SET %s %s %s\n", arg1, arg2, arg3)) {
709 *error = errno;
710 return PWMD_ERROR;
712 break;
713 case PWMD_ATTR_DELETE:
714 arg1 = va_arg(ap, char *);
715 arg2 = va_arg(ap, char *);
717 if (send_to_daemon(pwm, "ATTR DELETE %s %s\n", arg1, arg2)) {
718 *error = errno;
719 return PWMD_ERROR;
721 break;
722 case PWMD_ATTR_LIST:
723 arg1 = va_arg(ap, char *);
725 if (send_to_daemon(pwm, "ATTR LIST %s\n", arg1)) {
726 *error = errno;
727 return PWMD_ERROR;
729 break;
730 case PWMD_CACHE:
731 n = va_arg(ap, int);
733 switch (n) {
734 case PWMD_CACHE_ISCACHED:
735 p = va_arg(ap, char *);
737 if (send_to_daemon(pwm, "CACHE ISCACHED %s\n", p)) {
738 *error = errno;
739 return PWMD_ERROR;
741 break;
742 case PWMD_CACHE_CLEAR:
743 p = va_arg(ap, char *);
745 if (send_to_daemon(pwm, "CACHE CLEAR %s\n", p)) {
746 *error = errno;
747 return PWMD_ERROR;
749 break;
750 case PWMD_CACHE_CLEARALL:
751 if (send_to_daemon(pwm, "CACHE CLEARALL\n")) {
752 *error = errno;
753 return PWMD_ERROR;
755 break;
757 break;
758 case PWMD_SETOPT:
759 n = va_arg(ap, int);
761 switch (n) {
762 case PWMD_OPTION_USEAGENT:
763 n = va_arg(ap, int);
765 if (n != 0 && n != 1)
766 return PWMD_ERROR;
768 pwm->use_agent = n;
769 break;
770 case PWMD_OPTION_PASSWORD:
771 if (pwm->password) {
772 memset(pwm->password, 0, strlen(pwm->password));
773 free(pwm->password);
776 p = va_arg(ap, char *);
777 pwm->password = pwmd_base64_encode(p, strlen(p));
778 break;
779 case PWMD_OPTION_TITLE:
780 if (pwm->title)
781 free(pwm->title);
782 pwm->title = strdup(va_arg(ap, char *));
783 break;
784 case PWMD_OPTION_PROMPT:
785 if (pwm->prompt)
786 free(pwm->prompt);
787 pwm->prompt = strdup(va_arg(ap, char *));
788 break;
789 case PWMD_OPTION_DESC:
790 if (pwm->desc)
791 free(pwm->desc);
792 pwm->desc = strdup(va_arg(ap, char *));
793 break;
794 default:
795 *error = 0;
796 return PWMD_ERROR;
799 return PWMD_OK;
800 case PWMD_SAVE:
801 if (pwm->use_agent) {
802 if (get_agent_password(pwm, &password)) {
803 *error = 0;
804 return PWMD_AGENT_ERROR;
807 if (!password || !*password) {
808 *error = EPWMD_KEY;
809 return PWMD_PERROR;
812 p = pwmd_base64_encode(password, strlen(password));
813 secure_free(password, strlen(password) + 1);
814 password = p;
817 * We use pwmd's cache from now on.
819 clear_agent_password(pwm);
821 else
822 password = pwm->password;
824 if (send_to_daemon(pwm, "SAVE %s\n", password)) {
825 if (password)
826 secure_free(password, strlen(password) + 1);
828 *error = errno;
829 return PWMD_ERROR;
832 if (password)
833 secure_free(password, strlen(password) + 1);
834 break;
835 case PWMD_DUMP:
836 if (send_to_daemon(pwm, "DUMP\n")) {
837 *error = errno;
838 return PWMD_ERROR;
840 break;
841 case PWMD_QUIT:
842 if (send_to_daemon(pwm, "QUIT\n")) {
843 *error = errno;
844 return PWMD_ERROR;
846 break;
847 default:
848 return PWMD_ERROR;
851 va_end(ap);
852 *result = NULL;
853 n = get_daemon_result(pwm, &res);
855 switch (n) {
856 case PWMD_PERROR:
857 p = res + 4;
858 *error = atoi(p);
859 free(res);
860 return PWMD_PERROR;
861 case PWMD_ERROR:
862 *error = errno;
863 return PWMD_ERROR;
864 case PWMD_OK:
865 switch (cmd) {
866 case PWMD_ATTR_LIST:
867 case PWMD_LIST:
868 case PWMD_LIST_ACCOUNT:
869 *result = parse_list_command(res);
870 break;
871 case PWMD_DUMP:
872 case PWMD_GET:
873 *result = res;
874 break;
875 default:
876 break;
878 default:
879 break;
882 return PWMD_OK;