Fixed a double free().
[pwmd.git] / libpwmd / libpwmd.c
bloba99fd4b30d31a40640b4e433b98fe4f7309bf3ed
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",
72 "Attribute not found",
73 "Not a file",
76 if (pwmd_errno < 0 || pwmd_errno >= EPWMD_MAX)
77 return "Invalid pwmd_errno";
79 return pwmd_error_str[pwmd_errno];
82 void pwmd_list_free(char **list)
84 char **p;
86 if (!list)
87 return;
89 for (p = list; *p; p++)
90 free(*p);
92 free(list);
95 unsigned char *pwmd_base64_decode(const char *str, size_t *size)
97 gsize len;
98 guchar *buf = g_base64_decode(str, &len);
100 *size = len;
101 return buf;
104 char *pwmd_base64_encode(const char *str, size_t len)
106 gchar *buf = g_base64_encode((const guchar *)str, len);
107 char *dst;
109 if (!buf)
110 return NULL;
112 dst = strdup((char *)buf);
113 memset(buf, 0, strlen((char *)buf));
114 g_free(buf);
115 return dst;
118 static int parse_protocol(char *data, size_t total, char **result)
120 char *p;
121 int i;
124 * Set the pointer to the end of the buffer.
126 p = data + total - 1;
128 if (*p != '\n' || total < 3)
129 return -2;
131 p--;
134 * "Rewind" the pointer to the second to last newline character.
136 for (i = total - 1; i; i--, p--) {
137 if (*p == '\n')
138 break;
141 p++;
143 if (strncmp(p, "ERR ", 4) == 0) {
144 *result = strdup(p);
145 return PWMD_PERROR;
148 if (strncmp(p, "OK ", 3) == 0) {
149 //p--;
150 *p = 0;
151 p = data;
153 if (strncmp(p, "BEGIN ", 6) == 0) {
154 p += 6;
156 while (*p && isdigit(*p))
157 p++;
159 if (*p)
160 p++;
163 *result = strdup(p);
164 return PWMD_OK;
167 return -2;
170 static int get_daemon_result(pwm_t *pwm, char **result)
172 fd_set fds;
173 int n;
174 char *data = NULL;
175 size_t total = 0;
177 while (1) {
178 FD_ZERO(&fds);
179 FD_SET(pwm->fd, &fds);
180 n = select(pwm->fd + 1, &fds, NULL, NULL, NULL);
182 if (n == -1)
183 return PWMD_ERROR;
185 if (FD_ISSET(pwm->fd, &fds)) {
186 char buf[4096];
187 ssize_t len = recv(pwm->fd, buf, sizeof(buf), 0);
189 if (len == 0) {
190 if (data) {
191 memset(data, 0, total);
192 free(data);
195 return PWMD_OK;
198 if (len == -1) {
199 if (data) {
200 memset(data, 0, total);
201 free(data);
204 return PWMD_ERROR;
208 * Keep appending to data until a valid protocol code is found.
210 data = (char *)realloc(data, len + total + 1);
211 memcpy(&(data[total]), buf, len);
212 total += len;
213 data[total] = 0;
215 switch (parse_protocol(data, total, result)) {
216 case -2:
217 continue;
218 case PWMD_ERROR:
219 memset(data, 0, total);
220 free(data);
221 return PWMD_ERROR;
222 case PWMD_PERROR:
223 memset(data, 0, total);
224 free(data);
225 return PWMD_PERROR;
226 default:
227 break;
230 break;
234 memset(data, 0, total);
235 free(data);
236 return PWMD_OK;
239 pwm_t *pwmd_connect(const char *path, int *error)
241 int fd;
242 struct sockaddr_un addr;
243 pwm_t *pwm = NULL;
244 char *result = NULL;
245 char cwd[PATH_MAX];
246 char *socketdir = NULL, *socketname = NULL, *socketarg;
247 char *p = NULL;
248 time_t now;
249 struct passwd *pw;
251 if (!path) {
252 pw = getpwuid(getuid());
253 snprintf(cwd, sizeof(cwd), "%s/.pwmd/socket", pw->pw_dir);
254 socketarg = strdup(cwd);
256 else
257 socketarg = strdup(path);
259 if (getcwd(cwd, sizeof(cwd)) == NULL) {
260 free(socketarg);
261 *error = errno;
262 return NULL;
265 if (strchr(socketarg, '/') == NULL) {
266 socketdir = strdup(cwd);
267 p = strdup(socketarg);
268 socketname = p;
270 else {
271 p = strdup(strrchr(socketarg, '/'));
272 socketname = p + 1;
273 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
274 socketdir = strdup(socketarg);
277 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
278 *error = errno;
279 free(socketdir);
280 free(socketarg);
281 free(p);
282 return NULL;
285 if (chdir(socketdir) == -1) {
286 close(fd);
287 *error = errno;
288 free(socketdir);
289 free(socketarg);
290 free(p);
291 return NULL;
294 addr.sun_family = AF_UNIX;
295 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
297 if (connect(fd, (struct sockaddr *)&addr,
298 sizeof(struct sockaddr)) == -1) {
299 close(fd);
300 *error = errno;
301 free(socketdir);
302 free(p);
303 free(socketarg);
304 return NULL;
307 free(socketdir);
308 free(socketarg);
309 free(p);
311 if ((pwm = (pwm_t *)calloc(1, sizeof(pwm_t))) == NULL) {
312 close(fd);
313 *error = errno;
314 return NULL;
317 pwm->fd = fd;
318 time(&now);
319 srandom(now);
322 * Clear the initial OK response
324 get_daemon_result(pwm, &result);
326 if (result)
327 free(result);
328 return pwm;
331 void pwmd_close(pwm_t *pwm)
333 if (!pwm || pwm->fd < 0)
334 return;
336 shutdown(pwm->fd, SHUT_RDWR);
337 close(pwm->fd);
339 if (pwm->password)
340 free(pwm->password);
342 if (pwm->title)
343 free(pwm->title);
345 if (pwm->desc)
346 free(pwm->desc);
348 if (pwm->prompt)
349 free(pwm->prompt);
351 free(pwm);
354 static void secure_free(void *p, size_t len)
356 memset(p, 0, len);
357 free(p);
360 static int send_to_daemon(pwm_t *pwm, char *fmt, ...)
362 va_list ap;
363 char *buf;
364 fd_set fds;
365 int n;
366 size_t bufsize = 0;
367 int ret = 1;
369 va_start(ap, fmt);
370 bufsize = vasprintf(&buf, fmt, ap);
371 va_end(ap);
372 bufsize++;
373 FD_ZERO(&fds);
374 FD_SET(pwm->fd, &fds);
375 n = select(pwm->fd + 1, NULL, &fds, NULL, NULL);
377 if (n == -1) {
378 ret = 1;
379 goto done;
382 if (FD_ISSET(pwm->fd, &fds)) {
383 size_t t = strlen(buf);
384 ssize_t len = send(pwm->fd, buf, t, 0);
386 if (len == -1) {
387 ret = 1;
388 goto done;
391 if (len == t)
392 ret = 0;
395 done:
396 secure_free(buf, bufsize);
397 return ret;
400 static char **parse_list_command(char *str)
402 char **buf = NULL;
403 char *p;
404 int n = 0;
406 while ((p = strsep(&str, "\n")) != NULL) {
407 if (!*p)
408 continue;
410 buf = (char **)realloc(buf, (n + 2) * sizeof(char *));
411 buf[n++] = strdup(p);
412 buf[n] = NULL;
415 return buf;
418 static void init_cache_id(pwm_t *pwm)
420 char t[16];
421 long n = random();
423 snprintf(t, sizeof(t), "%li", n);
425 if (strncmp(t, (char *)pwm->cache_id, sizeof(t)) == 0)
426 init_cache_id(pwm);
428 strncpy((char *)pwm->cache_id, t, sizeof(pwm->cache_id));
431 static gpg_error_t connect_to_agent(pwm_t *pwm)
433 char *env;
434 char *p;
435 pid_t pid;
436 int rc;
437 char path[PATH_MAX], *t;
438 assuan_context_t ctx;
440 if ((env = getenv("GPG_AGENT_INFO")) == NULL)
441 return 1;
443 env = strdup(env);
445 for (p = env, t = path; *p; p++) {
446 if (*p == ':') {
447 *t = 0;
448 p++;
449 pid = atoi(p);
450 break;
453 *t++ = *p;
456 free(env);
457 rc = assuan_socket_connect(&ctx, path, pid);
458 pwm->ctx = ctx;
460 if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED)
461 return 1;
463 init_cache_id(pwm);
465 if (!pwm->title)
466 pwm->title = strdup("LibPWMD");
468 if (!pwm->prompt)
469 pwm->prompt = strdup("Password:");
471 if (!pwm->desc)
472 pwm->desc = strdup("Enter a password.");
474 return send_pinentry_environment ((assuan_context_t)pwm->ctx, GPG_ERR_SOURCE_DEFAULT,
475 NULL, NULL, NULL, NULL, NULL);
478 /* From GnuPG (g10/call-agent.c) */
479 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
480 escaping. Note that the provided buffer needs to be 3 times the
481 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
482 static char *
483 percent_plus_escape (char *p, const char *atext)
485 const unsigned char *s;
487 for (s=(const unsigned char *)atext; *s; s++)
489 if (*s < ' ' || *s == '+')
491 sprintf (p, "%%%02X", *s);
492 p += 3;
494 else if (*s == ' ')
495 *p++ = '+';
496 else
497 *p++ = *s;
499 *p = 0;
500 return p;
503 typedef struct {
504 size_t len;
505 void *buf;
506 } membuf_t;
508 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
510 membuf_t *mem = (membuf_t *)data;
511 void *p;
513 if (!buffer)
514 return 0;
516 if ((p = realloc(mem->buf, mem->len + len)) == NULL)
517 return 1;
519 mem->buf = p;
520 memcpy((char *)mem->buf + mem->len, buffer, len);
521 mem->len += len;
522 return 0;
526 static int get_agent_password(pwm_t *pwm, char **password)
528 char cmd[] = "GET_PASSPHRASE --data -- ";
529 char *line;
530 char *p;
531 membuf_t data;
532 int rc;
534 if (!pwm->ctx) {
535 if (connect_to_agent(pwm))
536 return 1;
539 line = (char *)malloc(strlen(cmd) + 1
540 + (3 * sizeof(pwm->cache_id) + 1)
541 + (3 * strlen(pwm->title) + 1)
542 + (3 * strlen(pwm->desc) + 1)
543 + (3 * strlen(pwm->prompt) + 1) + 1);
544 p = stpcpy(line, cmd);
545 p = percent_plus_escape(p, pwm->cache_id);
546 *p++ = ' ';
547 p = percent_plus_escape(p, pwm->desc);
548 *p++ = ' ';
549 p = percent_plus_escape(p, pwm->prompt);
550 *p++ = ' ';
551 p = percent_plus_escape(p, pwm->title);
552 data.len = 0;
553 data.buf = NULL;
554 rc = assuan_transact((assuan_context_t)pwm->ctx, line, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
556 if (rc) {
557 if (data.buf) {
558 memset(data.buf, 0, data.len);
559 free(data.buf);
562 else {
563 mem_realloc_cb(&data, "", 1);
564 *password = (char *)data.buf;
567 free(line);
568 assuan_disconnect((assuan_context_t)pwm->ctx);
569 pwm->ctx = NULL;
570 return 0;
573 static int clear_agent_password(pwm_t *pwm)
575 char *buf = "CLEAR_PASSPHRASE";
576 char *line;
577 size_t len = strlen(buf) + sizeof(pwm->cache_id) + 2;
578 int rc;
580 if (pwm->ctx) {
581 if ((line = (char *)malloc(len)) == NULL)
582 return 1;
584 sprintf(line, "%s %s", buf, pwm->cache_id);
585 rc = assuan_transact((assuan_context_t)pwm->ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
586 free(line);
588 if (rc)
589 return 1;
592 return 0;
595 int pwmd_command(pwm_t *pwm, void **result, int *error, pwmd_cmd cmd, ...)
597 va_list ap;
598 char *res = NULL;
599 int n;
600 char *filename = NULL;
601 char *p = NULL;
602 char *password = NULL;
603 void *tresult = NULL;
604 int terror = 0;
605 char *arg1, *arg2, *arg3;
607 if (!pwm)
608 return PWMD_ERROR;
610 *result = NULL;
611 *error = EPWMD_ERROR;
612 va_start(ap, cmd);
614 switch (cmd) {
615 case PWMD_OPEN:
616 filename = va_arg(ap, char *);
619 * Avoid calling gpg-agent if the password is cached on the
620 * server.
622 n = pwmd_command(pwm, &tresult, &terror, PWMD_CACHE,
623 PWMD_CACHE_ISCACHED, filename);
625 switch (n) {
626 case PWMD_ERROR:
627 *error = terror;
628 return PWMD_ERROR;
629 case PWMD_PERROR:
630 if (terror == EPWMD_CACHE_NOT_FOUND) {
632 * Get the password from gpg-agent/pinentry.
634 if (pwm->use_agent) {
635 if (get_agent_password(pwm, &password)) {
636 *error = EPWMD_ERROR;
637 return PWMD_AGENT_ERROR;
640 if (!password) {
641 *error = EPWMD_KEY;
642 return PWMD_PERROR;
645 p = strdup(password);
646 secure_free(password, strlen(password));
647 password = p;
650 * We use pwmd's cache from now on.
652 clear_agent_password(pwm);
653 break;
655 else {
657 * Not using gpg-agent and the file was not found
658 * in the cache.
660 if (pwm->password == NULL) {
661 *error = EPWMD_KEY;
662 return PWMD_PERROR;
665 password = strdup(pwm->password);
668 else if (terror != EPWMD_FILE_NOT_FOUND) {
669 *error = terror;
670 return PWMD_PERROR;
672 break;
673 case PWMD_OK:
674 break;
677 if (send_to_daemon(pwm, "OPEN %s %s\n", filename, (password) ? password : "")) {
678 if (password)
679 secure_free(password, strlen(password));
680 *error = errno;
681 return PWMD_ERROR;
684 if (password)
685 secure_free(password, strlen(password));
686 break;
687 case PWMD_LIST:
688 if (send_to_daemon(pwm, "LIST\n")) {
689 *error = errno;
690 return PWMD_ERROR;
692 break;
693 case PWMD_LIST_ACCOUNT:
694 p = va_arg(ap, char *);
696 if (send_to_daemon(pwm, "LIST %s\n", p)) {
697 *error = errno;
698 return PWMD_ERROR;
700 break;
701 case PWMD_GET:
702 arg1 = va_arg(ap, char *);
704 if (send_to_daemon(pwm, "GET %s\n", arg1)) {
705 *error = errno;
706 return PWMD_ERROR;
708 break;
709 case PWMD_STORE:
710 arg1 = va_arg(ap, char *);
712 if (send_to_daemon(pwm, "STORE %s\n", arg1)) {
713 *error = errno;
714 return PWMD_ERROR;
716 break;
717 case PWMD_DELETE:
718 arg1 = va_arg(ap, char *);
720 if (send_to_daemon(pwm, "DELETE %s\n", arg1)) {
721 *error = errno;
722 return PWMD_ERROR;
724 break;
725 case PWMD_ATTR_SET:
726 arg1 = va_arg(ap, char *);
727 arg2 = va_arg(ap, char *);
728 arg3 = va_arg(ap, char *);
730 if (send_to_daemon(pwm, "ATTR SET %s %s %s\n", arg1, arg2, arg3)) {
731 *error = errno;
732 return PWMD_ERROR;
734 break;
735 case PWMD_ATTR_GET:
736 arg1 = va_arg(ap, char *);
737 arg2 = va_arg(ap, char *);
739 if (send_to_daemon(pwm, "ATTR GET %s %s\n", arg1, arg2)) {
740 *error = errno;
741 return PWMD_ERROR;
743 break;
744 case PWMD_ATTR_DELETE:
745 arg1 = va_arg(ap, char *);
746 arg2 = va_arg(ap, char *);
748 if (send_to_daemon(pwm, "ATTR DELETE %s %s\n", arg1, arg2)) {
749 *error = errno;
750 return PWMD_ERROR;
752 break;
753 case PWMD_ATTR_LIST:
754 arg1 = va_arg(ap, char *);
756 if (send_to_daemon(pwm, "ATTR LIST %s\n", arg1)) {
757 *error = errno;
758 return PWMD_ERROR;
760 break;
761 case PWMD_CACHE:
762 n = va_arg(ap, int);
764 switch (n) {
765 case PWMD_CACHE_ISCACHED:
766 p = va_arg(ap, char *);
768 if (send_to_daemon(pwm, "CACHE ISCACHED %s\n", p)) {
769 *error = errno;
770 return PWMD_ERROR;
772 break;
773 case PWMD_CACHE_CLEAR:
774 arg1 = va_arg(ap, char *);
776 if (send_to_daemon(pwm, "CACHE CLEAR %s\n", arg1)) {
777 *error = errno;
778 return PWMD_ERROR;
780 break;
781 case PWMD_CACHE_CLEARALL:
782 if (send_to_daemon(pwm, "CACHE CLEARALL\n")) {
783 *error = errno;
784 return PWMD_ERROR;
786 break;
788 break;
789 case PWMD_SETOPT:
790 n = va_arg(ap, int);
792 switch (n) {
793 case PWMD_OPTION_USEAGENT:
794 n = va_arg(ap, int);
796 if (n != 0 && n != 1)
797 return PWMD_ERROR;
799 pwm->use_agent = n;
800 break;
801 case PWMD_OPTION_PASSWORD:
802 arg1 = va_arg(ap, char *);
804 if (pwm->password) {
805 memset(pwm->password, 0, strlen(pwm->password));
806 free(pwm->password);
809 pwm->password = strdup(arg1);
810 break;
811 case PWMD_OPTION_TITLE:
812 if (pwm->title)
813 free(pwm->title);
814 pwm->title = strdup(va_arg(ap, char *));
815 break;
816 case PWMD_OPTION_PROMPT:
817 if (pwm->prompt)
818 free(pwm->prompt);
819 pwm->prompt = strdup(va_arg(ap, char *));
820 break;
821 case PWMD_OPTION_DESC:
822 if (pwm->desc)
823 free(pwm->desc);
824 pwm->desc = strdup(va_arg(ap, char *));
825 break;
826 default:
827 *error = 0;
828 return PWMD_ERROR;
831 return PWMD_OK;
832 case PWMD_SAVE:
833 if (pwm->use_agent) {
834 if (get_agent_password(pwm, &password)) {
835 *error = 0;
836 return PWMD_AGENT_ERROR;
839 if (!password || !*password) {
840 *error = EPWMD_KEY;
841 return PWMD_PERROR;
844 p = strdup(password);
845 secure_free(password, strlen(password));
846 password = p;
849 * We use pwmd's cache from now on.
851 clear_agent_password(pwm);
853 else
854 password = pwm->password;
856 if (send_to_daemon(pwm, "SAVE %s\n", password)) {
857 if (password)
858 secure_free(password, strlen(password));
860 *error = errno;
861 return PWMD_ERROR;
864 if (password)
865 secure_free(password, strlen(password));
867 pwm->password = NULL;
868 break;
869 case PWMD_DUMP:
870 if (send_to_daemon(pwm, "DUMP\n")) {
871 *error = errno;
872 return PWMD_ERROR;
874 break;
875 case PWMD_QUIT:
876 if (send_to_daemon(pwm, "QUIT\n")) {
877 *error = errno;
878 return PWMD_ERROR;
880 break;
881 default:
882 return PWMD_ERROR;
885 va_end(ap);
886 *result = NULL;
887 n = get_daemon_result(pwm, &res);
889 switch (n) {
890 case PWMD_PERROR:
891 p = res + 4;
892 *error = atoi(p);
893 free(res);
894 return PWMD_PERROR;
895 case PWMD_ERROR:
896 *error = errno;
897 free(res);
898 return PWMD_ERROR;
899 case PWMD_OK:
900 switch (cmd) {
901 case PWMD_ATTR_LIST:
902 case PWMD_LIST:
903 case PWMD_LIST_ACCOUNT:
904 *result = parse_list_command(res);
905 memset(res, 0, strlen(res));
906 free(res);
907 break;
908 case PWMD_ATTR_GET:
909 case PWMD_DUMP:
910 case PWMD_GET:
911 if (res[strlen(res) - 1] == '\n')
912 res[strlen(res) - 1] = 0;
914 *result = res;
915 break;
916 default:
917 if (res) {
918 memset(res, 0, strlen(res));
919 free(res);
921 break;
923 default:
924 break;
927 return PWMD_OK;