1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
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
26 #include <sys/socket.h>
51 const char *pwmd_strerror(int pwmd_errno
)
53 const char *pwmd_error_str
[] = {
55 "Invalid characters in filename",
56 "No cache slots available",
59 "Invalid command or command syntax",
62 "Invalid character in element",
63 "Text element in account root element not allowed",
65 "Invalid attribute name or syntax",
66 "Will not overwrite existing account",
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
)
83 guchar
*buf
= g_base64_decode(str
, &len
);
86 dst
= strdup((char *)buf
);
92 char *pwmd_base64_encode(const char *str
, size_t len
)
94 gchar
*buf
= g_base64_encode((const guchar
*)str
, len
);
100 dst
= strdup((char *)buf
);
101 memset(buf
, 0, strlen((char *)buf
));
106 static int parse_protocol(char *data
, char **result
)
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)
123 * "Rewind" the pointer to the second to last newline character.
125 for (i
= total
- 1; i
; i
--, p
--) {
132 if (strncmp(p
, "ERR ", 4) == 0) {
137 if (strncmp(p
, "OK ", 3) == 0) {
142 if (strncmp(p
, "BEGIN ", 6) == 0) {
145 while (*p
&& isdigit(*p
))
159 static int get_daemon_result(pwm_t
*pwm
, char **result
)
168 FD_SET(pwm
->fd
, &fds
);
169 n
= select(pwm
->fd
+ 1, &fds
, NULL
, NULL
, NULL
);
174 if (FD_ISSET(pwm
->fd
, &fds
)) {
176 ssize_t len
= recv(pwm
->fd
, buf
, sizeof(buf
), 0);
187 memset(data
, 0, total
);
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
);
202 switch (parse_protocol(data
, result
)) {
209 memset(data
, 0, total
);
220 memset(data
, 0, total
);
225 pwm_t
*pwmd_connect(const char *path
, int *error
)
228 struct sockaddr_un addr
;
232 char *socketdir
= NULL
, *socketname
= NULL
, *socketarg
;
238 pw
= getpwuid(getuid());
239 snprintf(cwd
, sizeof(cwd
), "%s/.pwmd/socket", pw
->pw_dir
);
240 socketarg
= strdup(cwd
);
243 socketarg
= strdup(path
);
245 if (getcwd(cwd
, sizeof(cwd
)) == NULL
) {
251 if (strchr(socketarg
, '/') == NULL
) {
252 socketdir
= strdup(cwd
);
253 p
= strdup(socketarg
);
257 p
= strdup(strrchr(socketarg
, '/'));
259 socketarg
[strlen(socketarg
) - strlen(socketname
) -1] = 0;
260 socketdir
= strdup(socketarg
);
263 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) == -1) {
271 if (chdir(socketdir
) == -1) {
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) {
297 if ((pwm
= calloc(1, sizeof(pwm_t
))) == NULL
) {
308 * Clear the initial OK response
310 get_daemon_result(pwm
, &result
);
314 void pwmd_close(pwm_t
*pwm
)
316 if (!pwm
|| pwm
->fd
< 0)
319 shutdown(pwm
->fd
, SHUT_RDWR
);
324 static void secure_free(void *p
, size_t len
)
330 static int send_to_daemon(pwm_t
*pwm
, char *fmt
, ...)
340 bufsize
= vasprintf(&buf
, fmt
, ap
);
346 FD_SET(pwm
->fd
, &fds
);
347 n
= select(pwm
->fd
+ 1, NULL
, &fds
, NULL
, NULL
);
350 secure_free(buf
, bufsize
);
354 if (FD_ISSET(pwm
->fd
, &fds
)) {
355 size_t t
= strlen(buf
);
356 ssize_t len
= send(pwm
->fd
, buf
, t
, 0);
359 secure_free(buf
, bufsize
);
366 * Keep sending data to the socket until the entire buffer has
370 memmove(&(buf
[0]), buf
+ len
, t
- len
);
379 secure_free(buf
, bufsize
);
383 static char **parse_list_command(char *str
)
389 while ((p
= strsep(&str
, "\n")) != NULL
) {
390 buf
= realloc(buf
, (n
+ 2) * sizeof(char *));
391 buf
[n
++] = strdup(p
);
398 static void init_cache_id(pwm_t
*pwm
)
403 snprintf(t
, sizeof(t
), "%li", n
);
405 if (strncmp(t
, pwm
->cache_id
, sizeof(t
)) == 0)
408 strncpy(pwm
->cache_id
, t
, sizeof(pwm
->cache_id
));
411 static gpg_error_t
connect_to_agent(pwm_t
*pwm
)
417 char path
[PATH_MAX
], *t
;
418 assuan_context_t ctx
;
420 if ((env
= getenv("GPG_AGENT_INFO")) == NULL
)
425 for (p
= env
, t
= path
; *p
; p
++) {
437 rc
= assuan_socket_connect(&ctx
, path
, pid
);
440 if (gpg_err_code(rc
) == GPG_ERR_ASS_CONNECT_FAILED
)
446 pwm
->title
= strdup("LibPWMD");
449 pwm
->prompt
= strdup("Password:");
452 pwm
->desc
= strdup("Enter a password.");
454 return send_pinentry_environment (pwm
->ctx
, GPG_ERR_SOURCE_DEFAULT
,
455 NULL
, NULL
, NULL
, NULL
, NULL
);
458 /* From GnuPG (g10/call-agent.c) */
459 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
460 escaping. Note that the provided buffer needs to be 3 times the
461 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
463 percent_plus_escape (char *p
, const char *atext
)
465 const unsigned char *s
;
467 for (s
=atext
; *s
; s
++)
469 if (*s
< ' ' || *s
== '+')
471 sprintf (p
, "%%%02X", *s
);
488 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
490 membuf_t
*mem
= data
;
496 if ((p
= realloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
500 memcpy(mem
->buf
+ mem
->len
, buffer
, len
);
506 static int get_agent_password(pwm_t
*pwm
, char **password
)
508 char cmd
[] = "GET_PASSPHRASE --data -- ";
515 if (connect_to_agent(pwm
))
519 line
= malloc(strlen(cmd
) + 1
520 + (3 * sizeof(pwm
->cache_id
) + 1)
521 + (3 * strlen(pwm
->title
) + 1)
522 + (3 * strlen(pwm
->desc
) + 1)
523 + (3 * strlen(pwm
->prompt
) + 1) + 1);
524 p
= stpcpy(line
, cmd
);
525 p
= percent_plus_escape(p
, pwm
->cache_id
);
527 p
= percent_plus_escape(p
, pwm
->desc
);
529 p
= percent_plus_escape(p
, pwm
->prompt
);
531 p
= percent_plus_escape(p
, pwm
->title
);
534 rc
= assuan_transact(pwm
->ctx
, line
, mem_realloc_cb
, &data
, NULL
, NULL
, NULL
, NULL
);
538 memset(data
.buf
, 0, data
.len
);
543 mem_realloc_cb(&data
, "", 1);
544 *password
= (char *)data
.buf
;
551 static int clear_agent_password(pwm_t
*pwm
)
553 char *buf
= "CLEAR_PASSPHRASE";
555 size_t len
= strlen(buf
) + sizeof(pwm
->cache_id
) + 2;
559 if ((line
= malloc(len
)) == NULL
)
562 sprintf(line
, "%s %s", buf
, pwm
->cache_id
);
563 rc
= assuan_transact(pwm
->ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
573 int pwmd_command(pwm_t
*pwm
, void **result
, int *error
, pwmd_cmd cmd
, ...)
578 char *filename
= NULL
;
580 char *password
= NULL
;
581 void *tresult
= NULL
;
583 char *arg1
, *arg2
, *arg3
;
589 *error
= EPWMD_ERROR
;
594 filename
= va_arg(ap
, char *);
597 * Avoid calling gpg-agent if the password is cached on the
600 n
= pwmd_command(pwm
, &tresult
, &terror
, PWMD_CACHE
,
601 PWMD_CACHE_ISCACHED
, filename
);
608 if (terror
== EPWMD_CACHE_NOT_FOUND
) {
610 * Get the password from gpg-agent/pinentry.
612 if (pwm
->use_agent
) {
613 if (get_agent_password(pwm
, &password
)) {
614 *error
= EPWMD_ERROR
;
615 return PWMD_AGENT_ERROR
;
623 p
= pwmd_base64_encode(password
, strlen(password
));
624 secure_free(password
, strlen(password
) + 1);
628 * We use pwmd's cache from now on.
630 clear_agent_password(pwm
);
635 * Not using gpg-agent and the file was not found
638 if (pwm
->password
== NULL
) {
643 password
= strdup(pwm
->password
);
646 else if (terror
!= EPWMD_FILE_NOT_FOUND
) {
655 if (send_to_daemon(pwm
, "OPEN %s %s\n", filename
, (password
) ? password
: "")) {
657 secure_free(password
, strlen(password
) + 1);
663 secure_free(password
, strlen(password
) + 1);
666 if (send_to_daemon(pwm
, "LIST\n")) {
671 case PWMD_LIST_ACCOUNT
:
672 p
= va_arg(ap
, char *);
674 if (send_to_daemon(pwm
, "LIST %s\n", p
)) {
680 arg1
= va_arg(ap
, char *);
682 if (send_to_daemon(pwm
, "GET %s\n", arg1
)) {
688 arg1
= va_arg(ap
, char *);
690 if (send_to_daemon(pwm
, "STORE %s\n", arg1
)) {
696 arg1
= va_arg(ap
, char *);
698 if (send_to_daemon(pwm
, "DELETE %s\n", arg1
)) {
704 arg1
= va_arg(ap
, char *);
705 arg2
= va_arg(ap
, char *);
706 arg3
= va_arg(ap
, char *);
708 if (send_to_daemon(pwm
, "ATTR SET %s %s %s\n", arg1
, arg2
, arg3
)) {
713 case PWMD_ATTR_DELETE
:
714 arg1
= va_arg(ap
, char *);
715 arg2
= va_arg(ap
, char *);
717 if (send_to_daemon(pwm
, "ATTR DELETE %s %s\n", arg1
, arg2
)) {
723 arg1
= va_arg(ap
, char *);
725 if (send_to_daemon(pwm
, "ATTR LIST %s\n", arg1
)) {
734 case PWMD_CACHE_ISCACHED
:
735 p
= va_arg(ap
, char *);
737 if (send_to_daemon(pwm
, "CACHE ISCACHED %s\n", p
)) {
742 case PWMD_CACHE_CLEAR
:
743 p
= va_arg(ap
, char *);
745 if (send_to_daemon(pwm
, "CACHE CLEAR %s\n", p
)) {
750 case PWMD_CACHE_CLEARALL
:
751 if (send_to_daemon(pwm
, "CACHE CLEARALL\n")) {
762 case PWMD_OPTION_USEAGENT
:
765 if (n
!= 0 && n
!= 1)
770 case PWMD_OPTION_PASSWORD
:
772 memset(pwm
->password
, 0, strlen(pwm
->password
));
776 p
= va_arg(ap
, char *);
777 pwm
->password
= pwmd_base64_encode(p
, strlen(p
));
779 case PWMD_OPTION_TITLE
:
782 pwm
->title
= strdup(va_arg(ap
, char *));
784 case PWMD_OPTION_PROMPT
:
787 pwm
->prompt
= strdup(va_arg(ap
, char *));
789 case PWMD_OPTION_DESC
:
792 pwm
->desc
= strdup(va_arg(ap
, char *));
801 if (pwm
->use_agent
) {
802 if (get_agent_password(pwm
, &password
)) {
804 return PWMD_AGENT_ERROR
;
807 if (!password
|| !*password
) {
812 p
= pwmd_base64_encode(password
, strlen(password
));
813 secure_free(password
, strlen(password
) + 1);
817 * We use pwmd's cache from now on.
819 clear_agent_password(pwm
);
822 password
= pwm
->password
;
824 if (send_to_daemon(pwm
, "SAVE %s\n", password
)) {
826 secure_free(password
, strlen(password
) + 1);
833 secure_free(password
, strlen(password
) + 1);
836 if (send_to_daemon(pwm
, "DUMP\n")) {
842 if (send_to_daemon(pwm
, "QUIT\n")) {
853 n
= get_daemon_result(pwm
, &res
);
868 case PWMD_LIST_ACCOUNT
:
869 *result
= parse_list_command(res
);