1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
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
26 #include <sys/socket.h>
50 #include "pwmd_error.h"
52 char *pwmd_base64_decode(const char *str
)
55 guchar
*buf
= g_base64_decode(str
, &len
);
58 dst
= strdup((char *)buf
);
64 char *pwmd_base64_encode(const char *str
, size_t len
)
66 gchar
*buf
= g_base64_encode((const guchar
*)str
, len
);
72 dst
= strdup((char *)buf
);
73 memset(buf
, 0, strlen((char *)buf
));
78 static int parse_protocol(char *data
, char **result
)
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)
95 * "Rewind" the pointer to the second to last newline character.
97 for (i
= total
- 1; i
; i
--, p
--) {
104 if (strncmp(p
, "ERR ", 4) == 0) {
109 if (strncmp(p
, "OK ", 3) == 0) {
114 if (strncmp(p
, "BEGIN ", 6) == 0) {
117 while (*p
&& isdigit(*p
))
131 static int get_daemon_result(pwm_t
*pwm
, char **result
)
140 FD_SET(pwm
->fd
, &fds
);
141 n
= select(pwm
->fd
+ 1, &fds
, NULL
, NULL
, NULL
);
146 if (FD_ISSET(pwm
->fd
, &fds
)) {
148 ssize_t len
= recv(pwm
->fd
, buf
, sizeof(buf
), 0);
159 memset(data
, 0, total
);
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
);
174 switch (parse_protocol(data
, result
)) {
181 memset(data
, 0, total
);
192 memset(data
, 0, total
);
197 pwm_t
*pwmd_connect(const char *path
, int *error
)
200 struct sockaddr_un addr
;
204 char *socketdir
= NULL
, *socketname
= NULL
, *socketarg
;
209 *error
= EPWMD_ERROR
;
213 if (getcwd(cwd
, sizeof(cwd
)) == NULL
) {
218 socketarg
= strdup(path
);
220 if (strchr(socketarg
, '/') == NULL
) {
221 socketdir
= strdup(cwd
);
222 p
= strdup(socketarg
);
226 p
= strdup(strrchr(socketarg
, '/'));
228 socketarg
[strlen(socketarg
) - strlen(socketname
) -1] = 0;
229 socketdir
= strdup(socketarg
);
232 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) == -1) {
240 if (chdir(socketdir
) == -1) {
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) {
266 if ((pwm
= calloc(1, sizeof(pwm_t
))) == NULL
) {
277 * Clear the initial OK response
279 get_daemon_result(pwm
, &result
);
283 void pwmd_close(pwm_t
*pwm
)
285 if (!pwm
|| pwm
->fd
< 0)
288 shutdown(pwm
->fd
, SHUT_RDWR
);
293 static void secure_free(void *p
, size_t len
)
299 static int send_to_daemon(pwm_t
*pwm
, char *fmt
, ...)
309 bufsize
= vasprintf(&buf
, fmt
, ap
);
315 FD_SET(pwm
->fd
, &fds
);
316 n
= select(pwm
->fd
+ 1, NULL
, &fds
, NULL
, NULL
);
319 secure_free(buf
, bufsize
);
323 if (FD_ISSET(pwm
->fd
, &fds
)) {
324 size_t t
= strlen(buf
);
325 ssize_t len
= send(pwm
->fd
, buf
, t
, 0);
328 secure_free(buf
, bufsize
);
335 * Keep sending data to the socket until the entire buffer has
339 memmove(&(buf
[0]), buf
+ len
, t
- len
);
348 secure_free(buf
, bufsize
);
352 static char **parse_list_command(char *str
)
358 while ((p
= strsep(&str
, "\n")) != NULL
) {
359 buf
= realloc(buf
, (n
+ 2) * sizeof(char *));
360 buf
[n
++] = strdup(p
);
367 static char *attr_to_str(int opt
)
379 static void init_cache_id(pwm_t
*pwm
)
384 snprintf(t
, sizeof(t
), "%li", n
);
386 if (strncmp(t
, pwm
->cache_id
, sizeof(t
)) == 0)
389 strncpy(pwm
->cache_id
, t
, sizeof(pwm
->cache_id
));
392 static gpg_error_t
connect_to_agent(pwm_t
*pwm
)
398 char path
[PATH_MAX
], *t
;
400 if ((env
= getenv("GPG_AGENT_INFO")) == NULL
)
405 for (p
= env
, t
= path
; *p
; p
++) {
417 rc
= assuan_socket_connect(&pwm
->ctx
, path
, pid
);
419 if (gpg_err_code(rc
) == GPG_ERR_ASS_CONNECT_FAILED
)
425 pwm
->title
= strdup("LibPWMD");
428 pwm
->prompt
= strdup("Password:");
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. */
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
);
467 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
469 membuf_t
*mem
= data
;
475 if ((p
= realloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
479 memcpy(mem
->buf
+ mem
->len
, buffer
, len
);
485 static int get_agent_password(pwm_t
*pwm
, char **password
)
487 char cmd
[] = "GET_PASSPHRASE --data -- ";
494 if (connect_to_agent(pwm
))
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
);
506 p
= percent_plus_escape(p
, pwm
->desc
);
508 p
= percent_plus_escape(p
, pwm
->prompt
);
510 p
= percent_plus_escape(p
, pwm
->title
);
513 rc
= assuan_transact(pwm
->ctx
, line
, mem_realloc_cb
, &data
, NULL
, NULL
, NULL
, NULL
);
517 memset(data
.buf
, 0, data
.len
);
522 mem_realloc_cb(&data
, "", 1);
523 *password
= (char *)data
.buf
;
530 static int clear_agent_password(pwm_t
*pwm
)
532 char *buf
= "CLEAR_PASSPHRASE";
534 size_t len
= strlen(buf
) + sizeof(pwm
->cache_id
) + 2;
538 if ((line
= malloc(len
)) == NULL
)
541 sprintf(line
, "%s %s", buf
, pwm
->cache_id
);
542 rc
= assuan_transact(pwm
->ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
552 int pwmd_command(pwm_t
*pwm
, void **result
, int *error
, pwmd_cmd cmd
, ...)
557 char *filename
= NULL
;
559 char *password
= NULL
;
560 char *account
= NULL
;
563 void *tresult
= NULL
;
570 *error
= EPWMD_ERROR
;
575 filename
= va_arg(ap
, char *);
577 if (pwm
->use_agent
) {
579 * Avoid calling gpg-agent if the password is cached on the
582 n
= pwmd_command(pwm
, &tresult
, &terror
, PWMD_CACHE
,
583 PWMD_CACHE_ISCACHED
, filename
);
590 if (terror
== EPWMD_CACHE_NOT_FOUND
) {
591 if (get_agent_password(pwm
, &password
)) {
592 *error
= EPWMD_ERROR
;
593 return PWMD_AGENT_ERROR
;
597 *error
= EPWMD_BADKEY
;
601 p
= pwmd_base64_encode(password
, strlen(password
));
602 secure_free(password
, strlen(password
) + 1);
606 * We use pwmd's cache from now on.
608 clear_agent_password(pwm
);
611 else if (terror
!= EPWMD_FILE_NOT_FOUND
) {
621 if (pwm
->password
== NULL
) {
622 *error
= EPWMD_BADKEY
;
626 password
= strdup(pwm
->password
);
629 if (send_to_daemon(pwm
, "OPEN %s %s\n", filename
, (password
) ? password
: "")) {
631 secure_free(password
, strlen(password
) + 1);
637 secure_free(password
, strlen(password
) + 1);
640 if (send_to_daemon(pwm
, "LIST\n")) {
645 case PWMD_LIST_ACCOUNT
:
646 p
= va_arg(ap
, char *);
648 if (send_to_daemon(pwm
, "LIST %s\n", p
)) {
654 if (send_to_daemon(pwm
, "GET %s\n", va_arg(ap
, char *))) {
660 if (send_to_daemon(pwm
, "STORE %s\n", va_arg(ap
, char *))) {
666 if (send_to_daemon(pwm
, "DELETE %s\n", va_arg(ap
, char *))) {
672 account
= va_arg(ap
, char *);
673 opt
= attr_to_str(va_arg(ap
, int));
674 value
= va_arg(ap
, char *);
679 if (send_to_daemon(pwm
, "SETATTR %s %s %s\n", account
, opt
, value
)) {
688 case PWMD_CACHE_ISCACHED
:
689 p
= va_arg(ap
, char *);
691 if (send_to_daemon(pwm
, "CACHE ISCACHED %s\n", p
)) {
696 case PWMD_CACHE_CLEAR
:
697 p
= va_arg(ap
, char *);
699 if (send_to_daemon(pwm
, "CACHE CLEAR %s\n", p
)) {
704 case PWMD_CACHE_CLEARALL
:
705 if (send_to_daemon(pwm
, "CACHE CLEARALL\n")) {
716 case PWMD_OPTION_USEAGENT
:
719 if (n
!= 0 && n
!= 1)
724 case PWMD_OPTION_PASSWORD
:
726 memset(pwm
->password
, 0, strlen(pwm
->password
));
730 p
= va_arg(ap
, char *);
731 pwm
->password
= pwmd_base64_encode(p
, strlen(p
));
733 case PWMD_OPTION_TITLE
:
736 pwm
->title
= strdup(va_arg(ap
, char *));
738 case PWMD_OPTION_PROMPT
:
741 pwm
->prompt
= strdup(va_arg(ap
, char *));
743 case PWMD_OPTION_DESC
:
746 pwm
->desc
= strdup(va_arg(ap
, char *));
755 if (pwm
->use_agent
) {
756 if (get_agent_password(pwm
, &password
)) {
758 return PWMD_AGENT_ERROR
;
762 *error
= EPWMD_BADKEY
;
766 p
= pwmd_base64_encode(password
, strlen(password
));
767 secure_free(password
, strlen(password
) + 1);
771 * We use pwmd's cache from now on.
773 clear_agent_password(pwm
);
776 password
= pwm
->password
;
778 if (send_to_daemon(pwm
, "SAVE %s\n", password
)) {
780 secure_free(password
, strlen(password
) + 1);
787 secure_free(password
, strlen(password
) + 1);
790 if (send_to_daemon(pwm
, "QUIT\n")) {
801 n
= get_daemon_result(pwm
, &res
);
815 case PWMD_LIST_ACCOUNT
:
816 *result
= parse_list_command(res
);