send_to_daemon() cleanup.
[pwmd.git] / libpwmd / libpwmd.c
blobfa69af874764cb29019cc50e5c24b51d8075a23b
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 bufsize = 0;
337 int ret = 1;
339 va_start(ap, fmt);
340 bufsize = vasprintf(&buf, fmt, ap);
341 va_end(ap);
342 bufsize++;
343 FD_ZERO(&fds);
344 FD_SET(pwm->fd, &fds);
345 n = select(pwm->fd + 1, NULL, &fds, NULL, NULL);
347 if (n == -1) {
348 ret = 1;
349 goto done;
352 if (FD_ISSET(pwm->fd, &fds)) {
353 size_t t = strlen(buf);
354 ssize_t len = send(pwm->fd, buf, t, 0);
356 if (len == -1) {
357 ret = 1;
358 goto done;
361 if (len == t)
362 ret = 0;
365 done:
366 secure_free(buf, bufsize);
367 return ret;
370 static char **parse_list_command(char *str)
372 char **buf = NULL;
373 char *p;
374 int n = 0;
376 while ((p = strsep(&str, "\n")) != NULL) {
377 buf = realloc(buf, (n + 2) * sizeof(char *));
378 buf[n++] = strdup(p);
379 buf[n] = NULL;
382 return buf;
385 static void init_cache_id(pwm_t *pwm)
387 char t[16];
388 long n = random();
390 snprintf(t, sizeof(t), "%li", n);
392 if (strncmp(t, pwm->cache_id, sizeof(t)) == 0)
393 init_cache_id(pwm);
395 strncpy(pwm->cache_id, t, sizeof(pwm->cache_id));
398 static gpg_error_t connect_to_agent(pwm_t *pwm)
400 char *env;
401 char *p;
402 pid_t pid;
403 int rc;
404 char path[PATH_MAX], *t;
405 assuan_context_t ctx;
407 if ((env = getenv("GPG_AGENT_INFO")) == NULL)
408 return 1;
410 env = strdup(env);
412 for (p = env, t = path; *p; p++) {
413 if (*p == ':') {
414 *t = 0;
415 p++;
416 pid = atoi(p);
417 break;
420 *t++ = *p;
423 free(env);
424 rc = assuan_socket_connect(&ctx, path, pid);
425 pwm->ctx = ctx;
427 if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED)
428 return 1;
430 init_cache_id(pwm);
432 if (!pwm->title)
433 pwm->title = strdup("LibPWMD");
435 if (!pwm->prompt)
436 pwm->prompt = strdup("Password:");
438 if (!pwm->desc)
439 pwm->desc = strdup("Enter a password.");
441 return send_pinentry_environment (pwm->ctx, GPG_ERR_SOURCE_DEFAULT,
442 NULL, NULL, NULL, NULL, NULL);
445 /* From GnuPG (g10/call-agent.c) */
446 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
447 escaping. Note that the provided buffer needs to be 3 times the
448 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
449 static char *
450 percent_plus_escape (char *p, const char *atext)
452 const unsigned char *s;
454 for (s=atext; *s; s++)
456 if (*s < ' ' || *s == '+')
458 sprintf (p, "%%%02X", *s);
459 p += 3;
461 else if (*s == ' ')
462 *p++ = '+';
463 else
464 *p++ = *s;
466 *p = 0;
467 return p;
470 typedef struct {
471 size_t len;
472 void *buf;
473 } membuf_t;
475 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
477 membuf_t *mem = data;
478 void *p;
480 if (!buffer)
481 return 0;
483 if ((p = realloc(mem->buf, mem->len + len)) == NULL)
484 return 1;
486 mem->buf = p;
487 memcpy(mem->buf + mem->len, buffer, len);
488 mem->len += len;
489 return 0;
493 static int get_agent_password(pwm_t *pwm, char **password)
495 char cmd[] = "GET_PASSPHRASE --data -- ";
496 char *line;
497 char *p;
498 membuf_t data;
499 int rc;
501 if (!pwm->ctx) {
502 if (connect_to_agent(pwm))
503 return 1;
506 line = malloc(strlen(cmd) + 1
507 + (3 * sizeof(pwm->cache_id) + 1)
508 + (3 * strlen(pwm->title) + 1)
509 + (3 * strlen(pwm->desc) + 1)
510 + (3 * strlen(pwm->prompt) + 1) + 1);
511 p = stpcpy(line, cmd);
512 p = percent_plus_escape(p, pwm->cache_id);
513 *p++ = ' ';
514 p = percent_plus_escape(p, pwm->desc);
515 *p++ = ' ';
516 p = percent_plus_escape(p, pwm->prompt);
517 *p++ = ' ';
518 p = percent_plus_escape(p, pwm->title);
519 data.len = 0;
520 data.buf = NULL;
521 rc = assuan_transact(pwm->ctx, line, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
523 if (rc) {
524 if (data.buf) {
525 memset(data.buf, 0, data.len);
526 free(data.buf);
529 else {
530 mem_realloc_cb(&data, "", 1);
531 *password = (char *)data.buf;
534 free(line);
535 return 0;
538 static int clear_agent_password(pwm_t *pwm)
540 char *buf = "CLEAR_PASSPHRASE";
541 char *line;
542 size_t len = strlen(buf) + sizeof(pwm->cache_id) + 2;
543 int rc;
545 if (pwm->ctx) {
546 if ((line = malloc(len)) == NULL)
547 return 1;
549 sprintf(line, "%s %s", buf, pwm->cache_id);
550 rc = assuan_transact(pwm->ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
551 free(line);
553 if (rc)
554 return 1;
557 return 0;
560 int pwmd_command(pwm_t *pwm, void **result, int *error, pwmd_cmd cmd, ...)
562 va_list ap;
563 char *res = NULL;
564 int n;
565 char *filename = NULL;
566 char *p = NULL;
567 char *password = NULL;
568 void *tresult = NULL;
569 int terror = 0;
570 char *arg1, *arg2, *arg3;
572 if (!pwm)
573 return PWMD_ERROR;
575 *result = NULL;
576 *error = EPWMD_ERROR;
577 va_start(ap, cmd);
579 switch (cmd) {
580 case PWMD_OPEN:
581 filename = va_arg(ap, char *);
584 * Avoid calling gpg-agent if the password is cached on the
585 * server.
587 n = pwmd_command(pwm, &tresult, &terror, PWMD_CACHE,
588 PWMD_CACHE_ISCACHED, filename);
590 switch (n) {
591 case PWMD_ERROR:
592 *error = terror;
593 return PWMD_ERROR;
594 case PWMD_PERROR:
595 if (terror == EPWMD_CACHE_NOT_FOUND) {
597 * Get the password from gpg-agent/pinentry.
599 if (pwm->use_agent) {
600 if (get_agent_password(pwm, &password)) {
601 *error = EPWMD_ERROR;
602 return PWMD_AGENT_ERROR;
605 if (!password) {
606 *error = EPWMD_KEY;
607 return PWMD_PERROR;
610 p = pwmd_base64_encode(password, strlen(password));
611 secure_free(password, strlen(password) + 1);
612 password = p;
615 * We use pwmd's cache from now on.
617 clear_agent_password(pwm);
618 break;
620 else {
622 * Not using gpg-agent and the file was not found
623 * in the cache.
625 if (pwm->password == NULL) {
626 *error = EPWMD_KEY;
627 return PWMD_PERROR;
630 password = strdup(pwm->password);
633 else if (terror != EPWMD_FILE_NOT_FOUND) {
634 *error = terror;
635 return PWMD_PERROR;
637 break;
638 case PWMD_OK:
639 break;
642 if (send_to_daemon(pwm, "OPEN %s %s\n", filename, (password) ? password : "")) {
643 if (password)
644 secure_free(password, strlen(password) + 1);
645 *error = errno;
646 return PWMD_ERROR;
649 if (password)
650 secure_free(password, strlen(password) + 1);
651 break;
652 case PWMD_LIST:
653 if (send_to_daemon(pwm, "LIST\n")) {
654 *error = errno;
655 return PWMD_ERROR;
657 break;
658 case PWMD_LIST_ACCOUNT:
659 p = va_arg(ap, char *);
661 if (send_to_daemon(pwm, "LIST %s\n", p)) {
662 *error = errno;
663 return PWMD_ERROR;
665 break;
666 case PWMD_GET:
667 arg1 = va_arg(ap, char *);
669 if (send_to_daemon(pwm, "GET %s\n", arg1)) {
670 *error = errno;
671 return PWMD_ERROR;
673 break;
674 case PWMD_STORE:
675 arg1 = va_arg(ap, char *);
677 if (send_to_daemon(pwm, "STORE %s\n", arg1)) {
678 *error = errno;
679 return PWMD_ERROR;
681 break;
682 case PWMD_DELETE:
683 arg1 = va_arg(ap, char *);
685 if (send_to_daemon(pwm, "DELETE %s\n", arg1)) {
686 *error = errno;
687 return PWMD_ERROR;
689 break;
690 case PWMD_ATTR_SET:
691 arg1 = va_arg(ap, char *);
692 arg2 = va_arg(ap, char *);
693 arg3 = va_arg(ap, char *);
695 if (send_to_daemon(pwm, "ATTR SET %s %s %s\n", arg1, arg2, arg3)) {
696 *error = errno;
697 return PWMD_ERROR;
699 break;
700 case PWMD_ATTR_DELETE:
701 arg1 = va_arg(ap, char *);
702 arg2 = va_arg(ap, char *);
704 if (send_to_daemon(pwm, "ATTR DELETE %s %s\n", arg1, arg2)) {
705 *error = errno;
706 return PWMD_ERROR;
708 break;
709 case PWMD_ATTR_LIST:
710 arg1 = va_arg(ap, char *);
712 if (send_to_daemon(pwm, "ATTR LIST %s\n", arg1)) {
713 *error = errno;
714 return PWMD_ERROR;
716 break;
717 case PWMD_CACHE:
718 n = va_arg(ap, int);
720 switch (n) {
721 case PWMD_CACHE_ISCACHED:
722 p = va_arg(ap, char *);
724 if (send_to_daemon(pwm, "CACHE ISCACHED %s\n", p)) {
725 *error = errno;
726 return PWMD_ERROR;
728 break;
729 case PWMD_CACHE_CLEAR:
730 p = va_arg(ap, char *);
732 if (send_to_daemon(pwm, "CACHE CLEAR %s\n", p)) {
733 *error = errno;
734 return PWMD_ERROR;
736 break;
737 case PWMD_CACHE_CLEARALL:
738 if (send_to_daemon(pwm, "CACHE CLEARALL\n")) {
739 *error = errno;
740 return PWMD_ERROR;
742 break;
744 break;
745 case PWMD_SETOPT:
746 n = va_arg(ap, int);
748 switch (n) {
749 case PWMD_OPTION_USEAGENT:
750 n = va_arg(ap, int);
752 if (n != 0 && n != 1)
753 return PWMD_ERROR;
755 pwm->use_agent = n;
756 break;
757 case PWMD_OPTION_PASSWORD:
758 if (pwm->password) {
759 memset(pwm->password, 0, strlen(pwm->password));
760 free(pwm->password);
763 p = va_arg(ap, char *);
764 pwm->password = pwmd_base64_encode(p, strlen(p));
765 break;
766 case PWMD_OPTION_TITLE:
767 if (pwm->title)
768 free(pwm->title);
769 pwm->title = strdup(va_arg(ap, char *));
770 break;
771 case PWMD_OPTION_PROMPT:
772 if (pwm->prompt)
773 free(pwm->prompt);
774 pwm->prompt = strdup(va_arg(ap, char *));
775 break;
776 case PWMD_OPTION_DESC:
777 if (pwm->desc)
778 free(pwm->desc);
779 pwm->desc = strdup(va_arg(ap, char *));
780 break;
781 default:
782 *error = 0;
783 return PWMD_ERROR;
786 return PWMD_OK;
787 case PWMD_SAVE:
788 if (pwm->use_agent) {
789 if (get_agent_password(pwm, &password)) {
790 *error = 0;
791 return PWMD_AGENT_ERROR;
794 if (!password || !*password) {
795 *error = EPWMD_KEY;
796 return PWMD_PERROR;
799 p = pwmd_base64_encode(password, strlen(password));
800 secure_free(password, strlen(password) + 1);
801 password = p;
804 * We use pwmd's cache from now on.
806 clear_agent_password(pwm);
808 else
809 password = pwm->password;
811 if (send_to_daemon(pwm, "SAVE %s\n", password)) {
812 if (password)
813 secure_free(password, strlen(password) + 1);
815 *error = errno;
816 return PWMD_ERROR;
819 if (password)
820 secure_free(password, strlen(password) + 1);
821 break;
822 case PWMD_DUMP:
823 if (send_to_daemon(pwm, "DUMP\n")) {
824 *error = errno;
825 return PWMD_ERROR;
827 break;
828 case PWMD_QUIT:
829 if (send_to_daemon(pwm, "QUIT\n")) {
830 *error = errno;
831 return PWMD_ERROR;
833 break;
834 default:
835 return PWMD_ERROR;
838 va_end(ap);
839 *result = NULL;
840 n = get_daemon_result(pwm, &res);
842 switch (n) {
843 case PWMD_PERROR:
844 p = res + 4;
845 *error = atoi(p);
846 free(res);
847 return PWMD_PERROR;
848 case PWMD_ERROR:
849 *error = errno;
850 return PWMD_ERROR;
851 case PWMD_OK:
852 switch (cmd) {
853 case PWMD_ATTR_LIST:
854 case PWMD_LIST:
855 case PWMD_LIST_ACCOUNT:
856 *result = parse_list_command(res);
857 break;
858 case PWMD_DUMP:
859 case PWMD_GET:
860 *result = res;
861 break;
862 default:
863 break;
865 default:
866 break;
869 return PWMD_OK;