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
);
344 FD_SET(pwm
->fd
, &fds
);
345 n
= select(pwm
->fd
+ 1, NULL
, &fds
, NULL
, NULL
);
352 if (FD_ISSET(pwm
->fd
, &fds
)) {
353 size_t t
= strlen(buf
);
354 ssize_t len
= send(pwm
->fd
, buf
, t
, 0);
366 secure_free(buf
, bufsize
);
370 static char **parse_list_command(char *str
)
376 while ((p
= strsep(&str
, "\n")) != NULL
) {
377 buf
= realloc(buf
, (n
+ 2) * sizeof(char *));
378 buf
[n
++] = strdup(p
);
385 static void init_cache_id(pwm_t
*pwm
)
390 snprintf(t
, sizeof(t
), "%li", n
);
392 if (strncmp(t
, pwm
->cache_id
, sizeof(t
)) == 0)
395 strncpy(pwm
->cache_id
, t
, sizeof(pwm
->cache_id
));
398 static gpg_error_t
connect_to_agent(pwm_t
*pwm
)
404 char path
[PATH_MAX
], *t
;
405 assuan_context_t ctx
;
407 if ((env
= getenv("GPG_AGENT_INFO")) == NULL
)
412 for (p
= env
, t
= path
; *p
; p
++) {
424 rc
= assuan_socket_connect(&ctx
, path
, pid
);
427 if (gpg_err_code(rc
) == GPG_ERR_ASS_CONNECT_FAILED
)
433 pwm
->title
= strdup("LibPWMD");
436 pwm
->prompt
= strdup("Password:");
439 pwm
->desc
= strdup("Enter a password.");
441 return send_pinentry_environment (pwm
->ctx
, GPG_ERR_SOURCE_DEFAULT
,
442 NULL
, NULL
, NULL
, NULL
, NULL
);
445 /* From GnuPG (g10/call-agent.c) */
446 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
447 escaping. Note that the provided buffer needs to be 3 times the
448 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
450 percent_plus_escape (char *p
, const char *atext
)
452 const unsigned char *s
;
454 for (s
=atext
; *s
; s
++)
456 if (*s
< ' ' || *s
== '+')
458 sprintf (p
, "%%%02X", *s
);
475 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
477 membuf_t
*mem
= data
;
483 if ((p
= realloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
487 memcpy(mem
->buf
+ mem
->len
, buffer
, len
);
493 static int get_agent_password(pwm_t
*pwm
, char **password
)
495 char cmd
[] = "GET_PASSPHRASE --data -- ";
502 if (connect_to_agent(pwm
))
506 line
= malloc(strlen(cmd
) + 1
507 + (3 * sizeof(pwm
->cache_id
) + 1)
508 + (3 * strlen(pwm
->title
) + 1)
509 + (3 * strlen(pwm
->desc
) + 1)
510 + (3 * strlen(pwm
->prompt
) + 1) + 1);
511 p
= stpcpy(line
, cmd
);
512 p
= percent_plus_escape(p
, pwm
->cache_id
);
514 p
= percent_plus_escape(p
, pwm
->desc
);
516 p
= percent_plus_escape(p
, pwm
->prompt
);
518 p
= percent_plus_escape(p
, pwm
->title
);
521 rc
= assuan_transact(pwm
->ctx
, line
, mem_realloc_cb
, &data
, NULL
, NULL
, NULL
, NULL
);
525 memset(data
.buf
, 0, data
.len
);
530 mem_realloc_cb(&data
, "", 1);
531 *password
= (char *)data
.buf
;
538 static int clear_agent_password(pwm_t
*pwm
)
540 char *buf
= "CLEAR_PASSPHRASE";
542 size_t len
= strlen(buf
) + sizeof(pwm
->cache_id
) + 2;
546 if ((line
= malloc(len
)) == NULL
)
549 sprintf(line
, "%s %s", buf
, pwm
->cache_id
);
550 rc
= assuan_transact(pwm
->ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
560 int pwmd_command(pwm_t
*pwm
, void **result
, int *error
, pwmd_cmd cmd
, ...)
565 char *filename
= NULL
;
567 char *password
= NULL
;
568 void *tresult
= NULL
;
570 char *arg1
, *arg2
, *arg3
;
576 *error
= EPWMD_ERROR
;
581 filename
= va_arg(ap
, char *);
584 * Avoid calling gpg-agent if the password is cached on the
587 n
= pwmd_command(pwm
, &tresult
, &terror
, PWMD_CACHE
,
588 PWMD_CACHE_ISCACHED
, filename
);
595 if (terror
== EPWMD_CACHE_NOT_FOUND
) {
597 * Get the password from gpg-agent/pinentry.
599 if (pwm
->use_agent
) {
600 if (get_agent_password(pwm
, &password
)) {
601 *error
= EPWMD_ERROR
;
602 return PWMD_AGENT_ERROR
;
610 p
= pwmd_base64_encode(password
, strlen(password
));
611 secure_free(password
, strlen(password
) + 1);
615 * We use pwmd's cache from now on.
617 clear_agent_password(pwm
);
622 * Not using gpg-agent and the file was not found
625 if (pwm
->password
== NULL
) {
630 password
= strdup(pwm
->password
);
633 else if (terror
!= EPWMD_FILE_NOT_FOUND
) {
642 if (send_to_daemon(pwm
, "OPEN %s %s\n", filename
, (password
) ? password
: "")) {
644 secure_free(password
, strlen(password
) + 1);
650 secure_free(password
, strlen(password
) + 1);
653 if (send_to_daemon(pwm
, "LIST\n")) {
658 case PWMD_LIST_ACCOUNT
:
659 p
= va_arg(ap
, char *);
661 if (send_to_daemon(pwm
, "LIST %s\n", p
)) {
667 arg1
= va_arg(ap
, char *);
669 if (send_to_daemon(pwm
, "GET %s\n", arg1
)) {
675 arg1
= va_arg(ap
, char *);
677 if (send_to_daemon(pwm
, "STORE %s\n", arg1
)) {
683 arg1
= va_arg(ap
, char *);
685 if (send_to_daemon(pwm
, "DELETE %s\n", arg1
)) {
691 arg1
= va_arg(ap
, char *);
692 arg2
= va_arg(ap
, char *);
693 arg3
= va_arg(ap
, char *);
695 if (send_to_daemon(pwm
, "ATTR SET %s %s %s\n", arg1
, arg2
, arg3
)) {
700 case PWMD_ATTR_DELETE
:
701 arg1
= va_arg(ap
, char *);
702 arg2
= va_arg(ap
, char *);
704 if (send_to_daemon(pwm
, "ATTR DELETE %s %s\n", arg1
, arg2
)) {
710 arg1
= va_arg(ap
, char *);
712 if (send_to_daemon(pwm
, "ATTR LIST %s\n", arg1
)) {
721 case PWMD_CACHE_ISCACHED
:
722 p
= va_arg(ap
, char *);
724 if (send_to_daemon(pwm
, "CACHE ISCACHED %s\n", p
)) {
729 case PWMD_CACHE_CLEAR
:
730 p
= va_arg(ap
, char *);
732 if (send_to_daemon(pwm
, "CACHE CLEAR %s\n", p
)) {
737 case PWMD_CACHE_CLEARALL
:
738 if (send_to_daemon(pwm
, "CACHE CLEARALL\n")) {
749 case PWMD_OPTION_USEAGENT
:
752 if (n
!= 0 && n
!= 1)
757 case PWMD_OPTION_PASSWORD
:
759 memset(pwm
->password
, 0, strlen(pwm
->password
));
763 p
= va_arg(ap
, char *);
764 pwm
->password
= pwmd_base64_encode(p
, strlen(p
));
766 case PWMD_OPTION_TITLE
:
769 pwm
->title
= strdup(va_arg(ap
, char *));
771 case PWMD_OPTION_PROMPT
:
774 pwm
->prompt
= strdup(va_arg(ap
, char *));
776 case PWMD_OPTION_DESC
:
779 pwm
->desc
= strdup(va_arg(ap
, char *));
788 if (pwm
->use_agent
) {
789 if (get_agent_password(pwm
, &password
)) {
791 return PWMD_AGENT_ERROR
;
794 if (!password
|| !*password
) {
799 p
= pwmd_base64_encode(password
, strlen(password
));
800 secure_free(password
, strlen(password
) + 1);
804 * We use pwmd's cache from now on.
806 clear_agent_password(pwm
);
809 password
= pwm
->password
;
811 if (send_to_daemon(pwm
, "SAVE %s\n", password
)) {
813 secure_free(password
, strlen(password
) + 1);
820 secure_free(password
, strlen(password
) + 1);
823 if (send_to_daemon(pwm
, "DUMP\n")) {
829 if (send_to_daemon(pwm
, "QUIT\n")) {
840 n
= get_daemon_result(pwm
, &res
);
855 case PWMD_LIST_ACCOUNT
:
856 *result
= parse_list_command(res
);