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",
72 "Attribute not found",
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
)
89 for (p
= list
; *p
; p
++)
95 unsigned char *pwmd_base64_decode(const char *str
, size_t *size
)
98 guchar
*buf
= g_base64_decode(str
, &len
);
104 char *pwmd_base64_encode(const char *str
, size_t len
)
106 gchar
*buf
= g_base64_encode((const guchar
*)str
, len
);
112 dst
= strdup((char *)buf
);
113 memset(buf
, 0, strlen((char *)buf
));
118 static int parse_protocol(char *data
, size_t total
, char **result
)
124 * Set the pointer to the end of the buffer.
126 p
= data
+ total
- 1;
128 if (*p
!= '\n' || total
< 3)
134 * "Rewind" the pointer to the second to last newline character.
136 for (i
= total
- 1; i
; i
--, p
--) {
143 if (strncmp(p
, "ERR ", 4) == 0) {
148 if (strncmp(p
, "OK ", 3) == 0) {
153 if (strncmp(p
, "BEGIN ", 6) == 0) {
156 while (*p
&& isdigit(*p
))
170 static int get_daemon_result(pwm_t
*pwm
, char **result
)
179 FD_SET(pwm
->fd
, &fds
);
180 n
= select(pwm
->fd
+ 1, &fds
, NULL
, NULL
, NULL
);
185 if (FD_ISSET(pwm
->fd
, &fds
)) {
187 ssize_t len
= recv(pwm
->fd
, buf
, sizeof(buf
), 0);
191 memset(data
, 0, total
);
200 memset(data
, 0, total
);
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
);
215 switch (parse_protocol(data
, total
, result
)) {
219 memset(data
, 0, total
);
223 memset(data
, 0, total
);
234 memset(data
, 0, total
);
239 pwm_t
*pwmd_connect(const char *path
, int *error
)
242 struct sockaddr_un addr
;
246 char *socketdir
= NULL
, *socketname
= NULL
, *socketarg
;
252 pw
= getpwuid(getuid());
253 snprintf(cwd
, sizeof(cwd
), "%s/.pwmd/socket", pw
->pw_dir
);
254 socketarg
= strdup(cwd
);
257 socketarg
= strdup(path
);
259 if (getcwd(cwd
, sizeof(cwd
)) == NULL
) {
265 if (strchr(socketarg
, '/') == NULL
) {
266 socketdir
= strdup(cwd
);
267 p
= strdup(socketarg
);
271 p
= strdup(strrchr(socketarg
, '/'));
273 socketarg
[strlen(socketarg
) - strlen(socketname
) -1] = 0;
274 socketdir
= strdup(socketarg
);
277 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) == -1) {
285 if (chdir(socketdir
) == -1) {
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) {
311 if ((pwm
= (pwm_t
*)calloc(1, sizeof(pwm_t
))) == NULL
) {
322 * Clear the initial OK response
324 get_daemon_result(pwm
, &result
);
331 void pwmd_close(pwm_t
*pwm
)
333 if (!pwm
|| pwm
->fd
< 0)
336 shutdown(pwm
->fd
, SHUT_RDWR
);
354 static void secure_free(void *p
, size_t len
)
360 static int send_to_daemon(pwm_t
*pwm
, char *fmt
, ...)
370 bufsize
= vasprintf(&buf
, fmt
, ap
);
374 FD_SET(pwm
->fd
, &fds
);
375 n
= select(pwm
->fd
+ 1, NULL
, &fds
, NULL
, NULL
);
382 if (FD_ISSET(pwm
->fd
, &fds
)) {
383 size_t t
= strlen(buf
);
384 ssize_t len
= send(pwm
->fd
, buf
, t
, 0);
396 secure_free(buf
, bufsize
);
400 static char **parse_list_command(char *str
)
406 while ((p
= strsep(&str
, "\n")) != NULL
) {
410 buf
= (char **)realloc(buf
, (n
+ 2) * sizeof(char *));
411 buf
[n
++] = strdup(p
);
418 static void init_cache_id(pwm_t
*pwm
)
423 snprintf(t
, sizeof(t
), "%li", n
);
425 if (strncmp(t
, (char *)pwm
->cache_id
, sizeof(t
)) == 0)
428 strncpy((char *)pwm
->cache_id
, t
, sizeof(pwm
->cache_id
));
431 static gpg_error_t
connect_to_agent(pwm_t
*pwm
)
437 char path
[PATH_MAX
], *t
;
438 assuan_context_t ctx
;
440 if ((env
= getenv("GPG_AGENT_INFO")) == NULL
)
445 for (p
= env
, t
= path
; *p
; p
++) {
457 rc
= assuan_socket_connect(&ctx
, path
, pid
);
460 if (gpg_err_code(rc
) == GPG_ERR_ASS_CONNECT_FAILED
)
466 pwm
->title
= strdup("LibPWMD");
469 pwm
->prompt
= strdup("Password:");
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. */
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
);
508 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
510 membuf_t
*mem
= (membuf_t
*)data
;
516 if ((p
= realloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
520 memcpy((char *)mem
->buf
+ mem
->len
, buffer
, len
);
526 static int get_agent_password(pwm_t
*pwm
, char **password
)
528 char cmd
[] = "GET_PASSPHRASE --data -- ";
535 if (connect_to_agent(pwm
))
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
);
547 p
= percent_plus_escape(p
, pwm
->desc
);
549 p
= percent_plus_escape(p
, pwm
->prompt
);
551 p
= percent_plus_escape(p
, pwm
->title
);
554 rc
= assuan_transact((assuan_context_t
)pwm
->ctx
, line
, mem_realloc_cb
, &data
, NULL
, NULL
, NULL
, NULL
);
558 memset(data
.buf
, 0, data
.len
);
563 mem_realloc_cb(&data
, "", 1);
564 *password
= (char *)data
.buf
;
568 assuan_disconnect((assuan_context_t
)pwm
->ctx
);
573 static int clear_agent_password(pwm_t
*pwm
)
575 char *buf
= "CLEAR_PASSPHRASE";
577 size_t len
= strlen(buf
) + sizeof(pwm
->cache_id
) + 2;
581 if ((line
= (char *)malloc(len
)) == NULL
)
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
);
595 int pwmd_command(pwm_t
*pwm
, void **result
, int *error
, pwmd_cmd cmd
, ...)
600 char *filename
= NULL
;
602 char *password
= NULL
;
603 void *tresult
= NULL
;
605 char *arg1
, *arg2
, *arg3
;
611 *error
= EPWMD_ERROR
;
616 filename
= va_arg(ap
, char *);
619 * Avoid calling gpg-agent if the password is cached on the
622 n
= pwmd_command(pwm
, &tresult
, &terror
, PWMD_CACHE
,
623 PWMD_CACHE_ISCACHED
, filename
);
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
;
645 p
= strdup(password
);
646 secure_free(password
, strlen(password
));
650 * We use pwmd's cache from now on.
652 clear_agent_password(pwm
);
657 * Not using gpg-agent and the file was not found
660 if (pwm
->password
== NULL
) {
665 password
= strdup(pwm
->password
);
668 else if (terror
!= EPWMD_FILE_NOT_FOUND
) {
677 if (send_to_daemon(pwm
, "OPEN %s %s\n", filename
, (password
) ? password
: "")) {
679 secure_free(password
, strlen(password
));
685 secure_free(password
, strlen(password
));
688 if (send_to_daemon(pwm
, "LIST\n")) {
693 case PWMD_LIST_ACCOUNT
:
694 p
= va_arg(ap
, char *);
696 if (send_to_daemon(pwm
, "LIST %s\n", p
)) {
702 arg1
= va_arg(ap
, char *);
704 if (send_to_daemon(pwm
, "GET %s\n", arg1
)) {
710 arg1
= va_arg(ap
, char *);
712 if (send_to_daemon(pwm
, "STORE %s\n", arg1
)) {
718 arg1
= va_arg(ap
, char *);
720 if (send_to_daemon(pwm
, "DELETE %s\n", arg1
)) {
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
)) {
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
)) {
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
)) {
754 arg1
= va_arg(ap
, char *);
756 if (send_to_daemon(pwm
, "ATTR LIST %s\n", arg1
)) {
765 case PWMD_CACHE_ISCACHED
:
766 p
= va_arg(ap
, char *);
768 if (send_to_daemon(pwm
, "CACHE ISCACHED %s\n", p
)) {
773 case PWMD_CACHE_CLEAR
:
774 arg1
= va_arg(ap
, char *);
776 if (send_to_daemon(pwm
, "CACHE CLEAR %s\n", arg1
)) {
781 case PWMD_CACHE_CLEARALL
:
782 if (send_to_daemon(pwm
, "CACHE CLEARALL\n")) {
793 case PWMD_OPTION_USEAGENT
:
796 if (n
!= 0 && n
!= 1)
801 case PWMD_OPTION_PASSWORD
:
802 arg1
= va_arg(ap
, char *);
805 memset(pwm
->password
, 0, strlen(pwm
->password
));
809 pwm
->password
= strdup(arg1
);
811 case PWMD_OPTION_TITLE
:
814 pwm
->title
= strdup(va_arg(ap
, char *));
816 case PWMD_OPTION_PROMPT
:
819 pwm
->prompt
= strdup(va_arg(ap
, char *));
821 case PWMD_OPTION_DESC
:
824 pwm
->desc
= strdup(va_arg(ap
, char *));
833 if (pwm
->use_agent
) {
834 if (get_agent_password(pwm
, &password
)) {
836 return PWMD_AGENT_ERROR
;
839 if (!password
|| !*password
) {
844 p
= strdup(password
);
845 secure_free(password
, strlen(password
));
849 * We use pwmd's cache from now on.
851 clear_agent_password(pwm
);
854 password
= pwm
->password
;
856 if (send_to_daemon(pwm
, "SAVE %s\n", password
)) {
858 secure_free(password
, strlen(password
));
865 secure_free(password
, strlen(password
));
867 pwm
->password
= NULL
;
870 if (send_to_daemon(pwm
, "DUMP\n")) {
876 if (send_to_daemon(pwm
, "QUIT\n")) {
887 n
= get_daemon_result(pwm
, &res
);
903 case PWMD_LIST_ACCOUNT
:
904 *result
= parse_list_command(res
);
905 memset(res
, 0, strlen(res
));
911 if (res
[strlen(res
) - 1] == '\n')
912 res
[strlen(res
) - 1] = 0;
918 memset(res
, 0, strlen(res
));