Fix for compiling the library with g++.
[pwmd.git] / libpwmd / libpwmd.c
blob94c9107690c512337e9c87445f289df6ef2a7055
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 unsigned char *pwmd_base64_decode(const char *str, size_t *size)
84 gsize len;
85 guchar *buf = g_base64_decode(str, &len);
87 *size = len;
88 return buf;
91 char *pwmd_base64_encode(const char *str, size_t len)
93 gchar *buf = g_base64_encode((const guchar *)str, len);
94 char *dst;
96 if (!buf)
97 return NULL;
99 dst = strdup((char *)buf);
100 memset(buf, 0, strlen((char *)buf));
101 g_free(buf);
102 return dst;
105 static int parse_protocol(char *data, char **result)
107 char *p;
108 int i;
109 size_t total = strlen(data);
112 * Set the pointer to the end of the buffer.
114 p = data + strlen(data) - 1;
116 if (*p != '\n' || total < 3)
117 return -2;
119 p--;
122 * "Rewind" the pointer to the second to last newline character.
124 for (i = total - 1; i; i--, p--) {
125 if (*p == '\n')
126 break;
129 p++;
131 if (strncmp(p, "ERR ", 4) == 0) {
132 *result = strdup(p);
133 return PWMD_PERROR;
136 if (strncmp(p, "OK ", 3) == 0) {
137 p--;
138 *p = 0;
139 p = data;
141 if (strncmp(p, "BEGIN ", 6) == 0) {
142 p += 6;
144 while (*p && isdigit(*p))
145 p++;
147 if (*p)
148 p++;
151 *result = strdup(p);
152 return PWMD_OK;
155 return -2;
158 static int get_daemon_result(pwm_t *pwm, char **result)
160 fd_set fds;
161 int n;
162 char *data = NULL;
163 size_t total = 0;
165 while (1) {
166 FD_ZERO(&fds);
167 FD_SET(pwm->fd, &fds);
168 n = select(pwm->fd + 1, &fds, NULL, NULL, NULL);
170 if (n == -1)
171 return PWMD_ERROR;
173 if (FD_ISSET(pwm->fd, &fds)) {
174 char buf[4096];
175 ssize_t len = recv(pwm->fd, buf, sizeof(buf), 0);
177 if (len == 0) {
178 if (data)
179 free(data);
181 return PWMD_OK;
184 if (len == -1) {
185 if (data) {
186 memset(data, 0, total);
187 free(data);
190 return PWMD_ERROR;
194 * Keep appending to data until a valid protocol code is found.
196 data = (char *)realloc(data, len + total + 1);
197 memcpy(&(data[total]), buf, len);
198 total += len;
199 data[total] = 0;
201 switch (parse_protocol(data, result)) {
202 case -2:
203 continue;
204 case PWMD_ERROR:
205 free(data);
206 return PWMD_ERROR;
207 case PWMD_PERROR:
208 memset(data, 0, total);
209 free(data);
210 return PWMD_PERROR;
211 default:
212 break;
215 break;
219 memset(data, 0, total);
220 free(data);
221 return PWMD_OK;
224 pwm_t *pwmd_connect(const char *path, int *error)
226 int fd;
227 struct sockaddr_un addr;
228 pwm_t *pwm = NULL;
229 char *result = NULL;
230 char cwd[PATH_MAX];
231 char *socketdir = NULL, *socketname = NULL, *socketarg;
232 char *p = NULL;
233 time_t now;
234 struct passwd *pw;
236 if (!path) {
237 pw = getpwuid(getuid());
238 snprintf(cwd, sizeof(cwd), "%s/.pwmd/socket", pw->pw_dir);
239 socketarg = strdup(cwd);
241 else
242 socketarg = strdup(path);
244 if (getcwd(cwd, sizeof(cwd)) == NULL) {
245 free(socketarg);
246 *error = errno;
247 return NULL;
250 if (strchr(socketarg, '/') == NULL) {
251 socketdir = strdup(cwd);
252 p = strdup(socketarg);
253 socketname = p;
255 else {
256 p = strdup(strrchr(socketarg, '/'));
257 socketname = p + 1;
258 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
259 socketdir = strdup(socketarg);
262 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
263 *error = errno;
264 free(socketdir);
265 free(socketarg);
266 free(p);
267 return NULL;
270 if (chdir(socketdir) == -1) {
271 close(fd);
272 *error = errno;
273 free(socketdir);
274 free(socketarg);
275 free(p);
276 return NULL;
279 addr.sun_family = AF_UNIX;
280 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
282 if (connect(fd, (struct sockaddr *)&addr,
283 sizeof(struct sockaddr)) == -1) {
284 close(fd);
285 *error = errno;
286 free(socketdir);
287 free(p);
288 free(socketarg);
289 return NULL;
292 free(socketdir);
293 free(socketarg);
294 free(p);
296 if ((pwm = (pwm_t *)calloc(1, sizeof(pwm_t))) == NULL) {
297 close(fd);
298 *error = errno;
299 return NULL;
302 pwm->fd = fd;
303 time(&now);
304 srandom(now);
307 * Clear the initial OK response
309 get_daemon_result(pwm, &result);
310 return pwm;
313 void pwmd_close(pwm_t *pwm)
315 if (!pwm || pwm->fd < 0)
316 return;
318 shutdown(pwm->fd, SHUT_RDWR);
319 close(pwm->fd);
320 free(pwm);
323 static void secure_free(void *p, size_t len)
325 memset(p, 0, len);
326 free(p);
329 static int send_to_daemon(pwm_t *pwm, char *fmt, ...)
331 va_list ap;
332 char *buf;
333 fd_set fds;
334 int n;
335 size_t bufsize = 0;
336 int ret = 1;
338 va_start(ap, fmt);
339 bufsize = vasprintf(&buf, fmt, ap);
340 va_end(ap);
341 bufsize++;
342 FD_ZERO(&fds);
343 FD_SET(pwm->fd, &fds);
344 n = select(pwm->fd + 1, NULL, &fds, NULL, NULL);
346 if (n == -1) {
347 ret = 1;
348 goto done;
351 if (FD_ISSET(pwm->fd, &fds)) {
352 size_t t = strlen(buf);
353 ssize_t len = send(pwm->fd, buf, t, 0);
355 if (len == -1) {
356 ret = 1;
357 goto done;
360 if (len == t)
361 ret = 0;
364 done:
365 secure_free(buf, bufsize);
366 return ret;
369 static char **parse_list_command(char *str)
371 char **buf = NULL;
372 char *p;
373 int n = 0;
375 while ((p = strsep(&str, "\n")) != NULL) {
376 buf = (char **)realloc(buf, (n + 2) * sizeof(char *));
377 buf[n++] = strdup(p);
378 buf[n] = NULL;
381 return buf;
384 static void init_cache_id(pwm_t *pwm)
386 char t[16];
387 long n = random();
389 snprintf(t, sizeof(t), "%li", n);
391 if (strncmp(t, (char *)pwm->cache_id, sizeof(t)) == 0)
392 init_cache_id(pwm);
394 strncpy((char *)pwm->cache_id, t, sizeof(pwm->cache_id));
397 static gpg_error_t connect_to_agent(pwm_t *pwm)
399 char *env;
400 char *p;
401 pid_t pid;
402 int rc;
403 char path[PATH_MAX], *t;
404 assuan_context_t ctx;
406 if ((env = getenv("GPG_AGENT_INFO")) == NULL)
407 return 1;
409 env = strdup(env);
411 for (p = env, t = path; *p; p++) {
412 if (*p == ':') {
413 *t = 0;
414 p++;
415 pid = atoi(p);
416 break;
419 *t++ = *p;
422 free(env);
423 rc = assuan_socket_connect(&ctx, path, pid);
424 pwm->ctx = ctx;
426 if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED)
427 return 1;
429 init_cache_id(pwm);
431 if (!pwm->title)
432 pwm->title = strdup("LibPWMD");
434 if (!pwm->prompt)
435 pwm->prompt = strdup("Password:");
437 if (!pwm->desc)
438 pwm->desc = strdup("Enter a password.");
440 return send_pinentry_environment ((assuan_context_t)pwm->ctx, GPG_ERR_SOURCE_DEFAULT,
441 NULL, NULL, NULL, NULL, NULL);
444 /* From GnuPG (g10/call-agent.c) */
445 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
446 escaping. Note that the provided buffer needs to be 3 times the
447 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
448 static char *
449 percent_plus_escape (char *p, const char *atext)
451 const unsigned char *s;
453 for (s=(const unsigned char *)atext; *s; s++)
455 if (*s < ' ' || *s == '+')
457 sprintf (p, "%%%02X", *s);
458 p += 3;
460 else if (*s == ' ')
461 *p++ = '+';
462 else
463 *p++ = *s;
465 *p = 0;
466 return p;
469 typedef struct {
470 size_t len;
471 void *buf;
472 } membuf_t;
474 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
476 membuf_t *mem = (membuf_t *)data;
477 void *p;
479 if (!buffer)
480 return 0;
482 if ((p = realloc(mem->buf, mem->len + len)) == NULL)
483 return 1;
485 mem->buf = p;
486 memcpy((char *)mem->buf + mem->len, buffer, len);
487 mem->len += len;
488 return 0;
492 static int get_agent_password(pwm_t *pwm, char **password)
494 char cmd[] = "GET_PASSPHRASE --data -- ";
495 char *line;
496 char *p;
497 membuf_t data;
498 int rc;
500 if (!pwm->ctx) {
501 if (connect_to_agent(pwm))
502 return 1;
505 line = (char *)malloc(strlen(cmd) + 1
506 + (3 * sizeof(pwm->cache_id) + 1)
507 + (3 * strlen(pwm->title) + 1)
508 + (3 * strlen(pwm->desc) + 1)
509 + (3 * strlen(pwm->prompt) + 1) + 1);
510 p = stpcpy(line, cmd);
511 p = percent_plus_escape(p, pwm->cache_id);
512 *p++ = ' ';
513 p = percent_plus_escape(p, pwm->desc);
514 *p++ = ' ';
515 p = percent_plus_escape(p, pwm->prompt);
516 *p++ = ' ';
517 p = percent_plus_escape(p, pwm->title);
518 data.len = 0;
519 data.buf = NULL;
520 rc = assuan_transact((assuan_context_t)pwm->ctx, line, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
522 if (rc) {
523 if (data.buf) {
524 memset(data.buf, 0, data.len);
525 free(data.buf);
528 else {
529 mem_realloc_cb(&data, "", 1);
530 *password = (char *)data.buf;
533 free(line);
534 return 0;
537 static int clear_agent_password(pwm_t *pwm)
539 char *buf = "CLEAR_PASSPHRASE";
540 char *line;
541 size_t len = strlen(buf) + sizeof(pwm->cache_id) + 2;
542 int rc;
544 if (pwm->ctx) {
545 if ((line = (char *)malloc(len)) == NULL)
546 return 1;
548 sprintf(line, "%s %s", buf, pwm->cache_id);
549 rc = assuan_transact((assuan_context_t)pwm->ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
550 free(line);
552 if (rc)
553 return 1;
556 return 0;
559 int pwmd_command(pwm_t *pwm, void **result, int *error, pwmd_cmd cmd, ...)
561 va_list ap;
562 char *res = NULL;
563 int n;
564 char *filename = NULL;
565 char *p = NULL;
566 char *password = NULL;
567 void *tresult = NULL;
568 int terror = 0;
569 char *arg1, *arg2, *arg3;
571 if (!pwm)
572 return PWMD_ERROR;
574 *result = NULL;
575 *error = EPWMD_ERROR;
576 va_start(ap, cmd);
578 switch (cmd) {
579 case PWMD_OPEN:
580 filename = va_arg(ap, char *);
583 * Avoid calling gpg-agent if the password is cached on the
584 * server.
586 n = pwmd_command(pwm, &tresult, &terror, PWMD_CACHE,
587 PWMD_CACHE_ISCACHED, filename);
589 switch (n) {
590 case PWMD_ERROR:
591 *error = terror;
592 return PWMD_ERROR;
593 case PWMD_PERROR:
594 if (terror == EPWMD_CACHE_NOT_FOUND) {
596 * Get the password from gpg-agent/pinentry.
598 if (pwm->use_agent) {
599 if (get_agent_password(pwm, &password)) {
600 *error = EPWMD_ERROR;
601 return PWMD_AGENT_ERROR;
604 if (!password) {
605 *error = EPWMD_KEY;
606 return PWMD_PERROR;
609 p = strdup(password);
610 secure_free(password, strlen(password));
611 password = p;
614 * We use pwmd's cache from now on.
616 clear_agent_password(pwm);
617 break;
619 else {
621 * Not using gpg-agent and the file was not found
622 * in the cache.
624 if (pwm->password == NULL) {
625 *error = EPWMD_KEY;
626 return PWMD_PERROR;
629 password = strdup(pwm->password);
632 else if (terror != EPWMD_FILE_NOT_FOUND) {
633 *error = terror;
634 return PWMD_PERROR;
636 break;
637 case PWMD_OK:
638 break;
641 if (send_to_daemon(pwm, "OPEN %s %s\n", filename, (password) ? password : "")) {
642 if (password)
643 secure_free(password, strlen(password));
644 *error = errno;
645 return PWMD_ERROR;
648 if (password)
649 secure_free(password, strlen(password));
650 break;
651 case PWMD_LIST:
652 if (send_to_daemon(pwm, "LIST\n")) {
653 *error = errno;
654 return PWMD_ERROR;
656 break;
657 case PWMD_LIST_ACCOUNT:
658 p = va_arg(ap, char *);
660 if (send_to_daemon(pwm, "LIST %s\n", p)) {
661 *error = errno;
662 return PWMD_ERROR;
664 break;
665 case PWMD_GET:
666 arg1 = va_arg(ap, char *);
668 if (send_to_daemon(pwm, "GET %s\n", arg1)) {
669 *error = errno;
670 return PWMD_ERROR;
672 break;
673 case PWMD_STORE:
674 arg1 = va_arg(ap, char *);
676 if (send_to_daemon(pwm, "STORE %s\n", arg1)) {
677 *error = errno;
678 return PWMD_ERROR;
680 break;
681 case PWMD_DELETE:
682 arg1 = va_arg(ap, char *);
684 if (send_to_daemon(pwm, "DELETE %s\n", arg1)) {
685 *error = errno;
686 return PWMD_ERROR;
688 break;
689 case PWMD_ATTR_SET:
690 arg1 = va_arg(ap, char *);
691 arg2 = va_arg(ap, char *);
692 arg3 = va_arg(ap, char *);
694 if (send_to_daemon(pwm, "ATTR SET %s %s %s\n", arg1, arg2, arg3)) {
695 *error = errno;
696 return PWMD_ERROR;
698 break;
699 case PWMD_ATTR_GET:
700 arg1 = va_arg(ap, char *);
701 arg2 = va_arg(ap, char *);
703 if (send_to_daemon(pwm, "ATTR GET %s %s\n", arg1, arg2)) {
704 *error = errno;
705 return PWMD_ERROR;
707 break;
708 case PWMD_ATTR_DELETE:
709 arg1 = va_arg(ap, char *);
710 arg2 = va_arg(ap, char *);
712 if (send_to_daemon(pwm, "ATTR DELETE %s %s\n", arg1, arg2)) {
713 *error = errno;
714 return PWMD_ERROR;
716 break;
717 case PWMD_ATTR_LIST:
718 arg1 = va_arg(ap, char *);
720 if (send_to_daemon(pwm, "ATTR LIST %s\n", arg1)) {
721 *error = errno;
722 return PWMD_ERROR;
724 break;
725 case PWMD_CACHE:
726 n = va_arg(ap, int);
728 switch (n) {
729 case PWMD_CACHE_ISCACHED:
730 p = va_arg(ap, char *);
732 if (send_to_daemon(pwm, "CACHE ISCACHED %s\n", p)) {
733 *error = errno;
734 return PWMD_ERROR;
736 break;
737 case PWMD_CACHE_CLEAR:
738 arg1 = va_arg(ap, char *);
740 if (send_to_daemon(pwm, "CACHE CLEAR %s\n", arg1)) {
741 *error = errno;
742 return PWMD_ERROR;
744 break;
745 case PWMD_CACHE_CLEARALL:
746 if (send_to_daemon(pwm, "CACHE CLEARALL\n")) {
747 *error = errno;
748 return PWMD_ERROR;
750 break;
752 break;
753 case PWMD_SETOPT:
754 n = va_arg(ap, int);
756 switch (n) {
757 case PWMD_OPTION_USEAGENT:
758 n = va_arg(ap, int);
760 if (n != 0 && n != 1)
761 return PWMD_ERROR;
763 pwm->use_agent = n;
764 break;
765 case PWMD_OPTION_PASSWORD:
766 arg1 = va_arg(ap, char *);
768 if (pwm->password) {
769 memset(pwm->password, 0, strlen(pwm->password));
770 free(pwm->password);
773 pwm->password = strdup(arg1);
774 break;
775 case PWMD_OPTION_TITLE:
776 if (pwm->title)
777 free(pwm->title);
778 pwm->title = strdup(va_arg(ap, char *));
779 break;
780 case PWMD_OPTION_PROMPT:
781 if (pwm->prompt)
782 free(pwm->prompt);
783 pwm->prompt = strdup(va_arg(ap, char *));
784 break;
785 case PWMD_OPTION_DESC:
786 if (pwm->desc)
787 free(pwm->desc);
788 pwm->desc = strdup(va_arg(ap, char *));
789 break;
790 default:
791 *error = 0;
792 return PWMD_ERROR;
795 return PWMD_OK;
796 case PWMD_SAVE:
797 if (pwm->use_agent) {
798 if (get_agent_password(pwm, &password)) {
799 *error = 0;
800 return PWMD_AGENT_ERROR;
803 if (!password || !*password) {
804 *error = EPWMD_KEY;
805 return PWMD_PERROR;
808 p = strdup(password);
809 secure_free(password, strlen(password));
810 password = p;
813 * We use pwmd's cache from now on.
815 clear_agent_password(pwm);
817 else
818 password = pwm->password;
820 if (send_to_daemon(pwm, "SAVE %s\n", password)) {
821 if (password)
822 secure_free(password, strlen(password));
824 *error = errno;
825 return PWMD_ERROR;
828 if (password)
829 secure_free(password, strlen(password));
830 break;
831 case PWMD_DUMP:
832 if (send_to_daemon(pwm, "DUMP\n")) {
833 *error = errno;
834 return PWMD_ERROR;
836 break;
837 case PWMD_QUIT:
838 if (send_to_daemon(pwm, "QUIT\n")) {
839 *error = errno;
840 return PWMD_ERROR;
842 break;
843 default:
844 return PWMD_ERROR;
847 va_end(ap);
848 *result = NULL;
849 n = get_daemon_result(pwm, &res);
851 switch (n) {
852 case PWMD_PERROR:
853 p = res + 4;
854 *error = atoi(p);
855 free(res);
856 return PWMD_PERROR;
857 case PWMD_ERROR:
858 *error = errno;
859 return PWMD_ERROR;
860 case PWMD_OK:
861 switch (cmd) {
862 case PWMD_ATTR_LIST:
863 case PWMD_LIST:
864 case PWMD_LIST_ACCOUNT:
865 *result = parse_list_command(res);
866 free(res);
867 break;
868 case PWMD_ATTR_GET:
869 case PWMD_DUMP:
870 case PWMD_GET:
871 *result = res;
872 break;
873 default:
874 break;
876 default:
877 break;
880 return PWMD_OK;