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
24 #include <sys/types.h>
29 #include <glib/gprintf.h>
44 #include "pwmd_error.h"
48 static void *z_alloc(void *data
, unsigned items
, unsigned size
)
51 return gcry_calloc(items
, size
);
53 return calloc(items
, size
);
57 static void z_free(void *data
, void *p
)
66 static gpg_error_t
file_modified(struct client_s
*client
)
70 if (client
->state
!= STATE_OPEN
)
73 if (stat(client
->filename
, &st
) == 0 && client
->mtime
) {
74 if (client
->mtime
!= st
.st_mtime
)
75 return EPWMD_FILE_MODIFIED
;
81 static gboolean
encrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
82 void *inbuf
, gsize insize
)
86 if ((rc
= gcry_cipher_encrypt(gh
, outbuf
, outsize
, inbuf
, insize
))) {
87 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
94 gpg_error_t
decrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
95 void *inbuf
, gsize insize
)
99 if ((rc
= gcry_cipher_decrypt(gh
, outbuf
, outsize
, inbuf
, insize
)))
100 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
105 static gpg_error_t
parse_xml(assuan_context_t ctx
)
107 struct client_s
*client
= assuan_get_pointer(ctx
);
109 client
->doc
= xmlReadMemory(client
->xml
, client
->len
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
112 return EPWMD_LIBXML_ERROR
;
117 gboolean
valid_filename(const gchar
*filename
)
121 if (!filename
|| !*filename
)
124 for (p
= filename
; *p
; p
++) {
125 if (g_ascii_isalnum(*p
) == FALSE
&& *p
!= '-' && *p
!= '_' && *p
!= '.')
132 gint
open_file(const gchar
*filename
, struct stat
*st
)
136 if ((fd
= open(filename
, O_RDONLY
)) == -1)
139 if (stat(filename
, st
) == -1) {
147 void unlock_file_mutex(struct client_s
*client
)
151 if (client
->has_lock
== FALSE
|| client
->pinentry
->status
!= PINENTRY_NONE
)
154 CACHE_LOCK(client
->ctx
);
156 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
162 pth_mutex_release(m
);
163 client
->has_lock
= FALSE
;
166 gpg_error_t
lock_file_mutex(struct client_s
*client
)
170 if (client
->has_lock
== TRUE
)
173 CACHE_LOCK(client
->ctx
);
175 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
182 if (pth_mutex_acquire(m
, TRUE
, NULL
) == FALSE
) {
183 if (errno
== EBUSY
) {
185 assuan_write_status(client
->ctx
, "LOCKED", N_("Waiting for lock"));
187 pth_mutex_acquire(m
, FALSE
, NULL
);
191 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(errno
));
192 return gpg_error_from_errno(e
);
196 client
->has_lock
= TRUE
;
200 void free_client(struct client_s
*client
)
203 xmlFreeDoc(client
->doc
);
206 gcry_free(client
->xml
);
208 if (client
->filename
)
209 g_free(client
->filename
);
212 gcry_cipher_close(client
->gh
);
215 static void cleanup_client(struct client_s
*client
)
217 assuan_context_t ctx
= client
->ctx
;
218 struct pinentry_s
*pin
= client
->pinentry
;
220 unlock_file_mutex(client
);
221 CACHE_LOCK(client
->ctx
);
222 cache_decr_refcount(client
->md5file
);
225 * This may be a new file so don't use a cache slot. save_command() will
226 * set this to FALSE on success.
228 if (client
->new == TRUE
)
229 cache_clear(client
->md5file
, 1);
232 memset(client
, 0, sizeof(struct client_s
));
233 client
->state
= STATE_CONNECTED
;
235 client
->freed
= TRUE
;
236 client
->pinentry
= pin
;
240 static gchar
*print_fmt(const char *fmt
, ...)
243 static gchar buf
[ASSUAN_LINELENGTH
] = {0};
246 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
251 gboolean
do_decompress(assuan_context_t ctx
, gpointer in
, gint insize
,
252 gpointer
*out
, glong
*outsize
, gint
*error
)
265 z
.avail_out
= zlib_bufsize
;
266 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
269 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
270 *error
= Z_MEM_ERROR
;
274 ret
= inflateInit2(&z
, 47);
277 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
282 memset(&h
, 0, sizeof(gz_header
));
283 h
.comment
= (guchar
*)buf
;
284 h
.comm_max
= sizeof(buf
);
285 ret
= inflateGetHeader(&z
, &h
);
288 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
294 ret
= inflate(&z
, Z_BLOCK
);
297 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
304 insize
= atoi((gchar
*)h
.comment
);
309 ret
= inflate(&z
, Z_FINISH
);
316 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
324 z
.next_out
= pout
+ z
.total_out
;
325 z
.avail_out
= zlib_bufsize
;
328 rc
= assuan_write_status(ctx
, "DECOMPRESS",
329 print_fmt("%i %i", z
.total_out
, insize
));
346 } while (ret
!= Z_STREAM_END
);
349 rc
= assuan_write_status(ctx
, "DECOMPRESS",
350 print_fmt("%i %i", z
.total_out
, insize
));
359 *outsize
= z
.total_out
;
364 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
371 static gpg_error_t
read_file_header(const gchar
*filename
, file_header_t
*fh
)
376 fd
= open(filename
, O_RDONLY
);
379 return gpg_error_from_errno(errno
);
381 len
= pth_read(fd
, fh
, sizeof(file_header_t
));
384 if (len
!= sizeof(file_header_t
))
385 return gpg_error_from_errno(errno
);
390 static gpg_error_t
open_command_finalize(assuan_context_t ctx
, guchar shakey
[],
393 struct client_s
*client
= assuan_get_pointer(ctx
);
399 if ((fd
= open_file(client
->filename
, &st
)) == -1) {
401 if (errno
== ENOENT
) {
409 log_write("%s: %s", client
->filename
, strerror(errno
));
410 cleanup_client(client
);
411 memset(shakey
, 0, sizeof(shakey
));
412 return send_syserror(ctx
, error
);
415 error
= try_xml_decrypt(ctx
, fd
, st
, shakey
);
419 memset(shakey
, 0, sizeof(shakey
));
420 cleanup_client(client
);
421 return send_error(ctx
, error
);
425 CACHE_LOCK(client
->ctx
);
427 if (cached
== FALSE
) {
428 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
429 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
430 cleanup_client(client
);
432 return send_error(ctx
, EPWMD_MAX_SLOTS
);
435 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
436 cache_reset_timeout(client
->md5file
, timeout
);
439 cache_set_timeout(client
->md5file
, -2);
444 memset(shakey
, 0, sizeof(shakey
));
445 error
= parse_xml(ctx
);
448 gcry_free(client
->xml
);
453 if (client
->new == FALSE
)
454 send_cache_status_all();
456 client
->state
= STATE_OPEN
;
459 return send_error(ctx
, error
);
462 static int open_command(assuan_context_t ctx
, char *line
)
465 guchar shakey
[gcrykeysize
];
466 gboolean cached
= FALSE
;
468 struct client_s
*client
= assuan_get_pointer(ctx
);
470 gchar
*filename
= NULL
;
471 file_header_t file_header
;
473 memset(shakey
, 0, sizeof(shakey
));
475 if ((req
= split_input_line(line
, " ", 2)) != NULL
)
478 if (!filename
|| !*filename
) {
480 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
483 if (valid_filename(filename
) == FALSE
) {
485 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
488 if (client
->state
== STATE_OPEN
)
489 cleanup_client(client
);
491 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
492 CACHE_LOCK(client
->ctx
);
494 if (cache_has_file(client
->md5file
) == FALSE
) {
495 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
498 return send_error(ctx
, EPWMD_MAX_SLOTS
);
502 cache_incr_refcount(client
->md5file
);
504 error
= lock_file_mutex(client
);
508 return send_error(ctx
, error
);
511 client
->freed
= FALSE
;
513 if ((error
= gcry_cipher_open(&client
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
515 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
516 cleanup_client(client
);
517 return send_error(ctx
, error
);
520 if (stat(filename
, &st
) == 0) {
521 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
522 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
524 cleanup_client(client
);
525 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
528 client
->mtime
= st
.st_mtime
;
531 client
->pinentry
->filename
= client
->filename
= g_strdup(filename
);
533 if (!client
->filename
) {
534 memset(shakey
, 0, sizeof(shakey
));
535 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
536 cleanup_client(client
);
538 return send_syserror(ctx
, ENOMEM
);
542 * New files don't need a key.
544 if (access(filename
, R_OK
|W_OK
) != 0) {
545 if (errno
!= ENOENT
) {
547 log_write("%s: %s", filename
, strerror(errno
));
549 cleanup_client(client
);
550 return send_syserror(ctx
, error
);
553 if ((client
->xml
= new_document()) == NULL
) {
554 log_write("%s", strerror(ENOMEM
));
556 cleanup_client(client
);
557 return send_syserror(ctx
, ENOMEM
);
560 client
->len
= xmlStrlen(client
->xml
);
562 client
->filename
= g_strdup(filename
);
564 if (!client
->filename
) {
566 cleanup_client(client
);
567 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
568 return send_syserror(ctx
, ENOMEM
);
571 memset(shakey
, 0, sizeof(shakey
));
573 if (req
[1] && *req
[1])
574 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
577 return open_command_finalize(ctx
, shakey
, cached
);
580 error
= read_file_header(filename
, &file_header
);
584 cleanup_client(client
);
585 return send_error(ctx
, error
);
588 if (file_header
.iter
== -1)
591 CACHE_LOCK(client
->ctx
);
592 cached
= cache_get_key(client
->md5file
, shakey
);
595 if (cached
== FALSE
) {
597 * No key specified and no matching filename found in the cache. Use
598 * pinentry to retrieve the key. Cannot return assuan_process_done()
599 * here otherwise the command will be interrupted. The event loop in
600 * client_thread() will poll the file descriptor waiting for it to
601 * become ready to read a pinentry_key_s which will contain the
602 * entered key or error. It will then call open_command_finalize() to
603 * to finish the command.
605 if (!req
[1] || !*req
[1]) {
607 if (client
->pinentry
->use
== FALSE
) {
608 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
613 client
->pinentry
->which
= PINENTRY_OPEN
;
614 error
= pinentry_fork(ctx
);
617 cleanup_client(client
);
618 return send_error(ctx
, error
);
621 client
->pinentry
->cb
= open_command_finalize
;
622 client
->pinentry
->status
= PINENTRY_INIT
;
625 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
630 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
635 return open_command_finalize(ctx
, shakey
, cached
);
638 gboolean
do_compress(assuan_context_t ctx
, gint level
, gpointer data
,
639 gint size
, gpointer
*out
, glong
*outsize
, gint
*error
)
646 gint cmd
= Z_NO_FLUSH
;
651 z
.next_in
= pin
= data
;
652 z
.avail_in
= size
< zlib_bufsize
? size
: zlib_bufsize
;
653 z
.avail_out
= zlib_bufsize
;
654 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
657 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
658 *error
= Z_MEM_ERROR
;
662 ret
= deflateInit2(&z
, level
, Z_DEFLATED
, 31, 8, Z_DEFAULT_STRATEGY
);
665 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
671 memset(&h
, 0, sizeof(gz_header
));
672 snprintf(buf
, sizeof(buf
), "%i", size
);
673 h
.comment
= (guchar
*)buf
;
674 ret
= deflateSetHeader(&z
, &h
);
677 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
687 ret
= deflate(&z
, cmd
);
694 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
702 z
.next_out
= pout
+ z
.total_out
;
703 z
.avail_out
= zlib_bufsize
;
706 if (!z
.avail_in
&& z
.total_in
< size
) {
707 if (z
.total_in
+ zlib_bufsize
> size
)
708 z
.avail_in
= size
- z
.total_in
;
710 z
.avail_in
= zlib_bufsize
;
713 rc
= assuan_write_status(ctx
, "COMPRESS",
714 print_fmt("%i %i", z
.total_in
, size
));
723 if (z
.total_in
>= size
)
734 } while (ret
!= Z_STREAM_END
);
737 rc
= assuan_write_status(ctx
, "COMPRESS",
738 print_fmt("%i %i", z
.total_in
, size
));
747 *outsize
= z
.total_out
;
752 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
759 gpg_error_t
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
760 const gchar
*filename
, gpointer data
, size_t insize
, guchar
*shakey
,
766 guchar tkey
[gcrykeysize
];
770 guint iter_progress
= 0, n_iter
= 0, xiter
= 0;
771 gchar tmp
[FILENAME_MAX
];
772 file_header_t file_header
;
776 * cache_file_count() needs both .used == TRUE and a valid key in
777 * order for it to count as a used cache entry. Fixes CACHE status
780 memset(shakey
, '!', gcrykeysize
);
782 file_header
.iter
= iter
;
786 if (insize
/ gcryblocksize
) {
787 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
789 if (insize
% gcryblocksize
)
790 len
+= gcryblocksize
;
794 * Resize the existing xml buffer to the block size required by gcrypt
795 * rather than duplicating it and wasting memory.
797 inbuf
= gcry_realloc(data
, len
);
800 return gpg_error_from_errno(ENOMEM
);
803 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
804 memcpy(tkey
, shakey
, sizeof(tkey
));
807 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
809 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
813 file_header
.iter
= iter
;
816 iter_progress
= get_key_file_integer("default", "iteration_progress");
818 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
819 error
= assuan_write_status(client
->ctx
, "ENCRYPT", "0");
827 while (xiter
< file_header
.iter
) {
828 if (client
&& iter_progress
> 0 && xiter
>= iter_progress
) {
829 if (!(xiter
% iter_progress
)) {
830 error
= assuan_write_status(client
->ctx
, "ENCRYPT", print_fmt("%i",
831 ++n_iter
* iter_progress
));
840 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
841 sizeof(file_header
.iv
)))) {
843 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
847 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
850 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
858 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
859 sizeof(file_header
.iv
)))) {
861 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
865 if ((rc
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
867 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
871 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
873 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
877 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
878 error
= assuan_write_status(client
->ctx
, "ENCRYPT",
879 print_fmt("%i", file_header
.iter
));
889 g_snprintf(tmp
, sizeof(tmp
), ".%s.tmp", filename
);
891 if ((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
894 p
= strrchr(tmp
, '/');
896 log_write("%s: %s", p
, strerror(errno
));
897 return gpg_error_from_errno(error
);
902 * xml_import() from command line.
906 len
= pth_write(fd
, &file_header
, sizeof(file_header_t
));
908 if (len
!= sizeof(file_header
)) {
915 return gpg_error_from_errno(len
);
918 len
= pth_write(fd
, inbuf
, insize
);
927 return gpg_error_from_errno(len
);
930 if (fsync(fd
) == -1) {
934 return gpg_error_from_errno(len
);
943 if (stat(filename
, &st
) == 0)
944 mode
= st
.st_mode
& (S_IRWXU
|S_IRWXG
|S_IRWXO
);
946 if (rename(tmp
, filename
) == -1) {
949 return gpg_error_from_errno(len
);
953 chmod(filename
, mode
);
960 static gpg_error_t
save_command_finalize(assuan_context_t ctx
,
961 guchar shakey
[], gboolean cached
)
963 struct client_s
*client
= assuan_get_pointer(ctx
);
975 xmlDocDumpFormatMemory(client
->doc
, &p
, &len
, 0);
978 iter
= get_key_file_integer(client
->filename
, "compression_level");
983 if (do_compress(ctx
, iter
, xmlbuf
, len
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
984 memset(shakey
, 0, sizeof(shakey
));
987 if (zerror
== Z_MEM_ERROR
) {
988 return send_syserror(ctx
, ENOMEM
);
991 return send_error(ctx
, GPG_ERR_COMPR_ALGO
);
999 iter
= get_key_file_integer(client
->filename
, "iterations");
1000 error
= do_xml_encrypt(client
, client
->gh
, client
->filename
, xmlbuf
, len
, shakey
, iter
);
1003 memset(shakey
, 0, sizeof(shakey
));
1004 return send_error(ctx
, error
);
1007 stat(client
->filename
, &st
);
1008 client
->mtime
= st
.st_mtime
;
1009 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
1010 CACHE_LOCK(client
->ctx
);
1013 memset(shakey
, 0, sizeof(shakey
));
1014 cache_reset_timeout(client
->md5file
, timeout
);
1017 if (client
->new == TRUE
)
1018 send_cache_status_all();
1020 client
->new = FALSE
;
1021 return send_error(ctx
, 0);
1024 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
1025 memset(shakey
, 0, sizeof(shakey
));
1026 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
1028 return send_error(ctx
, EPWMD_MAX_SLOTS
);
1031 client
->new = FALSE
;
1032 memset(shakey
, 0, sizeof(shakey
));
1033 cache_reset_timeout(client
->md5file
, timeout
);
1035 send_cache_status_all();
1036 return send_error(ctx
, 0);
1039 static int save_command(assuan_context_t ctx
, char *line
)
1041 gboolean cached
= FALSE
;
1042 guchar shakey
[gcrykeysize
];
1044 struct client_s
*client
= assuan_get_pointer(ctx
);
1047 memset(shakey
, 0, sizeof(shakey
));
1048 error
= file_modified(client
);
1051 log_write("%s: %s", client
->filename
? client
->filename
: "",
1052 pwmd_strerror(error
));
1053 return send_error(ctx
, error
);
1056 error
= lock_file_mutex(client
);
1059 return send_error(ctx
, error
);
1061 if (stat(client
->filename
, &st
) == -1 && errno
!= ENOENT
)
1062 return send_syserror(ctx
, errno
);
1064 if (errno
!= ENOENT
&& !S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
1065 log_write("%s: %s", client
->filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
1066 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
1069 if (get_key_file_integer(client
->filename
, "iterations") == -1)
1072 if (!line
|| !*line
) {
1073 guchar tmp
[sizeof(shakey
)];
1076 memset(tmp
, '!', sizeof(tmp
));
1078 if (cache_get_key(client
->md5file
, shakey
) == FALSE
||
1079 memcmp(shakey
, tmp
, sizeof(shakey
)) == 0) {
1081 #ifdef WITH_PINENTRY
1082 if (client
->pinentry
->use
== FALSE
) {
1083 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1087 client
->pinentry
->which
= PINENTRY_SAVE
;
1088 error
= pinentry_fork(ctx
);
1091 return send_error(ctx
, error
);
1093 client
->pinentry
->cb
= save_command_finalize
;
1094 client
->pinentry
->status
= PINENTRY_INIT
;
1097 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1107 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, line
, strlen(line
));
1108 memset(line
, 0, strlen(line
));
1112 return save_command_finalize(ctx
, shakey
, cached
);
1115 static gboolean
contains_whitespace(const gchar
*str
)
1117 const gchar
*p
= str
;
1121 len
= g_utf8_strlen(p
++, -1) -1;
1124 c
= g_utf8_get_char(p
++);
1126 if (g_unichar_isspace(c
))
1133 static int delete_command(assuan_context_t ctx
, char *line
)
1135 struct client_s
*client
= assuan_get_pointer(ctx
);
1140 error
= file_modified(client
);
1143 log_write("%s: %s", client
->filename
? client
->filename
: "",
1144 pwmd_strerror(error
));
1145 return send_error(ctx
, error
);
1148 if (strchr(line
, '\t'))
1149 req
= split_input_line(line
, "\t", -1);
1151 req
= split_input_line(line
, " ", -1);
1154 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1156 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1160 return send_error(ctx
, error
);
1164 * No sub-node defined. Remove the entire node (account).
1173 return send_error(ctx
, 0);
1176 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1180 return send_error(ctx
, error
);
1187 return send_error(ctx
, 0);
1191 * Don't return with assuan_process_done() here. This has been called from
1192 * assuan_process_next() and the command should be finished in
1195 static int store_command_finalize(gpointer data
, gint rc
, guchar
*line
,
1198 assuan_context_t ctx
= data
;
1199 struct client_s
*client
= assuan_get_pointer(ctx
);
1201 guchar
*result
= line
;
1203 gpg_error_t error
= file_modified(client
);
1215 req
= split_input_line((gchar
*)result
, "\t", 0);
1230 return EPWMD_COMMAND_SYNTAX
;
1232 if (valid_xml_element((xmlChar
*)*req
) == FALSE
) {
1234 return EPWMD_INVALID_ELEMENT
;
1237 if (valid_element_path(req
+1, TRUE
) == FALSE
) {
1239 return EPWMD_INVALID_ELEMENT
;
1243 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1245 if (error
&& error
== EPWMD_ELEMENT_NOT_FOUND
) {
1246 error
= new_account(client
->doc
, *req
);
1263 create_elements_cb(n
, req
+1, &error
, NULL
);
1265 find_elements(client
->doc
, n
->children
, req
+1, &error
,
1266 NULL
, NULL
, create_elements_cb
, FALSE
, 0, NULL
);
1270 client
->inquire_status
= INQUIRE_DONE
;
1274 static int store_command(assuan_context_t ctx
, char *line
)
1276 struct client_s
*client
= assuan_get_pointer(ctx
);
1277 gpg_error_t error
= file_modified(client
);
1280 log_write("%s: %s", client
->filename
? client
->filename
: "",
1281 pwmd_strerror(error
));
1282 return send_error(ctx
, error
);
1285 error
= assuan_inquire_ext(ctx
, "STORE", 0, store_command_finalize
, ctx
);
1288 return send_error(ctx
, error
);
1290 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1291 client
->inquire_status
= INQUIRE_BUSY
;
1295 static int get_command(assuan_context_t ctx
, char *line
)
1297 struct client_s
*client
= assuan_get_pointer(ctx
);
1302 error
= file_modified(client
);
1305 log_write("%s: %s", client
->filename
? client
->filename
: "",
1306 pwmd_strerror(error
));
1307 return send_error(ctx
, error
);
1310 req
= split_input_line(line
, "\t", -1);
1312 if (!req
|| !*req
) {
1314 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1317 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1321 return send_error(ctx
, error
);
1325 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1330 return send_error(ctx
, error
);
1332 if (!n
|| !n
->children
)
1333 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1337 if (!n
|| !n
->content
|| !*n
->content
)
1338 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1340 error
= assuan_send_data(ctx
, n
->content
, xmlStrlen(n
->content
));
1341 return send_error(ctx
, error
);
1344 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
)
1353 for (n
= 0; *p
&& n
< 3; p
++) {
1358 if (strstr((gchar
*)p
, "text()") != NULL
)
1359 p
[xmlStrlen(p
) - 7] = 0;
1361 for (n
= 0; p
[n
]; n
++) {
1366 buf
= g_strdup_printf("%s\t%s", account
, p
);
1367 p
= (xmlChar
*)buf
+ strlen(buf
) - 1;
1375 gboolean
strv_printf(gchar
***array
, const gchar
*fmt
, ...)
1380 gint len
= *array
? g_strv_length(*array
) : 0;
1386 if ((a
= g_realloc(*array
, (len
+ 2) * sizeof(gchar
*))) == NULL
)
1390 ret
= g_vasprintf(&buf
, fmt
, ap
);
1406 static xmlNodePtr
realpath_elements_cb(xmlNodePtr node
, gchar
**req
,
1407 gpg_error_t
*error
, void *data
)
1409 struct realpath_s
*rp
= data
;
1412 g_free(rp
->account
);
1414 rp
->account
= g_strdup(req
[0]);
1417 *error
= gpg_error_from_errno(ENOMEM
);
1424 static int realpath_command(assuan_context_t ctx
, char *line
)
1427 struct client_s
*client
= assuan_get_pointer(ctx
);
1433 struct realpath_s
*rp
;
1436 error
= file_modified(client
);
1439 log_write("%s: %s", client
->filename
? client
->filename
: "",
1440 pwmd_strerror(error
));
1441 return send_error(ctx
, error
);
1444 if (strchr(line
, '\t') != NULL
) {
1445 if ((req
= split_input_line(line
, "\t", 0)) == NULL
)
1446 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1449 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1450 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1453 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1457 return send_error(ctx
, error
);
1460 rp
= g_malloc(sizeof(struct realpath_s
));
1464 return send_syserror(ctx
, ENOMEM
);
1467 rp
->account
= g_strdup(req
[0]);
1471 return send_syserror(ctx
, ENOMEM
);
1475 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
,
1476 NULL
, realpath_elements_cb
, NULL
, FALSE
, 0, rp
);
1479 g_free(rp
->account
);
1482 return send_error(ctx
, error
);
1486 p
= xmlGetNodePath(n
);
1487 result
= element_path_to_req(rp
->account
, p
);
1491 g_free(rp
->account
);
1495 return send_syserror(ctx
, ENOMEM
);
1498 string
= g_string_new(result
);
1500 g_free(rp
->account
);
1507 for (t
= string
->str
+ i
; *t
; t
++, i
++) {
1508 if ((!i
&& *t
!= '!') || *t
== '\t') {
1509 string
= g_string_insert_c(string
, !i
? i
++ : ++i
, '!');
1514 error
= assuan_send_data(ctx
, string
->str
, string
->len
);
1515 g_string_free(string
, TRUE
);
1516 return send_error(ctx
, error
);
1519 struct list_element_s
{
1524 static gboolean
append_to_element_list(struct list_element_s
*elements
)
1531 if (!elements
|| !elements
->elements
)
1534 tmp
= g_strjoinv("\t", elements
->elements
);
1539 g_strfreev(elements
->elements
);
1540 elements
->elements
= NULL
;
1541 total
= g_slist_length(elements
->list
);
1542 a
= g_utf8_collate_key(tmp
, -1);
1550 * Removes duplicate element paths from the list. This is needed when
1551 * appending an element tree from list_command(). The glib docs recommend
1552 * using g_utf8_collate_key() for a large number of strings.
1554 for (i
= 0; i
< total
; i
++) {
1555 gchar
*p
= g_slist_nth_data(elements
->list
, i
);
1556 gchar
*b
= g_utf8_collate_key(p
, -1);
1564 if (strcmp(a
, b
) == 0) {
1575 list
= g_slist_append(elements
->list
, tmp
);
1580 elements
->list
= list
;
1584 static gpg_error_t
do_list_recurse(xmlDocPtr doc
, xmlNodePtr node
,
1585 struct list_element_s
*elements
, gchar
*prefix
)
1590 if (append_to_element_list(elements
) == FALSE
)
1591 return gpg_error_from_errno(ENOMEM
);
1593 for (n
= node
; n
; n
= n
->next
) {
1594 if (n
->type
== XML_ELEMENT_NODE
) {
1595 xmlChar
*content
= node_has_attribute(n
, (xmlChar
*)"target");
1599 if (strv_printf(&elements
->elements
, "%s\t%s", prefix
, n
->name
) == FALSE
)
1600 return gpg_error_from_errno(ENOMEM
);
1602 if (append_to_element_list(elements
) == FALSE
)
1603 return gpg_error_from_errno(ENOMEM
);
1606 tmp
= g_strdup_printf("%s\t!%s", prefix
, n
->name
);
1609 return gpg_error_from_errno(ENOMEM
);
1611 if (strv_printf(&elements
->elements
, "%s", tmp
) == FALSE
) {
1613 return gpg_error_from_errno(ENOMEM
);
1617 error
= do_list_recurse(doc
, n
->children
, elements
, tmp
);
1620 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
1626 if (append_to_element_list(elements
) == FALSE
)
1627 return gpg_error_from_errno(ENOMEM
);
1634 static gpg_error_t
do_list_command(assuan_context_t ctx
, xmlDocPtr doc
,
1635 struct list_element_s
*elements
, char *line
)
1637 gchar
*prefix
= NULL
, *account
;
1638 gchar
**req
= NULL
, **oreq
= NULL
, *tmp
;
1640 gboolean account_is_literal
, account_has_target
= FALSE
;
1645 if ((req
= split_input_line(line
, "\t", 0)) == NULL
) {
1646 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1647 return EPWMD_COMMAND_SYNTAX
;
1650 prefix
= g_strdup(*req
);
1654 return gpg_error_from_errno(ENOMEM
);
1657 account
= g_strdup(*req
);
1662 return gpg_error_from_errno(ENOMEM
);
1665 oreq
= g_strdupv(req
);
1671 return gpg_error_from_errno(ENOMEM
);
1676 account_has_target
= FALSE
;
1677 account_is_literal
= is_literal_element_str(prefix
);
1678 n
= find_account(doc
, &p
, &error
, &account_has_target
, 0);
1688 if (!which
&& account_is_literal
== FALSE
&& account_has_target
== FALSE
) {
1689 tmp
= g_strdup_printf("!%s", prefix
);
1692 error
= gpg_error_from_errno(ENOMEM
);
1703 n
= find_elements(doc
, n
->children
, p
+1, &error
, NULL
, NULL
, NULL
,
1709 tmp
= g_strjoinv("\t", p
+1);
1711 error
= gpg_error_from_errno(ENOMEM
);
1715 t
= g_strdup_printf("%s\t%s", prefix
, tmp
);
1717 error
= gpg_error_from_errno(ENOMEM
);
1726 if (strv_printf(&elements
->elements
, "%s", prefix
) == FALSE
) {
1727 error
= gpg_error_from_errno(ENOMEM
);
1731 if (node_has_child_element(n
->children
) == FALSE
) {
1732 if (append_to_element_list(elements
) == FALSE
) {
1733 error
= gpg_error_from_errno(ENOMEM
);
1738 error
= do_list_recurse(doc
, n
->children
, elements
, prefix
);
1743 if (!which
++ && !*(p
+1) && account_is_literal
== FALSE
&& account_has_target
== TRUE
) {
1745 *oreq
= g_strdup_printf("!%s", account
);
1748 error
= gpg_error_from_errno(ENOMEM
);
1754 prefix
= g_strdup(*oreq
);
1757 error
= gpg_error_from_errno(ENOMEM
);
1776 * This could be faster especially when finding "target" attributes.
1778 static int list_command(assuan_context_t ctx
, char *line
)
1780 struct client_s
*client
= assuan_get_pointer(ctx
);
1782 struct list_element_s
*elements
= NULL
;
1787 if (disable_list_and_dump
== TRUE
)
1788 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
1790 error
= file_modified(client
);
1793 log_write("%s: %s", client
->filename
, pwmd_strerror(error
));
1794 return send_error(ctx
, error
);
1800 error
= list_accounts(client
->doc
, &str
);
1803 return send_error(ctx
, error
);
1805 error
= assuan_send_data(ctx
, str
->str
, str
->len
);
1806 g_string_free(str
, TRUE
);
1807 return send_error(ctx
, error
);
1810 elements
= g_malloc0(sizeof(struct list_element_s
));
1813 error
= gpg_error_from_errno(ENOMEM
);
1817 error
= do_list_command(ctx
, client
->doc
, elements
, line
);
1823 total
= g_slist_length(elements
->list
);
1826 error
= EPWMD_EMPTY_ELEMENT
;
1831 * Find element paths with a target and append those element trees to
1834 for (i
= 0; i
< total
; i
++) {
1837 tmp
= g_slist_nth_data(elements
->list
, i
);
1838 req
= split_input_line(tmp
, "\t", 0);
1841 if (g_str_has_prefix(tmp
, "!") == TRUE
) {
1849 for (p
= req
; *p
; p
++) {
1850 if (g_str_has_prefix(*p
, "!") == FALSE
)
1861 error
= do_list_command(ctx
, client
->doc
, elements
, tmp
);
1863 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
1866 total
= g_slist_length(elements
->list
);
1870 string
= g_string_new(NULL
);
1872 for (i
= 0; i
< total
; i
++) {
1873 tmp
= g_slist_nth_data(elements
->list
, i
);
1874 g_string_append_printf(string
, "%s\n", tmp
);
1878 string
= g_string_truncate(string
, string
->len
- 1);
1879 error
= assuan_send_data(ctx
, string
->str
, string
->len
);
1880 g_string_free(string
, TRUE
);
1885 g_slist_free(elements
->list
);
1887 if (elements
->elements
)
1888 g_strfreev(elements
->elements
);
1893 return send_error(ctx
, error
);
1896 static gpg_error_t
add_attribute(xmlNodePtr node
, const gchar
*name
,
1901 if ((a
= xmlHasProp(node
, (xmlChar
*)name
)) == NULL
) {
1902 a
= xmlNewProp(node
, (xmlChar
*)name
, (xmlChar
*)value
);
1905 return EPWMD_LIBXML_ERROR
;
1908 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1914 * req[0] - element path
1916 static int attribute_list(assuan_context_t ctx
, gchar
**req
)
1918 struct client_s
*client
= assuan_get_pointer(ctx
);
1919 gchar
**attrlist
= NULL
;
1921 gchar
**path
= NULL
;
1927 if (!req
|| !req
[0])
1928 return EPWMD_COMMAND_SYNTAX
;
1930 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1932 * The first argument may be only an account.
1934 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
1935 return EPWMD_COMMAND_SYNTAX
;
1938 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1946 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1947 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1957 for (a
= n
->properties
; a
; a
= a
->next
) {
1960 if ((pa
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1962 g_strfreev(attrlist
);
1965 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(errno
));
1966 return gpg_error_from_errno(error
);
1971 attrlist
[i
] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1974 g_strfreev(attrlist
);
1975 return gpg_error_from_errno(ENOMEM
);
1978 attrlist
[++i
] = NULL
;
1982 return EPWMD_EMPTY_ELEMENT
;
1984 line
= g_strjoinv("\n", attrlist
);
1987 g_strfreev(attrlist
);
1988 return gpg_error_from_errno(ENOMEM
);
1991 error
= assuan_send_data(ctx
, line
, strlen(line
));
1993 g_strfreev(attrlist
);
1998 * req[0] - attribute
1999 * req[1] - element path
2001 static int attribute_delete(struct client_s
*client
, gchar
**req
)
2005 gchar
**path
= NULL
;
2008 if (!req
|| !req
[0] || !req
[1])
2009 return EPWMD_COMMAND_SYNTAX
;
2011 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2013 * The first argument may be only an account.
2015 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2016 return EPWMD_COMMAND_SYNTAX
;
2020 * Don't remove the "name" attribute for the account element. To remove an
2021 * account use DELETE <account>.
2023 if (!path
[1] && xmlStrEqual((xmlChar
*)req
[0], (xmlChar
*)"name")) {
2024 error
= EPWMD_ATTR_SYNTAX
;
2028 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2034 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2035 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2043 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
)
2044 return EPWMD_ATTR_NOT_FOUND
;
2046 if (xmlRemoveProp(a
) == -1)
2047 return EPWMD_LIBXML_ERROR
;
2057 * Creates a "target" attribute. When other commands encounter an element with
2058 * this attribute, the element path is modified to the target value. If the
2059 * source element path doesn't exist when using 'ATTR SET target', it is
2060 * created, but the destination element path must exist.
2062 * req[0] - source element path
2063 * req[1] - destination element path
2065 static gpg_error_t
target_attribute(struct client_s
*client
, gchar
**req
)
2067 gchar
**src
, **dst
, *line
= NULL
;
2071 if (!req
|| !req
[0] || !req
[1])
2072 return EPWMD_COMMAND_SYNTAX
;
2074 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
2076 * The first argument may be only an account.
2078 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
)
2079 return EPWMD_COMMAND_SYNTAX
;
2082 if (valid_element_path(src
, FALSE
) == FALSE
) {
2084 return EPWMD_INVALID_ELEMENT
;
2087 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2089 * The first argument may be only an account.
2091 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
2092 error
= EPWMD_COMMAND_SYNTAX
;
2097 n
= find_account(client
->doc
, &dst
, &error
, NULL
, 0);
2100 * Make sure the destination element path exists.
2106 n
= find_elements(client
->doc
, n
->children
, dst
+1, &error
,
2107 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2114 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
2117 if (error
== EPWMD_ELEMENT_NOT_FOUND
) {
2118 error
= new_account(client
->doc
, src
[0]);
2131 n
= create_target_elements_cb(n
, src
+1, &error
, NULL
);
2133 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
2134 NULL
, NULL
, create_target_elements_cb
, FALSE
, 0, NULL
);
2140 * Reset the position of the element tree now that the elements
2141 * have been created.
2143 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
2148 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
2149 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2155 line
= g_strjoinv("\t", dst
);
2156 error
= add_attribute(n
, "target", line
);
2166 * req[0] - account name
2169 static gpg_error_t
name_attribute(struct client_s
*client
, gchar
**req
)
2175 tmp
= g_strdupv(req
);
2178 return gpg_error_from_errno(ENOMEM
);
2180 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2186 if (g_utf8_collate(req
[0], req
[1]) == 0)
2190 * Will not overwrite an existing account.
2192 tmp
= g_strdupv(req
+1);
2195 return gpg_error_from_errno(ENOMEM
);
2197 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2200 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
2204 return EPWMD_ACCOUNT_EXISTS
;
2207 * Whitespace not allowed in account names.
2209 if (contains_whitespace(req
[1]) == TRUE
)
2210 return EPWMD_ATTR_SYNTAX
;
2212 tmp
= g_strdupv(req
);
2215 return gpg_error_from_errno(ENOMEM
);
2217 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2221 return EPWMD_ELEMENT_NOT_FOUND
;
2223 return add_attribute(n
, "name", req
[1]);
2227 * req[0] - attribute
2228 * req[1] - element path
2230 static int attribute_get(assuan_context_t ctx
, gchar
**req
)
2232 struct client_s
*client
= assuan_get_pointer(ctx
);
2238 if (!req
|| !req
[0] || !req
[1])
2239 return EPWMD_COMMAND_SYNTAX
;
2241 if (strchr(req
[1], '\t')) {
2242 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
)
2243 return EPWMD_COMMAND_SYNTAX
;
2246 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2247 return EPWMD_COMMAND_SYNTAX
;
2250 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2256 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2257 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2265 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
)
2266 return EPWMD_ATTR_NOT_FOUND
;
2268 error
= assuan_send_data(ctx
, a
, xmlStrlen(a
));
2278 * req[0] - attribute
2279 * req[1] - element path
2282 static int attribute_set(struct client_s
*client
, gchar
**req
)
2284 gchar
**path
= NULL
;
2288 if (!req
|| !req
[0] || !req
[1] || !req
[2])
2289 return EPWMD_COMMAND_SYNTAX
;
2292 * Reserved attribute names.
2294 if (g_utf8_collate(req
[0], "name") == 0) {
2296 * Only reserved for the account element. Not the rest of the
2299 if (strchr(req
[1], '\t') == NULL
)
2300 return name_attribute(client
, req
+ 1);
2302 else if (g_utf8_collate(req
[0], "target") == 0)
2303 return target_attribute(client
, req
+ 1);
2305 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2307 * The first argument may be only an account.
2309 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2310 return EPWMD_COMMAND_SYNTAX
;
2313 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2319 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2320 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2327 return add_attribute(n
, req
[0], req
[2]);
2336 * req[1] - attribute name or element path if command is LIST
2337 * req[2] - element path
2338 * req[2] - element path or value
2340 static int attr_command(assuan_context_t ctx
, char *line
)
2342 struct client_s
*client
= assuan_get_pointer(ctx
);
2343 gchar
**req
= split_input_line(line
, " ", 4);
2344 gpg_error_t error
= 0;
2346 error
= file_modified(client
);
2349 log_write("%s: %s", client
->filename
? client
->filename
: "",
2350 pwmd_strerror(error
));
2352 return send_error(ctx
, error
);
2355 if (!req
|| !req
[0] || !req
[1]) {
2357 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2360 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
2361 error
= attribute_set(client
, req
+1);
2362 else if (g_ascii_strcasecmp(req
[0], "GET") == 0)
2363 error
= attribute_get(ctx
, req
+1);
2364 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
2365 error
= attribute_delete(client
, req
+1);
2366 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
2367 error
= attribute_list(ctx
, req
+1);
2369 error
= EPWMD_COMMAND_SYNTAX
;
2372 return send_error(ctx
, error
);
2375 static int iscached_command(assuan_context_t ctx
, char *line
)
2377 gchar
**req
= split_input_line(line
, " ", 0);
2380 if (!req
|| !*req
) {
2382 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2385 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2389 if (cache_iscached(md5file
) == FALSE
) {
2391 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2395 return send_error(ctx
, 0);
2398 gpg_error_t
send_cache_status(assuan_context_t ctx
)
2401 struct client_s
*client
= assuan_get_pointer(ctx
);
2403 CACHE_LOCK(client
->ctx
);
2404 tmp
= print_fmt("%i %i",
2406 (cache_size
/ sizeof(file_cache_t
)) - cache_file_count());
2409 return assuan_write_status(ctx
, "CACHE", tmp
);
2412 static int clearcache_command(assuan_context_t ctx
, char *line
)
2414 struct client_s
*client
= assuan_get_pointer(ctx
);
2415 gchar
**req
= split_input_line(line
, " ", 0);
2420 if (!req
|| !*req
) {
2422 cache_clear(client
->md5file
, 2);
2424 return send_error(ctx
, 0);
2427 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2430 if (cache_clear(md5file
, 1) == FALSE
) {
2432 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2436 return send_error(ctx
, 0);
2439 static int cachetimeout_command(assuan_context_t ctx
, char *line
)
2443 gchar
**req
= split_input_line(line
, " ", 0);
2445 struct client_s
*client
= assuan_get_pointer(ctx
);
2447 if (!req
|| !*req
|| !req
[1]) {
2449 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2453 timeout
= strtol(req
[0], &p
, 10);
2455 if (errno
!= 0 || *p
!= 0) {
2457 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2460 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2462 CACHE_LOCK(client
->ctx
);
2464 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2466 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2470 send_cache_status_all();
2471 return send_error(ctx
, 0);
2474 static int dump_command(assuan_context_t ctx
, char *line
)
2478 struct client_s
*client
= assuan_get_pointer(ctx
);
2481 if (disable_list_and_dump
== TRUE
)
2482 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2484 error
= file_modified(client
);
2487 log_write("%s: %s", client
->filename
? client
->filename
: "",
2488 pwmd_strerror(error
));
2489 return send_error(ctx
, error
);
2492 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2493 error
= assuan_send_data(ctx
, xml
, len
);
2495 return send_error(ctx
, error
);
2498 static int getconfig_command(assuan_context_t ctx
, gchar
*line
)
2500 struct client_s
*client
= assuan_get_pointer(ctx
);
2501 gpg_error_t error
= 0;
2504 if (strcmp(line
, "key") == 0 || strcmp(line
, "key_file") == 0)
2505 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2507 p
= get_key_file_string(client
->filename
? client
->filename
: "default", line
);
2510 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2512 tmp
= expand_homedir(p
);
2515 error
= assuan_send_data(ctx
, p
, strlen(p
));
2517 return send_error(ctx
, error
);
2520 void cleanup_assuan(assuan_context_t ctx
)
2522 struct client_s
*cl
= assuan_get_pointer(ctx
);
2527 static void reset_notify(assuan_context_t ctx
)
2529 struct client_s
*cl
= assuan_get_pointer(ctx
);
2534 static gpg_error_t
parse_client_option(assuan_context_t ctx
, const gchar
*line
)
2536 gchar name
[32] = {0}, value
[256] = {0};
2537 pth_attr_t attr
= pth_attr_of(pth_self());
2539 if (sscanf(line
, " %31[a-zA-Z] = %255c", name
, value
) != 2)
2540 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_SYNTAX
);
2542 if (g_strcasecmp(name
, (gchar
*)"NAME") == 0) {
2543 log_write("OPTION CLIENT %s", line
);
2544 pth_attr_set(attr
, PTH_ATTR_NAME
, value
);
2547 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_UNKNOWN_OPTION
);
2552 static int option_handler(assuan_context_t ctx
, const gchar
*name
,
2555 struct client_s
*client
= assuan_get_pointer(ctx
);
2557 if (!value
|| !*value
)
2558 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_INV_VALUE
);
2560 if (g_strcasecmp(name
, (gchar
*)"ttyname") == 0) {
2561 g_free(client
->pinentry
->ttyname
);
2562 client
->pinentry
->ttyname
= g_strdup(value
);
2564 else if (g_strcasecmp(name
, (gchar
*)"ttytype") == 0) {
2565 g_free(client
->pinentry
->ttytype
);
2566 client
->pinentry
->ttytype
= g_strdup(value
);
2568 else if (g_strcasecmp(name
, (gchar
*)"display") == 0) {
2569 g_free(client
->pinentry
->display
);
2570 client
->pinentry
->display
= g_strdup(value
);
2572 else if (g_strcasecmp(name
, (gchar
*)"path") == 0) {
2573 g_free(client
->pinentry
->path
);
2574 client
->pinentry
->path
= g_strdup(value
);
2576 else if (g_strcasecmp(name
, (gchar
*)"title") == 0) {
2577 g_free(client
->pinentry
->title
);
2578 client
->pinentry
->title
= g_strdup(value
);
2580 else if (g_strcasecmp(name
, (gchar
*)"prompt") == 0) {
2581 g_free(client
->pinentry
->prompt
);
2582 client
->pinentry
->prompt
= g_strdup(value
);
2584 else if (g_strcasecmp(name
, (gchar
*)"desc") == 0) {
2585 g_free(client
->pinentry
->desc
);
2586 client
->pinentry
->desc
= g_strdup(value
);
2588 else if (g_strcasecmp(name
, (gchar
*)"timeout") == 0) {
2590 gint n
= strtol(value
, &p
, 10);
2593 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_INV_VALUE
);
2595 client
->pinentry
->timeout
= n
;
2597 else if (g_strcasecmp(name
, (gchar
*)"pinentry") == 0) {
2599 gint n
= strtol(value
, &p
, 10);
2601 if (*p
|| n
< 0 || n
> 1)
2602 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_INV_VALUE
);
2604 client
->pinentry
->use
= n
== 0 ? FALSE
: TRUE
;
2606 else if (g_strcasecmp(name
, (gchar
*)"client") == 0)
2607 return parse_client_option(ctx
, value
);
2609 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_UNKNOWN_OPTION
);
2611 log_write("OPTION %s=%s", name
, value
);
2615 gpg_error_t
register_commands(assuan_context_t ctx
)
2619 int (*handler
)(assuan_context_t
, char *line
);
2621 { "OPEN", open_command
},
2622 { "SAVE", save_command
},
2623 { "LIST", list_command
},
2624 { "REALPATH", realpath_command
},
2625 { "STORE", store_command
},
2626 { "DELETE", delete_command
},
2627 { "GET", get_command
},
2628 { "ATTR", attr_command
},
2629 { "ISCACHED", iscached_command
},
2630 { "CLEARCACHE", clearcache_command
},
2631 { "CACHETIMEOUT", cachetimeout_command
},
2632 { "GETCONFIG", getconfig_command
},
2633 { "DUMP", dump_command
},
2640 for (i
=0; table
[i
].name
; i
++) {
2641 rc
= assuan_register_command (ctx
, table
[i
].name
, table
[i
].handler
);
2647 rc
= assuan_register_bye_notify(ctx
, cleanup_assuan
);
2651 rc
= assuan_register_option_handler(ctx
, option_handler
);
2655 return assuan_register_reset_notify(ctx
, reset_notify
);
2658 gpg_error_t
try_xml_decrypt(assuan_context_t ctx
, gint fd
, struct stat st
,
2664 guchar tkey
[gcrykeysize
];
2665 struct client_s
*client
= ctx
? assuan_get_pointer(ctx
) : NULL
;
2666 gcry_cipher_hd_t gh
;
2667 guint iter
= 0, n_iter
= 0;
2669 void *outbuf
= NULL
;
2673 file_header_t file_header
;
2676 error
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0);
2684 lseek(fd
, 0, SEEK_SET
);
2685 insize
= st
.st_size
- sizeof(file_header_t
);
2686 iv
= gcry_malloc(gcryblocksize
);
2690 gcry_cipher_close(gh
);
2692 return gpg_error_from_errno(ENOMEM
);
2695 len
= pth_read(fd
, &file_header
, sizeof(file_header_t
));
2697 if (len
!= sizeof(file_header_t
)) {
2701 gcry_cipher_close(gh
);
2705 return gpg_error_from_errno(errno
);
2708 /* No encryption iterations. This is a plain (gzipped) file. */
2709 if (file_header
.iter
== -1) {
2711 * cache_file_count() needs both .used == TRUE and a valid key in
2712 * order for it to count as a used cache entry. Fixes CACHE status
2715 memset(key
, '!', gcrykeysize
);
2716 insize
= st
.st_size
- sizeof(file_header_t
);
2719 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
2720 inbuf
= gcry_malloc(insize
);
2724 gcry_cipher_close(gh
);
2727 return gpg_error_from_errno(ENOMEM
);
2730 len
= pth_read(fd
, inbuf
, insize
);
2732 if (len
!= insize
) {
2736 gcry_cipher_close(gh
);
2740 return gpg_error_from_errno(errno
);
2743 if (file_header
.iter
== -1)
2746 memcpy(tkey
, key
, sizeof(tkey
));
2749 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2751 gcry_cipher_close(gh
);
2752 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2755 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2762 if ((error
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
2764 gcry_cipher_close(gh
);
2765 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2768 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2774 gcry_cipher_close(gh
);
2779 iter_progress
= get_key_file_integer("default", "iteration_progress");
2781 if (ctx
&& iter_progress
> 0 && file_header
.iter
>= iter_progress
) {
2782 error
= assuan_write_status(client
->ctx
, "DECRYPT", "0");
2791 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2799 if ((error
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
2801 gcry_cipher_close(gh
);
2802 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2805 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2812 while (iter
< file_header
.iter
) {
2813 if (ctx
&& iter_progress
> 0 && iter
>= iter_progress
) {
2814 if (!(iter
% iter_progress
)) {
2815 error
= assuan_write_status(ctx
, "DECRYPT", print_fmt("%i",
2816 ++n_iter
* iter_progress
));
2826 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2828 gcry_cipher_close(gh
);
2829 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2832 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2839 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2843 gcry_cipher_close(gh
);
2844 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2847 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2858 if (ctx
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
2859 error
= assuan_write_status(ctx
, "DECRYPT", print_fmt("%i", file_header
.iter
));
2871 if (do_decompress(ctx
, inbuf
, insize
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
2873 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2875 if (zerror
== Z_MEM_ERROR
) {
2877 return gpg_error_from_errno(ENOMEM
);
2879 else if (zerror
!= Z_DATA_ERROR
) {
2883 gcry_cipher_close(gh
);
2885 return EPWMD_BADKEY
;
2894 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
2898 gcry_cipher_close(gh
);
2900 return EPWMD_BADKEY
;
2904 client
->xml
= inbuf
;
2905 client
->len
= insize
;
2908 gcry_cipher_close(gh
);
2916 * This is called after every Assuan command.
2918 void command_finalize(assuan_context_t ctx
, gint error
)
2920 struct client_s
*client
= assuan_get_pointer(ctx
);
2922 unlock_file_mutex(client
);