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 unsigned char *pwmd_base64_decode(const char *str
, size_t *size
)
85 guchar
*buf
= g_base64_decode(str
, &len
);
91 char *pwmd_base64_encode(const char *str
, size_t len
)
93 gchar
*buf
= g_base64_encode((const guchar
*)str
, len
);
99 dst
= strdup((char *)buf
);
100 memset(buf
, 0, strlen((char *)buf
));
105 static int parse_protocol(char *data
, char **result
)
109 size_t total
= strlen(data
);
112 * Set the pointer to the end of the buffer.
114 p
= data
+ strlen(data
) - 1;
116 if (*p
!= '\n' || total
< 3)
122 * "Rewind" the pointer to the second to last newline character.
124 for (i
= total
- 1; i
; i
--, p
--) {
131 if (strncmp(p
, "ERR ", 4) == 0) {
136 if (strncmp(p
, "OK ", 3) == 0) {
141 if (strncmp(p
, "BEGIN ", 6) == 0) {
144 while (*p
&& isdigit(*p
))
158 static int get_daemon_result(pwm_t
*pwm
, char **result
)
167 FD_SET(pwm
->fd
, &fds
);
168 n
= select(pwm
->fd
+ 1, &fds
, NULL
, NULL
, NULL
);
173 if (FD_ISSET(pwm
->fd
, &fds
)) {
175 ssize_t len
= recv(pwm
->fd
, buf
, sizeof(buf
), 0);
186 memset(data
, 0, total
);
194 * Keep appending to data until a valid protocol code is found.
196 data
= (char *)realloc(data
, len
+ total
+ 1);
197 memcpy(&(data
[total
]), buf
, len
);
201 switch (parse_protocol(data
, result
)) {
208 memset(data
, 0, total
);
219 memset(data
, 0, total
);
224 pwm_t
*pwmd_connect(const char *path
, int *error
)
227 struct sockaddr_un addr
;
231 char *socketdir
= NULL
, *socketname
= NULL
, *socketarg
;
237 pw
= getpwuid(getuid());
238 snprintf(cwd
, sizeof(cwd
), "%s/.pwmd/socket", pw
->pw_dir
);
239 socketarg
= strdup(cwd
);
242 socketarg
= strdup(path
);
244 if (getcwd(cwd
, sizeof(cwd
)) == NULL
) {
250 if (strchr(socketarg
, '/') == NULL
) {
251 socketdir
= strdup(cwd
);
252 p
= strdup(socketarg
);
256 p
= strdup(strrchr(socketarg
, '/'));
258 socketarg
[strlen(socketarg
) - strlen(socketname
) -1] = 0;
259 socketdir
= strdup(socketarg
);
262 if ((fd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) == -1) {
270 if (chdir(socketdir
) == -1) {
279 addr
.sun_family
= AF_UNIX
;
280 snprintf(addr
.sun_path
, sizeof(addr
.sun_path
), "%s", socketname
);
282 if (connect(fd
, (struct sockaddr
*)&addr
,
283 sizeof(struct sockaddr
)) == -1) {
296 if ((pwm
= (pwm_t
*)calloc(1, sizeof(pwm_t
))) == NULL
) {
307 * Clear the initial OK response
309 get_daemon_result(pwm
, &result
);
313 void pwmd_close(pwm_t
*pwm
)
315 if (!pwm
|| pwm
->fd
< 0)
318 shutdown(pwm
->fd
, SHUT_RDWR
);
323 static void secure_free(void *p
, size_t len
)
329 static int send_to_daemon(pwm_t
*pwm
, char *fmt
, ...)
339 bufsize
= vasprintf(&buf
, fmt
, ap
);
343 FD_SET(pwm
->fd
, &fds
);
344 n
= select(pwm
->fd
+ 1, NULL
, &fds
, NULL
, NULL
);
351 if (FD_ISSET(pwm
->fd
, &fds
)) {
352 size_t t
= strlen(buf
);
353 ssize_t len
= send(pwm
->fd
, buf
, t
, 0);
365 secure_free(buf
, bufsize
);
369 static char **parse_list_command(char *str
)
375 while ((p
= strsep(&str
, "\n")) != NULL
) {
376 buf
= (char **)realloc(buf
, (n
+ 2) * sizeof(char *));
377 buf
[n
++] = strdup(p
);
384 static void init_cache_id(pwm_t
*pwm
)
389 snprintf(t
, sizeof(t
), "%li", n
);
391 if (strncmp(t
, (char *)pwm
->cache_id
, sizeof(t
)) == 0)
394 strncpy((char *)pwm
->cache_id
, t
, sizeof(pwm
->cache_id
));
397 static gpg_error_t
connect_to_agent(pwm_t
*pwm
)
403 char path
[PATH_MAX
], *t
;
404 assuan_context_t ctx
;
406 if ((env
= getenv("GPG_AGENT_INFO")) == NULL
)
411 for (p
= env
, t
= path
; *p
; p
++) {
423 rc
= assuan_socket_connect(&ctx
, path
, pid
);
426 if (gpg_err_code(rc
) == GPG_ERR_ASS_CONNECT_FAILED
)
432 pwm
->title
= strdup("LibPWMD");
435 pwm
->prompt
= strdup("Password:");
438 pwm
->desc
= strdup("Enter a password.");
440 return send_pinentry_environment ((assuan_context_t
)pwm
->ctx
, GPG_ERR_SOURCE_DEFAULT
,
441 NULL
, NULL
, NULL
, NULL
, NULL
);
444 /* From GnuPG (g10/call-agent.c) */
445 /* Copy the text ATEXT into the buffer P and do plus '+' and percent
446 escaping. Note that the provided buffer needs to be 3 times the
447 size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
449 percent_plus_escape (char *p
, const char *atext
)
451 const unsigned char *s
;
453 for (s
=(const unsigned char *)atext
; *s
; s
++)
455 if (*s
< ' ' || *s
== '+')
457 sprintf (p
, "%%%02X", *s
);
474 static int mem_realloc_cb(void *data
, const void *buffer
, size_t len
)
476 membuf_t
*mem
= (membuf_t
*)data
;
482 if ((p
= realloc(mem
->buf
, mem
->len
+ len
)) == NULL
)
486 memcpy((char *)mem
->buf
+ mem
->len
, buffer
, len
);
492 static int get_agent_password(pwm_t
*pwm
, char **password
)
494 char cmd
[] = "GET_PASSPHRASE --data -- ";
501 if (connect_to_agent(pwm
))
505 line
= (char *)malloc(strlen(cmd
) + 1
506 + (3 * sizeof(pwm
->cache_id
) + 1)
507 + (3 * strlen(pwm
->title
) + 1)
508 + (3 * strlen(pwm
->desc
) + 1)
509 + (3 * strlen(pwm
->prompt
) + 1) + 1);
510 p
= stpcpy(line
, cmd
);
511 p
= percent_plus_escape(p
, pwm
->cache_id
);
513 p
= percent_plus_escape(p
, pwm
->desc
);
515 p
= percent_plus_escape(p
, pwm
->prompt
);
517 p
= percent_plus_escape(p
, pwm
->title
);
520 rc
= assuan_transact((assuan_context_t
)pwm
->ctx
, line
, mem_realloc_cb
, &data
, NULL
, NULL
, NULL
, NULL
);
524 memset(data
.buf
, 0, data
.len
);
529 mem_realloc_cb(&data
, "", 1);
530 *password
= (char *)data
.buf
;
537 static int clear_agent_password(pwm_t
*pwm
)
539 char *buf
= "CLEAR_PASSPHRASE";
541 size_t len
= strlen(buf
) + sizeof(pwm
->cache_id
) + 2;
545 if ((line
= (char *)malloc(len
)) == NULL
)
548 sprintf(line
, "%s %s", buf
, pwm
->cache_id
);
549 rc
= assuan_transact((assuan_context_t
)pwm
->ctx
, line
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
559 int pwmd_command(pwm_t
*pwm
, void **result
, int *error
, pwmd_cmd cmd
, ...)
564 char *filename
= NULL
;
566 char *password
= NULL
;
567 void *tresult
= NULL
;
569 char *arg1
, *arg2
, *arg3
;
575 *error
= EPWMD_ERROR
;
580 filename
= va_arg(ap
, char *);
583 * Avoid calling gpg-agent if the password is cached on the
586 n
= pwmd_command(pwm
, &tresult
, &terror
, PWMD_CACHE
,
587 PWMD_CACHE_ISCACHED
, filename
);
594 if (terror
== EPWMD_CACHE_NOT_FOUND
) {
596 * Get the password from gpg-agent/pinentry.
598 if (pwm
->use_agent
) {
599 if (get_agent_password(pwm
, &password
)) {
600 *error
= EPWMD_ERROR
;
601 return PWMD_AGENT_ERROR
;
609 p
= strdup(password
);
610 secure_free(password
, strlen(password
));
614 * We use pwmd's cache from now on.
616 clear_agent_password(pwm
);
621 * Not using gpg-agent and the file was not found
624 if (pwm
->password
== NULL
) {
629 password
= strdup(pwm
->password
);
632 else if (terror
!= EPWMD_FILE_NOT_FOUND
) {
641 if (send_to_daemon(pwm
, "OPEN %s %s\n", filename
, (password
) ? password
: "")) {
643 secure_free(password
, strlen(password
));
649 secure_free(password
, strlen(password
));
652 if (send_to_daemon(pwm
, "LIST\n")) {
657 case PWMD_LIST_ACCOUNT
:
658 p
= va_arg(ap
, char *);
660 if (send_to_daemon(pwm
, "LIST %s\n", p
)) {
666 arg1
= va_arg(ap
, char *);
668 if (send_to_daemon(pwm
, "GET %s\n", arg1
)) {
674 arg1
= va_arg(ap
, char *);
676 if (send_to_daemon(pwm
, "STORE %s\n", arg1
)) {
682 arg1
= va_arg(ap
, char *);
684 if (send_to_daemon(pwm
, "DELETE %s\n", arg1
)) {
690 arg1
= va_arg(ap
, char *);
691 arg2
= va_arg(ap
, char *);
692 arg3
= va_arg(ap
, char *);
694 if (send_to_daemon(pwm
, "ATTR SET %s %s %s\n", arg1
, arg2
, arg3
)) {
700 arg1
= va_arg(ap
, char *);
701 arg2
= va_arg(ap
, char *);
703 if (send_to_daemon(pwm
, "ATTR GET %s %s\n", arg1
, arg2
)) {
708 case PWMD_ATTR_DELETE
:
709 arg1
= va_arg(ap
, char *);
710 arg2
= va_arg(ap
, char *);
712 if (send_to_daemon(pwm
, "ATTR DELETE %s %s\n", arg1
, arg2
)) {
718 arg1
= va_arg(ap
, char *);
720 if (send_to_daemon(pwm
, "ATTR LIST %s\n", arg1
)) {
729 case PWMD_CACHE_ISCACHED
:
730 p
= va_arg(ap
, char *);
732 if (send_to_daemon(pwm
, "CACHE ISCACHED %s\n", p
)) {
737 case PWMD_CACHE_CLEAR
:
738 arg1
= va_arg(ap
, char *);
740 if (send_to_daemon(pwm
, "CACHE CLEAR %s\n", arg1
)) {
745 case PWMD_CACHE_CLEARALL
:
746 if (send_to_daemon(pwm
, "CACHE CLEARALL\n")) {
757 case PWMD_OPTION_USEAGENT
:
760 if (n
!= 0 && n
!= 1)
765 case PWMD_OPTION_PASSWORD
:
766 arg1
= va_arg(ap
, char *);
769 memset(pwm
->password
, 0, strlen(pwm
->password
));
773 pwm
->password
= strdup(arg1
);
775 case PWMD_OPTION_TITLE
:
778 pwm
->title
= strdup(va_arg(ap
, char *));
780 case PWMD_OPTION_PROMPT
:
783 pwm
->prompt
= strdup(va_arg(ap
, char *));
785 case PWMD_OPTION_DESC
:
788 pwm
->desc
= strdup(va_arg(ap
, char *));
797 if (pwm
->use_agent
) {
798 if (get_agent_password(pwm
, &password
)) {
800 return PWMD_AGENT_ERROR
;
803 if (!password
|| !*password
) {
808 p
= strdup(password
);
809 secure_free(password
, strlen(password
));
813 * We use pwmd's cache from now on.
815 clear_agent_password(pwm
);
818 password
= pwm
->password
;
820 if (send_to_daemon(pwm
, "SAVE %s\n", password
)) {
822 secure_free(password
, strlen(password
));
829 secure_free(password
, strlen(password
));
832 if (send_to_daemon(pwm
, "DUMP\n")) {
838 if (send_to_daemon(pwm
, "QUIT\n")) {
849 n
= get_daemon_result(pwm
, &res
);
864 case PWMD_LIST_ACCOUNT
:
865 *result
= parse_list_command(res
);