Return EPWMD_ERROR when in unknown error occurs from pwmd_command().
[pwmd.git] / libpwmd / libpwmd.c
blobdd935315e856c04d43cab3610f4ddec8dc615d9d
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"
50 #include "pwmd_error.h"
52 char *pwmd_base64_decode(const char *str)
54 gsize len;
55 guchar *buf = g_base64_decode(str, &len);
56 char *dst;
58 dst = strdup((char *)buf);
59 memset(buf, 0, len);
60 g_free(buf);
61 return dst;
64 char *pwmd_base64_encode(const char *str, size_t len)
66 gchar *buf = g_base64_encode((const guchar *)str, len);
67 char *dst;
69 if (!buf)
70 return NULL;
72 dst = strdup((char *)buf);
73 memset(buf, 0, strlen((char *)buf));
74 g_free(buf);
75 return dst;
78 static int parse_protocol(char *data, char **result)
80 char *p;
81 int i;
82 size_t total = strlen(data);
85 * Set the pointer to the end of the buffer.
87 p = data + strlen(data) - 1;
89 if (*p != '\n' || total < 3)
90 return -2;
92 p--;
95 * "Rewind" the pointer to the second to last newline character.
97 for (i = total - 1; i; i--, p--) {
98 if (*p == '\n')
99 break;
102 p++;
104 if (strncmp(p, "ERR ", 4) == 0) {
105 *result = strdup(p);
106 return PWMD_PERROR;
109 if (strncmp(p, "OK ", 3) == 0) {
110 p--;
111 *p = 0;
112 p = data;
114 if (strncmp(p, "BEGIN ", 6) == 0) {
115 p += 6;
117 while (*p && isdigit(*p))
118 p++;
120 if (*p)
121 p++;
124 *result = strdup(p);
125 return PWMD_OK;
128 return -2;
131 static int get_daemon_result(pwm_t *pwm, char **result)
133 fd_set fds;
134 int n;
135 char *data = NULL;
136 size_t total = 0;
138 while (1) {
139 FD_ZERO(&fds);
140 FD_SET(pwm->fd, &fds);
141 n = select(pwm->fd + 1, &fds, NULL, NULL, NULL);
143 if (n == -1)
144 return PWMD_ERROR;
146 if (FD_ISSET(pwm->fd, &fds)) {
147 char buf[4096];
148 ssize_t len = recv(pwm->fd, buf, sizeof(buf), 0);
150 if (len == 0) {
151 if (data)
152 free(data);
154 return PWMD_OK;
157 if (len == -1) {
158 if (data) {
159 memset(data, 0, total);
160 free(data);
163 return PWMD_ERROR;
167 * Keep appending to data until a valid protocol code is found.
169 data = realloc(data, len + total + 1);
170 memcpy(&(data[total]), buf, len);
171 total += len;
172 data[total] = 0;
174 switch (parse_protocol(data, result)) {
175 case -2:
176 continue;
177 case PWMD_ERROR:
178 free(data);
179 return PWMD_ERROR;
180 case PWMD_PERROR:
181 memset(data, 0, total);
182 free(data);
183 return PWMD_PERROR;
184 default:
185 break;
188 break;
192 memset(data, 0, total);
193 free(data);
194 return PWMD_OK;
197 pwm_t *pwmd_connect(const char *path, int *error)
199 int fd;
200 struct sockaddr_un addr;
201 pwm_t *pwm = NULL;
202 char *result = NULL;
203 char cwd[PATH_MAX];
204 char *socketdir = NULL, *socketname = NULL, *socketarg;
205 char *p = NULL;
206 time_t now;
208 if (!path) {
209 *error = EPWMD_ERROR;
210 return NULL;
213 if (getcwd(cwd, sizeof(cwd)) == NULL) {
214 *error = errno;
215 return NULL;
218 socketarg = strdup(path);
220 if (strchr(socketarg, '/') == NULL) {
221 socketdir = strdup(cwd);
222 p = strdup(socketarg);
223 socketname = p;
225 else {
226 p = strdup(strrchr(socketarg, '/'));
227 socketname = p + 1;
228 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
229 socketdir = strdup(socketarg);
232 if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
233 *error = errno;
234 free(socketdir);
235 free(socketarg);
236 free(p);
237 return NULL;
240 if (chdir(socketdir) == -1) {
241 close(fd);
242 *error = errno;
243 free(socketdir);
244 free(socketarg);
245 free(p);
246 return NULL;
249 addr.sun_family = AF_UNIX;
250 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
252 if (connect(fd, (struct sockaddr *)&addr,
253 sizeof(struct sockaddr)) == -1) {
254 close(fd);
255 *error = errno;
256 free(socketdir);
257 free(p);
258 free(socketarg);
259 return NULL;
262 free(socketdir);
263 free(socketarg);
264 free(p);
266 if ((pwm = calloc(1, sizeof(pwm_t))) == NULL) {
267 close(fd);
268 *error = errno;
269 return NULL;
272 pwm->fd = fd;
273 time(&now);
274 srandom(now);
277 * Clear the initial OK response
279 get_daemon_result(pwm, &result);
280 return pwm;
283 void pwmd_close(pwm_t *pwm)
285 if (!pwm || pwm->fd < 0)
286 return;
288 shutdown(pwm->fd, SHUT_RDWR);
289 close(pwm->fd);
290 free(pwm);
293 static void secure_free(void *p, size_t len)
295 memset(p, 0, len);
296 free(p);
299 static int send_to_daemon(pwm_t *pwm, char *fmt, ...)
301 va_list ap;
302 char *buf;
303 fd_set fds;
304 int n;
305 size_t sent = 0;
306 size_t bufsize = 0;
308 va_start(ap, fmt);
309 bufsize = vasprintf(&buf, fmt, ap);
310 va_end(ap);
311 bufsize++;
313 while (1) {
314 FD_ZERO(&fds);
315 FD_SET(pwm->fd, &fds);
316 n = select(pwm->fd + 1, NULL, &fds, NULL, NULL);
318 if (n == -1) {
319 secure_free(buf, bufsize);
320 return 1;
323 if (FD_ISSET(pwm->fd, &fds)) {
324 size_t t = strlen(buf);
325 ssize_t len = send(pwm->fd, buf, t, 0);
327 if (len == -1) {
328 secure_free(buf, bufsize);
329 return 1;
332 sent += len;
335 * Keep sending data to the socket until the entire buffer has
336 * been sent.
338 if (sent < t) {
339 memmove(&(buf[0]), buf + len, t - len);
340 buf[t - len] = 0;
341 continue;
344 break;
348 secure_free(buf, bufsize);
349 return 0;
352 static char **parse_list_command(char *str)
354 char **buf = NULL;
355 char *p;
356 int n = 0;
358 while ((p = strsep(&str, "\n")) != NULL) {
359 buf = realloc(buf, (n + 2) * sizeof(char *));
360 buf[n++] = strdup(p);
361 buf[n] = NULL;
364 return buf;
367 static char *attr_to_str(int opt)
369 switch (opt) {
370 case PWMD_ATTR_NAME:
371 return "name";
372 default:
373 return NULL;
376 return NULL;
379 static void init_cache_id(pwm_t *pwm)
381 char t[16];
382 long n = random();
384 snprintf(t, sizeof(t), "%li", n);
386 if (strncmp(t, pwm->cache_id, sizeof(t)) == 0)
387 init_cache_id(pwm);
389 strncpy(pwm->cache_id, t, sizeof(pwm->cache_id));
392 static gpg_error_t connect_to_agent(pwm_t *pwm)
394 char *env;
395 char *p;
396 pid_t pid;
397 int rc;
398 char path[PATH_MAX], *t;
400 if ((env = getenv("GPG_AGENT_INFO")) == NULL)
401 return 1;
403 env = strdup(env);
405 for (p = env, t = path; *p; p++) {
406 if (*p == ':') {
407 *t = 0;
408 p++;
409 pid = atoi(p);
410 break;
413 *t++ = *p;
416 free(env);
417 rc = assuan_socket_connect(&pwm->ctx, path, pid);
419 if (gpg_err_code(rc) == GPG_ERR_ASS_CONNECT_FAILED)
420 return 1;
422 init_cache_id(pwm);
424 if (!pwm->title)
425 pwm->title = strdup("LibPWMD");
427 if (!pwm->prompt)
428 pwm->prompt = strdup("Password:");
430 if (!pwm->desc)
431 pwm->desc = strdup("Enter a password.");
433 return send_pinentry_environment (pwm->ctx, GPG_ERR_SOURCE_DEFAULT,
434 NULL, NULL, NULL, NULL, NULL);
437 /* From GnuPG (g10/call-agent.c) */
438 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
439 escaping. Note that the provided buffer needs to be 3 times the
440 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
441 static char *
442 percent_plus_escape (char *p, const char *atext)
444 const unsigned char *s;
446 for (s=atext; *s; s++)
448 if (*s < ' ' || *s == '+')
450 sprintf (p, "%%%02X", *s);
451 p += 3;
453 else if (*s == ' ')
454 *p++ = '+';
455 else
456 *p++ = *s;
458 *p = 0;
459 return p;
462 typedef struct {
463 size_t len;
464 void *buf;
465 } membuf_t;
467 static int mem_realloc_cb(void *data, const void *buffer, size_t len)
469 membuf_t *mem = data;
470 void *p;
472 if (!buffer)
473 return 0;
475 if ((p = realloc(mem->buf, mem->len + len)) == NULL)
476 return 1;
478 mem->buf = p;
479 memcpy(mem->buf + mem->len, buffer, len);
480 mem->len += len;
481 return 0;
485 static int get_agent_password(pwm_t *pwm, char **password)
487 char cmd[] = "GET_PASSPHRASE --data -- ";
488 char *line;
489 char *p;
490 membuf_t data;
491 int rc;
493 if (!pwm->ctx) {
494 if (connect_to_agent(pwm))
495 return 1;
498 line = malloc(strlen(cmd) + 1
499 + (3 * sizeof(pwm->cache_id) + 1)
500 + (3 * strlen(pwm->title) + 1)
501 + (3 * strlen(pwm->desc) + 1)
502 + (3 * strlen(pwm->prompt) + 1) + 1);
503 p = stpcpy(line, cmd);
504 p = percent_plus_escape(p, pwm->cache_id);
505 *p++ = ' ';
506 p = percent_plus_escape(p, pwm->desc);
507 *p++ = ' ';
508 p = percent_plus_escape(p, pwm->prompt);
509 *p++ = ' ';
510 p = percent_plus_escape(p, pwm->title);
511 data.len = 0;
512 data.buf = NULL;
513 rc = assuan_transact(pwm->ctx, line, mem_realloc_cb, &data, NULL, NULL, NULL, NULL);
515 if (rc) {
516 if (data.buf) {
517 memset(data.buf, 0, data.len);
518 free(data.buf);
521 else {
522 mem_realloc_cb(&data, "", 1);
523 *password = (char *)data.buf;
526 free(line);
527 return 0;
530 static int clear_agent_password(pwm_t *pwm)
532 char *buf = "CLEAR_PASSPHRASE";
533 char *line;
534 size_t len = strlen(buf) + sizeof(pwm->cache_id) + 2;
535 int rc;
537 if (pwm->ctx) {
538 if ((line = malloc(len)) == NULL)
539 return 1;
541 sprintf(line, "%s %s", buf, pwm->cache_id);
542 rc = assuan_transact(pwm->ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
543 free(line);
545 if (rc)
546 return 1;
549 return 0;
552 int pwmd_command(pwm_t *pwm, void **result, int *error, pwmd_cmd cmd, ...)
554 va_list ap;
555 char *res = NULL;
556 int n;
557 char *filename = NULL;
558 char *p = NULL;
559 char *password = NULL;
560 char *account = NULL;
561 char *opt = NULL;
562 char *value = NULL;
563 void *tresult = NULL;
564 int terror = 0;
566 if (!pwm)
567 return PWMD_ERROR;
569 *result = NULL;
570 *error = EPWMD_ERROR;
571 va_start(ap, cmd);
573 switch (cmd) {
574 case PWMD_OPEN:
575 filename = va_arg(ap, char *);
577 if (pwm->use_agent) {
579 * Avoid calling gpg-agent if the password is cached on the
580 * server.
582 n = pwmd_command(pwm, &tresult, &terror, PWMD_CACHE,
583 PWMD_CACHE_ISCACHED, filename);
585 switch (n) {
586 case PWMD_ERROR:
587 *error = terror;
588 return PWMD_ERROR;
589 case PWMD_PERROR:
590 if (terror == EPWMD_CACHE_NOT_FOUND) {
591 if (get_agent_password(pwm, &password)) {
592 *error = EPWMD_ERROR;
593 return PWMD_AGENT_ERROR;
596 if (!password) {
597 *error = EPWMD_BADKEY;
598 return PWMD_PERROR;
601 p = pwmd_base64_encode(password, strlen(password));
602 secure_free(password, strlen(password) + 1);
603 password = p;
606 * We use pwmd's cache from now on.
608 clear_agent_password(pwm);
609 break;
611 else if (terror != EPWMD_FILE_NOT_FOUND) {
612 *error = terror;
613 return PWMD_PERROR;
615 break;
616 case PWMD_OK:
617 break;
620 else {
621 if (pwm->password == NULL) {
622 *error = EPWMD_BADKEY;
623 return PWMD_PERROR;
626 password = strdup(pwm->password);
629 if (send_to_daemon(pwm, "OPEN %s %s\n", filename, (password) ? password : "")) {
630 if (password)
631 secure_free(password, strlen(password) + 1);
632 *error = errno;
633 return PWMD_ERROR;
636 if (password)
637 secure_free(password, strlen(password) + 1);
638 break;
639 case PWMD_LIST:
640 if (send_to_daemon(pwm, "LIST\n")) {
641 *error = errno;
642 return PWMD_ERROR;
644 break;
645 case PWMD_LIST_ACCOUNT:
646 p = va_arg(ap, char *);
648 if (send_to_daemon(pwm, "LIST %s\n", p)) {
649 *error = errno;
650 return PWMD_ERROR;
652 break;
653 case PWMD_GET:
654 if (send_to_daemon(pwm, "GET %s\n", va_arg(ap, char *))) {
655 *error = errno;
656 return PWMD_ERROR;
658 break;
659 case PWMD_STORE:
660 if (send_to_daemon(pwm, "STORE %s\n", va_arg(ap, char *))) {
661 *error = errno;
662 return PWMD_ERROR;
664 break;
665 case PWMD_DELETE:
666 if (send_to_daemon(pwm, "DELETE %s\n", va_arg(ap, char *))) {
667 *error = errno;
668 return PWMD_ERROR;
670 break;
671 case PWMD_SETATTR:
672 account = va_arg(ap, char *);
673 opt = attr_to_str(va_arg(ap, int));
674 value = va_arg(ap, char *);
676 if (!opt)
677 return PWMD_PERROR;
679 if (send_to_daemon(pwm, "SETATTR %s %s %s\n", account, opt, value)) {
680 *error = errno;
681 return PWMD_ERROR;
683 break;
684 case PWMD_CACHE:
685 n = va_arg(ap, int);
687 switch (n) {
688 case PWMD_CACHE_ISCACHED:
689 p = va_arg(ap, char *);
691 if (send_to_daemon(pwm, "CACHE ISCACHED %s\n", p)) {
692 *error = errno;
693 return PWMD_ERROR;
695 break;
696 case PWMD_CACHE_CLEAR:
697 p = va_arg(ap, char *);
699 if (send_to_daemon(pwm, "CACHE CLEAR %s\n", p)) {
700 *error = errno;
701 return PWMD_ERROR;
703 break;
704 case PWMD_CACHE_CLEARALL:
705 if (send_to_daemon(pwm, "CACHE CLEARALL\n")) {
706 *error = errno;
707 return PWMD_ERROR;
709 break;
711 break;
712 case PWMD_SETOPT:
713 n = va_arg(ap, int);
715 switch (n) {
716 case PWMD_OPTION_USEAGENT:
717 n = va_arg(ap, int);
719 if (n != 0 && n != 1)
720 return PWMD_ERROR;
722 pwm->use_agent = n;
723 break;
724 case PWMD_OPTION_PASSWORD:
725 if (pwm->password) {
726 memset(pwm->password, 0, strlen(pwm->password));
727 free(pwm->password);
730 p = va_arg(ap, char *);
731 pwm->password = pwmd_base64_encode(p, strlen(p));
732 break;
733 case PWMD_OPTION_TITLE:
734 if (pwm->title)
735 free(pwm->title);
736 pwm->title = strdup(va_arg(ap, char *));
737 break;
738 case PWMD_OPTION_PROMPT:
739 if (pwm->prompt)
740 free(pwm->prompt);
741 pwm->prompt = strdup(va_arg(ap, char *));
742 break;
743 case PWMD_OPTION_DESC:
744 if (pwm->desc)
745 free(pwm->desc);
746 pwm->desc = strdup(va_arg(ap, char *));
747 break;
748 default:
749 *error = 0;
750 return PWMD_ERROR;
753 return PWMD_OK;
754 case PWMD_SAVE:
755 if (pwm->use_agent) {
756 if (get_agent_password(pwm, &password)) {
757 *error = 0;
758 return PWMD_AGENT_ERROR;
761 if (!password) {
762 *error = EPWMD_BADKEY;
763 return PWMD_PERROR;
766 p = pwmd_base64_encode(password, strlen(password));
767 secure_free(password, strlen(password) + 1);
768 password = p;
771 * We use pwmd's cache from now on.
773 clear_agent_password(pwm);
775 else
776 password = pwm->password;
778 if (send_to_daemon(pwm, "SAVE %s\n", password)) {
779 if (password)
780 secure_free(password, strlen(password) + 1);
782 *error = errno;
783 return PWMD_ERROR;
786 if (password)
787 secure_free(password, strlen(password) + 1);
788 break;
789 case PWMD_QUIT:
790 if (send_to_daemon(pwm, "QUIT\n")) {
791 *error = errno;
792 return PWMD_ERROR;
794 break;
795 default:
796 return PWMD_ERROR;
799 va_end(ap);
800 *result = NULL;
801 n = get_daemon_result(pwm, &res);
803 switch (n) {
804 case PWMD_PERROR:
805 p = res + 4;
806 *error = atoi(p);
807 free(res);
808 return PWMD_PERROR;
809 case PWMD_ERROR:
810 *error = errno;
811 return PWMD_ERROR;
812 case PWMD_OK:
813 switch (cmd) {
814 case PWMD_LIST:
815 case PWMD_LIST_ACCOUNT:
816 *result = parse_list_command(res);
817 break;
818 case PWMD_GET:
819 *result = res;
820 break;
821 default:
822 break;
824 default:
825 break;
828 return PWMD_OK;