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>
48 #include "pwmd_error.h"
52 static void *z_alloc(void *data
, unsigned items
, unsigned size
)
55 return gcry_calloc(items
, size
);
57 return calloc(items
, size
);
61 static void z_free(void *data
, void *p
)
70 static gpg_error_t
file_modified(struct client_s
*client
)
74 if (client
->state
!= STATE_OPEN
)
77 if (stat(client
->filename
, &st
) == 0 && client
->mtime
) {
78 if (client
->mtime
!= st
.st_mtime
)
79 return EPWMD_FILE_MODIFIED
;
85 static gboolean
encrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
86 void *inbuf
, gsize insize
)
90 if ((rc
= gcry_cipher_encrypt(gh
, outbuf
, outsize
, inbuf
, insize
))) {
91 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
98 gpg_error_t
decrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
99 void *inbuf
, gsize insize
)
103 if ((rc
= gcry_cipher_decrypt(gh
, outbuf
, outsize
, inbuf
, insize
)))
104 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
109 static gpg_error_t
parse_xml(assuan_context_t ctx
)
111 struct client_s
*client
= assuan_get_pointer(ctx
);
113 client
->doc
= xmlReadMemory(client
->xml
, client
->len
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
116 return EPWMD_LIBXML_ERROR
;
121 gboolean
valid_filename(const gchar
*filename
)
125 if (!filename
|| !*filename
)
128 for (p
= filename
; *p
; p
++) {
129 if (g_ascii_isalnum(*p
) == FALSE
&& *p
!= '-' && *p
!= '_' && *p
!= '.')
136 gint
open_file(const gchar
*filename
, struct stat
*st
)
140 if ((fd
= open(filename
, O_RDONLY
)) == -1)
143 if (stat(filename
, st
) == -1) {
151 void unlock_file_mutex(struct client_s
*client
)
156 if (client
->has_lock
== FALSE
|| client
->pinentry
->status
!= PINENTRY_NONE
)
158 if (client
->has_lock
== FALSE
)
162 CACHE_LOCK(client
->ctx
);
164 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
170 pth_mutex_release(m
);
171 client
->has_lock
= FALSE
;
174 gpg_error_t
lock_file_mutex(struct client_s
*client
)
178 if (client
->has_lock
== TRUE
)
181 CACHE_LOCK(client
->ctx
);
183 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
190 if (pth_mutex_acquire(m
, TRUE
, NULL
) == FALSE
) {
191 if (errno
== EBUSY
) {
193 assuan_write_status(client
->ctx
, "LOCKED", N_("Waiting for lock"));
195 pth_mutex_acquire(m
, FALSE
, NULL
);
199 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(errno
));
200 return gpg_error_from_errno(e
);
204 client
->has_lock
= TRUE
;
208 void free_client(struct client_s
*client
)
211 xmlFreeDoc(client
->doc
);
214 gcry_free(client
->xml
);
216 if (client
->filename
)
217 g_free(client
->filename
);
220 gcry_cipher_close(client
->gh
);
223 static void cleanup_client(struct client_s
*client
)
225 assuan_context_t ctx
= client
->ctx
;
227 struct pinentry_s
*pin
= client
->pinentry
;
230 unlock_file_mutex(client
);
231 CACHE_LOCK(client
->ctx
);
232 cache_decr_refcount(client
->md5file
);
235 * This may be a new file so don't use a cache slot. save_command() will
236 * set this to FALSE on success.
238 if (client
->new == TRUE
)
239 cache_clear(client
->md5file
, 1);
242 memset(client
, 0, sizeof(struct client_s
));
243 client
->state
= STATE_CONNECTED
;
245 client
->freed
= TRUE
;
247 client
->pinentry
= pin
;
252 static gchar
*print_fmt(const char *fmt
, ...)
255 static gchar buf
[ASSUAN_LINELENGTH
] = {0};
258 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
263 gboolean
do_decompress(assuan_context_t ctx
, gpointer in
, gint insize
,
264 gpointer
*out
, glong
*outsize
, gint
*error
)
277 z
.avail_out
= zlib_bufsize
;
278 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
281 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
282 *error
= Z_MEM_ERROR
;
286 ret
= inflateInit2(&z
, 47);
289 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
294 memset(&h
, 0, sizeof(gz_header
));
295 h
.comment
= (guchar
*)buf
;
296 h
.comm_max
= sizeof(buf
);
297 ret
= inflateGetHeader(&z
, &h
);
300 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
306 ret
= inflate(&z
, Z_BLOCK
);
309 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
316 insize
= atoi((gchar
*)h
.comment
);
321 ret
= inflate(&z
, Z_FINISH
);
328 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
336 z
.next_out
= pout
+ z
.total_out
;
337 z
.avail_out
= zlib_bufsize
;
340 rc
= assuan_write_status(ctx
, "DECOMPRESS",
341 print_fmt("%i %i", z
.total_out
, insize
));
358 } while (ret
!= Z_STREAM_END
);
361 rc
= assuan_write_status(ctx
, "DECOMPRESS",
362 print_fmt("%i %i", z
.total_out
, insize
));
371 *outsize
= z
.total_out
;
376 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
383 gpg_error_t
read_file_header(const gchar
*filename
, file_header_t
*fh
)
388 fd
= open(filename
, O_RDONLY
);
391 return gpg_error_from_errno(errno
);
393 len
= pth_read(fd
, fh
, sizeof(file_header_t
));
396 if (len
!= sizeof(file_header_t
))
397 return gpg_error_from_errno(errno
);
402 static gpg_error_t
open_command_finalize(assuan_context_t ctx
, guchar shakey
[],
405 struct client_s
*client
= assuan_get_pointer(ctx
);
411 if ((fd
= open_file(client
->filename
, &st
)) == -1) {
413 if (errno
== ENOENT
) {
421 log_write("%s: %s", client
->filename
, strerror(errno
));
422 cleanup_client(client
);
423 memset(shakey
, 0, sizeof(shakey
));
424 return send_syserror(ctx
, error
);
427 error
= try_xml_decrypt(ctx
, fd
, st
, shakey
);
431 memset(shakey
, 0, sizeof(shakey
));
432 cleanup_client(client
);
433 return send_error(ctx
, error
);
437 CACHE_LOCK(client
->ctx
);
439 if (cached
== FALSE
) {
440 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
441 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
442 cleanup_client(client
);
444 return send_error(ctx
, EPWMD_MAX_SLOTS
);
447 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
448 cache_reset_timeout(client
->md5file
, timeout
);
451 cache_set_timeout(client
->md5file
, -2);
456 memset(shakey
, 0, sizeof(shakey
));
457 error
= parse_xml(ctx
);
460 gcry_free(client
->xml
);
465 if (client
->new == FALSE
)
466 send_cache_status_all();
468 client
->state
= STATE_OPEN
;
471 return send_error(ctx
, error
);
474 static int open_command(assuan_context_t ctx
, char *line
)
477 guchar shakey
[gcrykeysize
];
478 gboolean cached
= FALSE
;
480 struct client_s
*client
= assuan_get_pointer(ctx
);
482 gchar
*filename
= NULL
;
483 file_header_t file_header
;
485 memset(shakey
, 0, sizeof(shakey
));
487 if ((req
= split_input_line(line
, " ", 2)) != NULL
)
490 if (!filename
|| !*filename
) {
492 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
495 if (valid_filename(filename
) == FALSE
) {
497 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
500 if (client
->state
== STATE_OPEN
)
501 cleanup_client(client
);
503 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
504 CACHE_LOCK(client
->ctx
);
506 if (cache_has_file(client
->md5file
) == FALSE
) {
507 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
510 return send_error(ctx
, EPWMD_MAX_SLOTS
);
514 cache_incr_refcount(client
->md5file
);
516 error
= lock_file_mutex(client
);
520 return send_error(ctx
, error
);
523 client
->freed
= FALSE
;
525 if ((error
= gcry_cipher_open(&client
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
527 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
528 cleanup_client(client
);
529 return send_error(ctx
, error
);
532 if (stat(filename
, &st
) == 0) {
533 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
534 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
536 cleanup_client(client
);
537 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
540 client
->mtime
= st
.st_mtime
;
543 client
->filename
= g_strdup(filename
);
545 client
->pinentry
->filename
= client
->filename
;
548 if (!client
->filename
) {
549 memset(shakey
, 0, sizeof(shakey
));
550 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
551 cleanup_client(client
);
553 return send_syserror(ctx
, ENOMEM
);
557 * New files don't need a key.
559 if (access(filename
, R_OK
|W_OK
) != 0) {
560 if (errno
!= ENOENT
) {
562 log_write("%s: %s", filename
, strerror(errno
));
564 cleanup_client(client
);
565 return send_syserror(ctx
, error
);
568 if ((client
->xml
= new_document()) == NULL
) {
569 log_write("%s", strerror(ENOMEM
));
571 cleanup_client(client
);
572 return send_syserror(ctx
, ENOMEM
);
575 client
->len
= xmlStrlen(client
->xml
);
577 client
->filename
= g_strdup(filename
);
579 if (!client
->filename
) {
581 cleanup_client(client
);
582 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
583 return send_syserror(ctx
, ENOMEM
);
586 memset(shakey
, 0, sizeof(shakey
));
588 if (req
[1] && *req
[1])
589 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
592 return open_command_finalize(ctx
, shakey
, cached
);
595 error
= read_file_header(filename
, &file_header
);
599 cleanup_client(client
);
600 return send_error(ctx
, error
);
603 if (file_header
.iter
== -1)
606 CACHE_LOCK(client
->ctx
);
607 cached
= cache_get_key(client
->md5file
, shakey
);
610 if (cached
== FALSE
) {
612 * No key specified and no matching filename found in the cache. Use
613 * pinentry to retrieve the key. Cannot return assuan_process_done()
614 * here otherwise the command will be interrupted. The event loop in
615 * client_thread() will poll the file descriptor waiting for it to
616 * become ready to read a pinentry_key_s which will contain the
617 * entered key or error. It will then call open_command_finalize() to
618 * to finish the command.
620 if (!req
[1] || !*req
[1]) {
622 if (client
->pinentry
->use
== FALSE
) {
623 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
628 client
->pinentry
->which
= PINENTRY_OPEN
;
629 error
= pinentry_fork(ctx
);
632 cleanup_client(client
);
633 return send_error(ctx
, error
);
636 client
->pinentry
->cb
= open_command_finalize
;
637 client
->pinentry
->status
= PINENTRY_INIT
;
640 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
645 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
650 return open_command_finalize(ctx
, shakey
, cached
);
653 gboolean
do_compress(assuan_context_t ctx
, gint level
, gpointer data
,
654 gint size
, gpointer
*out
, glong
*outsize
, gint
*error
)
661 gint cmd
= Z_NO_FLUSH
;
666 z
.next_in
= pin
= data
;
667 z
.avail_in
= size
< zlib_bufsize
? size
: zlib_bufsize
;
668 z
.avail_out
= zlib_bufsize
;
669 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
672 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
673 *error
= Z_MEM_ERROR
;
677 ret
= deflateInit2(&z
, level
, Z_DEFLATED
, 31, 8, Z_DEFAULT_STRATEGY
);
680 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
686 memset(&h
, 0, sizeof(gz_header
));
687 snprintf(buf
, sizeof(buf
), "%i", size
);
688 h
.comment
= (guchar
*)buf
;
689 ret
= deflateSetHeader(&z
, &h
);
692 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
702 ret
= deflate(&z
, cmd
);
709 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
717 z
.next_out
= pout
+ z
.total_out
;
718 z
.avail_out
= zlib_bufsize
;
721 if (!z
.avail_in
&& z
.total_in
< size
) {
722 if (z
.total_in
+ zlib_bufsize
> size
)
723 z
.avail_in
= size
- z
.total_in
;
725 z
.avail_in
= zlib_bufsize
;
728 rc
= assuan_write_status(ctx
, "COMPRESS",
729 print_fmt("%i %i", z
.total_in
, size
));
738 if (z
.total_in
>= size
)
749 } while (ret
!= Z_STREAM_END
);
752 rc
= assuan_write_status(ctx
, "COMPRESS",
753 print_fmt("%i %i", z
.total_in
, size
));
762 *outsize
= z
.total_out
;
767 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
774 gpg_error_t
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
775 const gchar
*filename
, gpointer data
, size_t insize
, guchar
*shakey
,
781 guchar tkey
[gcrykeysize
];
785 guint iter_progress
= 0, n_iter
= 0, xiter
= 0;
786 gchar tmp
[FILENAME_MAX
];
787 file_header_t file_header
;
791 * cache_file_count() needs both .used == TRUE and a valid key in
792 * order for it to count as a used cache entry. Fixes CACHE status
795 memset(shakey
, '!', gcrykeysize
);
797 file_header
.iter
= iter
;
801 if (insize
/ gcryblocksize
) {
802 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
804 if (insize
% gcryblocksize
)
805 len
+= gcryblocksize
;
809 * Resize the existing xml buffer to the block size required by gcrypt
810 * rather than duplicating it and wasting memory.
812 inbuf
= gcry_realloc(data
, len
);
815 return gpg_error_from_errno(ENOMEM
);
818 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
819 memcpy(tkey
, shakey
, sizeof(tkey
));
822 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
824 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
828 file_header
.iter
= iter
;
831 iter_progress
= get_key_file_integer("default", "iteration_progress");
833 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
834 error
= assuan_write_status(client
->ctx
, "ENCRYPT", "0");
842 while (xiter
< file_header
.iter
) {
843 if (client
&& iter_progress
> 0 && xiter
>= iter_progress
) {
844 if (!(xiter
% iter_progress
)) {
845 error
= assuan_write_status(client
->ctx
, "ENCRYPT", print_fmt("%i",
846 ++n_iter
* iter_progress
));
855 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
856 sizeof(file_header
.iv
)))) {
858 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
862 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
865 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
873 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
874 sizeof(file_header
.iv
)))) {
876 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
880 if ((rc
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
882 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
886 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
888 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
892 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
893 error
= assuan_write_status(client
->ctx
, "ENCRYPT",
894 print_fmt("%i", file_header
.iter
));
904 g_snprintf(tmp
, sizeof(tmp
), ".%s.tmp", filename
);
906 if ((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
909 p
= strrchr(tmp
, '/');
911 log_write("%s: %s", p
, strerror(errno
));
912 return gpg_error_from_errno(error
);
917 * xml_import() from command line.
921 len
= pth_write(fd
, &file_header
, sizeof(file_header_t
));
923 if (len
!= sizeof(file_header
)) {
930 return gpg_error_from_errno(len
);
933 len
= pth_write(fd
, inbuf
, insize
);
942 return gpg_error_from_errno(len
);
945 if (fsync(fd
) == -1) {
949 return gpg_error_from_errno(len
);
958 if (stat(filename
, &st
) == 0)
959 mode
= st
.st_mode
& (S_IRWXU
|S_IRWXG
|S_IRWXO
);
961 if (rename(tmp
, filename
) == -1) {
964 return gpg_error_from_errno(len
);
968 chmod(filename
, mode
);
975 static gpg_error_t
save_command_finalize(assuan_context_t ctx
,
976 guchar shakey
[], gboolean cached
)
978 struct client_s
*client
= assuan_get_pointer(ctx
);
990 xmlDocDumpFormatMemory(client
->doc
, &p
, &len
, 0);
993 iter
= get_key_file_integer(client
->filename
, "compression_level");
998 if (do_compress(ctx
, iter
, xmlbuf
, len
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
999 memset(shakey
, 0, sizeof(shakey
));
1002 if (zerror
== Z_MEM_ERROR
) {
1003 return send_syserror(ctx
, ENOMEM
);
1006 return send_error(ctx
, GPG_ERR_COMPR_ALGO
);
1014 iter
= get_key_file_integer(client
->filename
, "iterations");
1015 error
= do_xml_encrypt(client
, client
->gh
, client
->filename
, xmlbuf
, len
, shakey
, iter
);
1018 memset(shakey
, 0, sizeof(shakey
));
1019 return send_error(ctx
, error
);
1022 stat(client
->filename
, &st
);
1023 client
->mtime
= st
.st_mtime
;
1024 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
1025 CACHE_LOCK(client
->ctx
);
1028 memset(shakey
, 0, sizeof(shakey
));
1029 cache_reset_timeout(client
->md5file
, timeout
);
1032 if (client
->new == TRUE
)
1033 send_cache_status_all();
1035 client
->new = FALSE
;
1036 return send_error(ctx
, 0);
1039 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
1040 memset(shakey
, 0, sizeof(shakey
));
1041 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
1043 return send_error(ctx
, EPWMD_MAX_SLOTS
);
1046 client
->new = FALSE
;
1047 memset(shakey
, 0, sizeof(shakey
));
1048 cache_reset_timeout(client
->md5file
, timeout
);
1050 send_cache_status_all();
1051 return send_error(ctx
, 0);
1054 static int save_command(assuan_context_t ctx
, char *line
)
1056 gboolean cached
= FALSE
;
1057 guchar shakey
[gcrykeysize
];
1059 struct client_s
*client
= assuan_get_pointer(ctx
);
1062 memset(shakey
, 0, sizeof(shakey
));
1063 error
= file_modified(client
);
1066 log_write("%s: %s", client
->filename
? client
->filename
: "",
1067 pwmd_strerror(error
));
1068 return send_error(ctx
, error
);
1071 error
= lock_file_mutex(client
);
1074 return send_error(ctx
, error
);
1076 if (stat(client
->filename
, &st
) == -1 && errno
!= ENOENT
)
1077 return send_syserror(ctx
, errno
);
1079 if (errno
!= ENOENT
&& !S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
1080 log_write("%s: %s", client
->filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
1081 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
1084 if (get_key_file_integer(client
->filename
, "iterations") == -1)
1087 if (!line
|| !*line
) {
1088 guchar tmp
[sizeof(shakey
)];
1091 memset(tmp
, '!', sizeof(tmp
));
1093 if (cache_get_key(client
->md5file
, shakey
) == FALSE
||
1094 memcmp(shakey
, tmp
, sizeof(shakey
)) == 0) {
1096 #ifdef WITH_PINENTRY
1097 if (client
->pinentry
->use
== FALSE
) {
1098 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1102 client
->pinentry
->which
= PINENTRY_SAVE
;
1103 error
= pinentry_fork(ctx
);
1106 return send_error(ctx
, error
);
1108 client
->pinentry
->cb
= save_command_finalize
;
1109 client
->pinentry
->status
= PINENTRY_INIT
;
1112 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1122 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, line
, strlen(line
));
1123 memset(line
, 0, strlen(line
));
1127 return save_command_finalize(ctx
, shakey
, cached
);
1130 static gboolean
contains_whitespace(const gchar
*str
)
1132 const gchar
*p
= str
;
1136 len
= g_utf8_strlen(p
++, -1) -1;
1139 c
= g_utf8_get_char(p
++);
1141 if (g_unichar_isspace(c
))
1148 static int delete_command(assuan_context_t ctx
, char *line
)
1150 struct client_s
*client
= assuan_get_pointer(ctx
);
1155 error
= file_modified(client
);
1158 log_write("%s: %s", client
->filename
? client
->filename
: "",
1159 pwmd_strerror(error
));
1160 return send_error(ctx
, error
);
1163 if (strchr(line
, '\t'))
1164 req
= split_input_line(line
, "\t", -1);
1166 req
= split_input_line(line
, " ", -1);
1169 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1171 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1175 return send_error(ctx
, error
);
1179 * No sub-node defined. Remove the entire node (account).
1188 return send_error(ctx
, 0);
1191 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1195 return send_error(ctx
, error
);
1202 return send_error(ctx
, 0);
1206 * Don't return with assuan_process_done() here. This has been called from
1207 * assuan_process_next() and the command should be finished in
1210 static int store_command_finalize(gpointer data
, gint rc
, guchar
*line
,
1213 assuan_context_t ctx
= data
;
1214 struct client_s
*client
= assuan_get_pointer(ctx
);
1216 guchar
*result
= line
;
1218 gpg_error_t error
= file_modified(client
);
1230 req
= split_input_line((gchar
*)result
, "\t", 0);
1245 return EPWMD_COMMAND_SYNTAX
;
1247 if (valid_xml_element((xmlChar
*)*req
) == FALSE
) {
1249 return EPWMD_INVALID_ELEMENT
;
1252 if (valid_element_path(req
+1, TRUE
) == FALSE
) {
1254 return EPWMD_INVALID_ELEMENT
;
1258 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1260 if (error
&& error
== EPWMD_ELEMENT_NOT_FOUND
) {
1261 error
= new_account(client
->doc
, *req
);
1278 create_elements_cb(n
, req
+1, &error
, NULL
);
1280 find_elements(client
->doc
, n
->children
, req
+1, &error
,
1281 NULL
, NULL
, create_elements_cb
, FALSE
, 0, NULL
);
1285 client
->inquire_status
= INQUIRE_DONE
;
1289 static int store_command(assuan_context_t ctx
, char *line
)
1291 struct client_s
*client
= assuan_get_pointer(ctx
);
1292 gpg_error_t error
= file_modified(client
);
1295 log_write("%s: %s", client
->filename
? client
->filename
: "",
1296 pwmd_strerror(error
));
1297 return send_error(ctx
, error
);
1300 error
= assuan_inquire_ext(ctx
, "STORE", 0, store_command_finalize
, ctx
);
1303 return send_error(ctx
, error
);
1305 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1306 client
->inquire_status
= INQUIRE_BUSY
;
1310 static int get_command(assuan_context_t ctx
, char *line
)
1312 struct client_s
*client
= assuan_get_pointer(ctx
);
1317 error
= file_modified(client
);
1320 log_write("%s: %s", client
->filename
? client
->filename
: "",
1321 pwmd_strerror(error
));
1322 return send_error(ctx
, error
);
1325 req
= split_input_line(line
, "\t", -1);
1327 if (!req
|| !*req
) {
1329 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1332 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1336 return send_error(ctx
, error
);
1340 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1345 return send_error(ctx
, error
);
1347 if (!n
|| !n
->children
)
1348 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1352 if (!n
|| !n
->content
|| !*n
->content
)
1353 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1355 error
= assuan_send_data(ctx
, n
->content
, xmlStrlen(n
->content
));
1356 return send_error(ctx
, error
);
1359 static gchar
*element_path_to_req(const gchar
*account
, xmlChar
*path
)
1368 for (n
= 0; *p
&& n
< 3; p
++) {
1373 if (strstr((gchar
*)p
, "text()") != NULL
)
1374 p
[xmlStrlen(p
) - 7] = 0;
1376 for (n
= 0; p
[n
]; n
++) {
1381 buf
= g_strdup_printf("%s\t%s", account
, p
);
1382 p
= (xmlChar
*)buf
+ strlen(buf
) - 1;
1390 gboolean
strv_printf(gchar
***array
, const gchar
*fmt
, ...)
1395 gint len
= *array
? g_strv_length(*array
) : 0;
1401 if ((a
= g_realloc(*array
, (len
+ 2) * sizeof(gchar
*))) == NULL
)
1405 ret
= g_vasprintf(&buf
, fmt
, ap
);
1421 static xmlNodePtr
realpath_elements_cb(xmlNodePtr node
, gchar
**req
,
1422 gpg_error_t
*error
, void *data
)
1424 struct realpath_s
*rp
= data
;
1427 g_free(rp
->account
);
1429 rp
->account
= g_strdup(req
[0]);
1432 *error
= gpg_error_from_errno(ENOMEM
);
1439 static int realpath_command(assuan_context_t ctx
, char *line
)
1442 struct client_s
*client
= assuan_get_pointer(ctx
);
1448 struct realpath_s
*rp
;
1451 error
= file_modified(client
);
1454 log_write("%s: %s", client
->filename
? client
->filename
: "",
1455 pwmd_strerror(error
));
1456 return send_error(ctx
, error
);
1459 if (strchr(line
, '\t') != NULL
) {
1460 if ((req
= split_input_line(line
, "\t", 0)) == NULL
)
1461 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1464 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1465 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1468 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1472 return send_error(ctx
, error
);
1475 rp
= g_malloc(sizeof(struct realpath_s
));
1479 return send_syserror(ctx
, ENOMEM
);
1482 rp
->account
= g_strdup(req
[0]);
1486 return send_syserror(ctx
, ENOMEM
);
1490 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
,
1491 NULL
, realpath_elements_cb
, NULL
, FALSE
, 0, rp
);
1494 g_free(rp
->account
);
1497 return send_error(ctx
, error
);
1501 p
= xmlGetNodePath(n
);
1502 result
= element_path_to_req(rp
->account
, p
);
1506 g_free(rp
->account
);
1510 return send_syserror(ctx
, ENOMEM
);
1513 string
= g_string_new(result
);
1515 g_free(rp
->account
);
1522 for (t
= string
->str
+ i
; *t
; t
++, i
++) {
1523 if ((!i
&& *t
!= '!') || *t
== '\t') {
1524 string
= g_string_insert_c(string
, !i
? i
++ : ++i
, '!');
1529 error
= assuan_send_data(ctx
, string
->str
, string
->len
);
1530 g_string_free(string
, TRUE
);
1531 return send_error(ctx
, error
);
1534 struct list_element_s
{
1539 static gboolean
append_to_element_list(struct list_element_s
*elements
)
1546 if (!elements
|| !elements
->elements
)
1549 tmp
= g_strjoinv("\t", elements
->elements
);
1554 g_strfreev(elements
->elements
);
1555 elements
->elements
= NULL
;
1556 total
= g_slist_length(elements
->list
);
1557 a
= g_utf8_collate_key(tmp
, -1);
1565 * Removes duplicate element paths from the list. This is needed when
1566 * appending an element tree from list_command(). The glib docs recommend
1567 * using g_utf8_collate_key() for a large number of strings.
1569 for (i
= 0; i
< total
; i
++) {
1570 gchar
*p
= g_slist_nth_data(elements
->list
, i
);
1571 gchar
*b
= g_utf8_collate_key(p
, -1);
1579 if (strcmp(a
, b
) == 0) {
1590 list
= g_slist_append(elements
->list
, tmp
);
1595 elements
->list
= list
;
1599 static gpg_error_t
do_list_recurse(xmlDocPtr doc
, xmlNodePtr node
,
1600 struct list_element_s
*elements
, gchar
*prefix
)
1605 if (append_to_element_list(elements
) == FALSE
)
1606 return gpg_error_from_errno(ENOMEM
);
1608 for (n
= node
; n
; n
= n
->next
) {
1609 if (n
->type
== XML_ELEMENT_NODE
) {
1610 xmlChar
*content
= node_has_attribute(n
, (xmlChar
*)"target");
1614 if (strv_printf(&elements
->elements
, "%s\t%s", prefix
, n
->name
) == FALSE
)
1615 return gpg_error_from_errno(ENOMEM
);
1617 if (append_to_element_list(elements
) == FALSE
)
1618 return gpg_error_from_errno(ENOMEM
);
1621 tmp
= g_strdup_printf("%s\t!%s", prefix
, n
->name
);
1624 return gpg_error_from_errno(ENOMEM
);
1626 if (strv_printf(&elements
->elements
, "%s", tmp
) == FALSE
) {
1628 return gpg_error_from_errno(ENOMEM
);
1632 error
= do_list_recurse(doc
, n
->children
, elements
, tmp
);
1635 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
1641 if (append_to_element_list(elements
) == FALSE
)
1642 return gpg_error_from_errno(ENOMEM
);
1649 static gpg_error_t
do_list_command(assuan_context_t ctx
, xmlDocPtr doc
,
1650 struct list_element_s
*elements
, char *line
)
1652 gchar
*prefix
= NULL
, *account
;
1653 gchar
**req
= NULL
, **oreq
= NULL
, *tmp
;
1655 gboolean account_is_literal
, account_has_target
= FALSE
;
1660 if ((req
= split_input_line(line
, "\t", 0)) == NULL
) {
1661 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1662 return EPWMD_COMMAND_SYNTAX
;
1665 prefix
= g_strdup(*req
);
1669 return gpg_error_from_errno(ENOMEM
);
1672 account
= g_strdup(*req
);
1677 return gpg_error_from_errno(ENOMEM
);
1680 oreq
= g_strdupv(req
);
1686 return gpg_error_from_errno(ENOMEM
);
1691 account_has_target
= FALSE
;
1692 account_is_literal
= is_literal_element_str(prefix
);
1693 n
= find_account(doc
, &p
, &error
, &account_has_target
, 0);
1703 if (!which
&& account_is_literal
== FALSE
&& account_has_target
== FALSE
) {
1704 tmp
= g_strdup_printf("!%s", prefix
);
1707 error
= gpg_error_from_errno(ENOMEM
);
1718 n
= find_elements(doc
, n
->children
, p
+1, &error
, NULL
, NULL
, NULL
,
1724 tmp
= g_strjoinv("\t", p
+1);
1726 error
= gpg_error_from_errno(ENOMEM
);
1730 t
= g_strdup_printf("%s\t%s", prefix
, tmp
);
1732 error
= gpg_error_from_errno(ENOMEM
);
1741 if (strv_printf(&elements
->elements
, "%s", prefix
) == FALSE
) {
1742 error
= gpg_error_from_errno(ENOMEM
);
1746 if (node_has_child_element(n
->children
) == FALSE
) {
1747 if (append_to_element_list(elements
) == FALSE
) {
1748 error
= gpg_error_from_errno(ENOMEM
);
1753 error
= do_list_recurse(doc
, n
->children
, elements
, prefix
);
1758 if (!which
++ && !*(p
+1) && account_is_literal
== FALSE
&& account_has_target
== TRUE
) {
1760 *oreq
= g_strdup_printf("!%s", account
);
1763 error
= gpg_error_from_errno(ENOMEM
);
1769 prefix
= g_strdup(*oreq
);
1772 error
= gpg_error_from_errno(ENOMEM
);
1791 * This could be faster especially when finding "target" attributes.
1793 static int list_command(assuan_context_t ctx
, char *line
)
1795 struct client_s
*client
= assuan_get_pointer(ctx
);
1797 struct list_element_s
*elements
= NULL
;
1802 if (disable_list_and_dump
== TRUE
)
1803 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
1805 error
= file_modified(client
);
1808 log_write("%s: %s", client
->filename
, pwmd_strerror(error
));
1809 return send_error(ctx
, error
);
1815 error
= list_accounts(client
->doc
, &str
);
1818 return send_error(ctx
, error
);
1820 error
= assuan_send_data(ctx
, str
->str
, str
->len
);
1821 g_string_free(str
, TRUE
);
1822 return send_error(ctx
, error
);
1825 elements
= g_malloc0(sizeof(struct list_element_s
));
1828 error
= gpg_error_from_errno(ENOMEM
);
1832 error
= do_list_command(ctx
, client
->doc
, elements
, line
);
1838 total
= g_slist_length(elements
->list
);
1841 error
= EPWMD_EMPTY_ELEMENT
;
1846 * Find element paths with a target and append those element trees to
1849 for (i
= 0; i
< total
; i
++) {
1852 tmp
= g_slist_nth_data(elements
->list
, i
);
1853 req
= split_input_line(tmp
, "\t", 0);
1856 if (g_str_has_prefix(tmp
, "!") == TRUE
) {
1864 for (p
= req
; *p
; p
++) {
1865 if (g_str_has_prefix(*p
, "!") == FALSE
)
1876 error
= do_list_command(ctx
, client
->doc
, elements
, tmp
);
1878 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
1881 total
= g_slist_length(elements
->list
);
1885 string
= g_string_new(NULL
);
1887 for (i
= 0; i
< total
; i
++) {
1888 tmp
= g_slist_nth_data(elements
->list
, i
);
1889 g_string_append_printf(string
, "%s\n", tmp
);
1893 string
= g_string_truncate(string
, string
->len
- 1);
1894 error
= assuan_send_data(ctx
, string
->str
, string
->len
);
1895 g_string_free(string
, TRUE
);
1900 g_slist_free(elements
->list
);
1902 if (elements
->elements
)
1903 g_strfreev(elements
->elements
);
1908 return send_error(ctx
, error
);
1911 static gpg_error_t
add_attribute(xmlNodePtr node
, const gchar
*name
,
1916 if ((a
= xmlHasProp(node
, (xmlChar
*)name
)) == NULL
) {
1917 a
= xmlNewProp(node
, (xmlChar
*)name
, (xmlChar
*)value
);
1920 return EPWMD_LIBXML_ERROR
;
1923 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1929 * req[0] - element path
1931 static int attribute_list(assuan_context_t ctx
, gchar
**req
)
1933 struct client_s
*client
= assuan_get_pointer(ctx
);
1934 gchar
**attrlist
= NULL
;
1936 gchar
**path
= NULL
;
1942 if (!req
|| !req
[0])
1943 return EPWMD_COMMAND_SYNTAX
;
1945 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1947 * The first argument may be only an account.
1949 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
1950 return EPWMD_COMMAND_SYNTAX
;
1953 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1961 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1962 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1972 for (a
= n
->properties
; a
; a
= a
->next
) {
1975 if ((pa
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1977 g_strfreev(attrlist
);
1980 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(errno
));
1981 return gpg_error_from_errno(error
);
1986 attrlist
[i
] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1989 g_strfreev(attrlist
);
1990 return gpg_error_from_errno(ENOMEM
);
1993 attrlist
[++i
] = NULL
;
1997 return EPWMD_EMPTY_ELEMENT
;
1999 line
= g_strjoinv("\n", attrlist
);
2002 g_strfreev(attrlist
);
2003 return gpg_error_from_errno(ENOMEM
);
2006 error
= assuan_send_data(ctx
, line
, strlen(line
));
2008 g_strfreev(attrlist
);
2013 * req[0] - attribute
2014 * req[1] - element path
2016 static int attribute_delete(struct client_s
*client
, gchar
**req
)
2020 gchar
**path
= NULL
;
2023 if (!req
|| !req
[0] || !req
[1])
2024 return EPWMD_COMMAND_SYNTAX
;
2026 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2028 * The first argument may be only an account.
2030 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2031 return EPWMD_COMMAND_SYNTAX
;
2035 * Don't remove the "name" attribute for the account element. To remove an
2036 * account use DELETE <account>.
2038 if (!path
[1] && xmlStrEqual((xmlChar
*)req
[0], (xmlChar
*)"name")) {
2039 error
= EPWMD_ATTR_SYNTAX
;
2043 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2049 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2050 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2058 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
)
2059 return EPWMD_ATTR_NOT_FOUND
;
2061 if (xmlRemoveProp(a
) == -1)
2062 return EPWMD_LIBXML_ERROR
;
2072 * Creates a "target" attribute. When other commands encounter an element with
2073 * this attribute, the element path is modified to the target value. If the
2074 * source element path doesn't exist when using 'ATTR SET target', it is
2075 * created, but the destination element path must exist.
2077 * req[0] - source element path
2078 * req[1] - destination element path
2080 static gpg_error_t
target_attribute(struct client_s
*client
, gchar
**req
)
2082 gchar
**src
, **dst
, *line
= NULL
;
2086 if (!req
|| !req
[0] || !req
[1])
2087 return EPWMD_COMMAND_SYNTAX
;
2089 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
2091 * The first argument may be only an account.
2093 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
)
2094 return EPWMD_COMMAND_SYNTAX
;
2097 if (valid_element_path(src
, FALSE
) == FALSE
) {
2099 return EPWMD_INVALID_ELEMENT
;
2102 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2104 * The first argument may be only an account.
2106 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
2107 error
= EPWMD_COMMAND_SYNTAX
;
2112 n
= find_account(client
->doc
, &dst
, &error
, NULL
, 0);
2115 * Make sure the destination element path exists.
2121 n
= find_elements(client
->doc
, n
->children
, dst
+1, &error
,
2122 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2129 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
2132 if (error
== EPWMD_ELEMENT_NOT_FOUND
) {
2133 error
= new_account(client
->doc
, src
[0]);
2146 n
= create_target_elements_cb(n
, src
+1, &error
, NULL
);
2148 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
2149 NULL
, NULL
, create_target_elements_cb
, FALSE
, 0, NULL
);
2155 * Reset the position of the element tree now that the elements
2156 * have been created.
2158 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
2163 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
2164 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2170 line
= g_strjoinv("\t", dst
);
2171 error
= add_attribute(n
, "target", line
);
2181 * req[0] - account name
2184 static gpg_error_t
name_attribute(struct client_s
*client
, gchar
**req
)
2190 tmp
= g_strdupv(req
);
2193 return gpg_error_from_errno(ENOMEM
);
2195 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2201 if (g_utf8_collate(req
[0], req
[1]) == 0)
2205 * Will not overwrite an existing account.
2207 tmp
= g_strdupv(req
+1);
2210 return gpg_error_from_errno(ENOMEM
);
2212 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2215 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
2219 return EPWMD_ACCOUNT_EXISTS
;
2222 * Whitespace not allowed in account names.
2224 if (contains_whitespace(req
[1]) == TRUE
)
2225 return EPWMD_ATTR_SYNTAX
;
2227 tmp
= g_strdupv(req
);
2230 return gpg_error_from_errno(ENOMEM
);
2232 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
2236 return EPWMD_ELEMENT_NOT_FOUND
;
2238 return add_attribute(n
, "name", req
[1]);
2242 * req[0] - attribute
2243 * req[1] - element path
2245 static int attribute_get(assuan_context_t ctx
, gchar
**req
)
2247 struct client_s
*client
= assuan_get_pointer(ctx
);
2253 if (!req
|| !req
[0] || !req
[1])
2254 return EPWMD_COMMAND_SYNTAX
;
2256 if (strchr(req
[1], '\t')) {
2257 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
)
2258 return EPWMD_COMMAND_SYNTAX
;
2261 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2262 return EPWMD_COMMAND_SYNTAX
;
2265 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2271 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2272 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2280 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
)
2281 return EPWMD_ATTR_NOT_FOUND
;
2283 error
= assuan_send_data(ctx
, a
, xmlStrlen(a
));
2293 * req[0] - attribute
2294 * req[1] - element path
2297 static int attribute_set(struct client_s
*client
, gchar
**req
)
2299 gchar
**path
= NULL
;
2303 if (!req
|| !req
[0] || !req
[1] || !req
[2])
2304 return EPWMD_COMMAND_SYNTAX
;
2307 * Reserved attribute names.
2309 if (g_utf8_collate(req
[0], "name") == 0) {
2311 * Only reserved for the account element. Not the rest of the
2314 if (strchr(req
[1], '\t') == NULL
)
2315 return name_attribute(client
, req
+ 1);
2317 else if (g_utf8_collate(req
[0], "target") == 0)
2318 return target_attribute(client
, req
+ 1);
2320 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2322 * The first argument may be only an account.
2324 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2325 return EPWMD_COMMAND_SYNTAX
;
2328 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
2334 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
2335 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2342 return add_attribute(n
, req
[0], req
[2]);
2351 * req[1] - attribute name or element path if command is LIST
2352 * req[2] - element path
2353 * req[2] - element path or value
2355 static int attr_command(assuan_context_t ctx
, char *line
)
2357 struct client_s
*client
= assuan_get_pointer(ctx
);
2358 gchar
**req
= split_input_line(line
, " ", 4);
2359 gpg_error_t error
= 0;
2361 error
= file_modified(client
);
2364 log_write("%s: %s", client
->filename
? client
->filename
: "",
2365 pwmd_strerror(error
));
2367 return send_error(ctx
, error
);
2370 if (!req
|| !req
[0] || !req
[1]) {
2372 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2375 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
2376 error
= attribute_set(client
, req
+1);
2377 else if (g_ascii_strcasecmp(req
[0], "GET") == 0)
2378 error
= attribute_get(ctx
, req
+1);
2379 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
2380 error
= attribute_delete(client
, req
+1);
2381 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
2382 error
= attribute_list(ctx
, req
+1);
2384 error
= EPWMD_COMMAND_SYNTAX
;
2387 return send_error(ctx
, error
);
2390 static int iscached_command(assuan_context_t ctx
, char *line
)
2392 gchar
**req
= split_input_line(line
, " ", 0);
2395 if (!req
|| !*req
) {
2397 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2400 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2404 if (cache_iscached(md5file
) == FALSE
) {
2406 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2410 return send_error(ctx
, 0);
2413 gpg_error_t
send_cache_status(assuan_context_t ctx
)
2416 struct client_s
*client
= assuan_get_pointer(ctx
);
2418 CACHE_LOCK(client
->ctx
);
2419 tmp
= print_fmt("%i %i",
2421 (cache_size
/ sizeof(file_cache_t
)) - cache_file_count());
2424 return assuan_write_status(ctx
, "CACHE", tmp
);
2427 static int clearcache_command(assuan_context_t ctx
, char *line
)
2429 struct client_s
*client
= assuan_get_pointer(ctx
);
2430 gchar
**req
= split_input_line(line
, " ", 0);
2435 if (!req
|| !*req
) {
2437 cache_clear(client
->md5file
, 2);
2439 return send_error(ctx
, 0);
2442 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2445 if (cache_clear(md5file
, 1) == FALSE
) {
2447 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2451 return send_error(ctx
, 0);
2454 static int cachetimeout_command(assuan_context_t ctx
, char *line
)
2458 gchar
**req
= split_input_line(line
, " ", 0);
2460 struct client_s
*client
= assuan_get_pointer(ctx
);
2462 if (!req
|| !*req
|| !req
[1]) {
2464 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2468 timeout
= strtol(req
[0], &p
, 10);
2470 if (errno
!= 0 || *p
!= 0) {
2472 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2475 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2477 CACHE_LOCK(client
->ctx
);
2479 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2481 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2485 send_cache_status_all();
2486 return send_error(ctx
, 0);
2489 static int dump_command(assuan_context_t ctx
, char *line
)
2493 struct client_s
*client
= assuan_get_pointer(ctx
);
2496 if (disable_list_and_dump
== TRUE
)
2497 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2499 error
= file_modified(client
);
2502 log_write("%s: %s", client
->filename
? client
->filename
: "",
2503 pwmd_strerror(error
));
2504 return send_error(ctx
, error
);
2507 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2508 error
= assuan_send_data(ctx
, xml
, len
);
2510 return send_error(ctx
, error
);
2513 static int getconfig_command(assuan_context_t ctx
, gchar
*line
)
2515 struct client_s
*client
= assuan_get_pointer(ctx
);
2516 gpg_error_t error
= 0;
2519 if (strcmp(line
, "key") == 0 || strcmp(line
, "key_file") == 0)
2520 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2522 p
= get_key_file_string(client
->filename
? client
->filename
: "default", line
);
2525 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2527 tmp
= expand_homedir(p
);
2530 error
= assuan_send_data(ctx
, p
, strlen(p
));
2532 return send_error(ctx
, error
);
2535 void cleanup_assuan(assuan_context_t ctx
)
2537 struct client_s
*cl
= assuan_get_pointer(ctx
);
2542 static void reset_notify(assuan_context_t ctx
)
2544 struct client_s
*cl
= assuan_get_pointer(ctx
);
2549 static gpg_error_t
parse_client_option(assuan_context_t ctx
, const gchar
*line
)
2551 gchar name
[32] = {0}, value
[256] = {0};
2552 pth_attr_t attr
= pth_attr_of(pth_self());
2554 if (sscanf(line
, " %31[a-zA-Z] = %255c", name
, value
) != 2)
2555 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_SYNTAX
);
2557 if (g_strcasecmp(name
, (gchar
*)"NAME") == 0) {
2558 log_write("OPTION CLIENT %s", line
);
2559 pth_attr_set(attr
, PTH_ATTR_NAME
, value
);
2562 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_UNKNOWN_OPTION
);
2567 static int option_handler(assuan_context_t ctx
, const gchar
*name
,
2570 #ifdef WITH_PINENTRY
2571 struct client_s
*client
= assuan_get_pointer(ctx
);
2574 if (!value
|| !*value
)
2575 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_INV_VALUE
);
2577 if (g_strcasecmp(name
, (gchar
*)"ttyname") == 0) {
2578 #ifdef WITH_PINENTRY
2579 g_free(client
->pinentry
->ttyname
);
2580 client
->pinentry
->ttyname
= g_strdup(value
);
2582 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2585 else if (g_strcasecmp(name
, (gchar
*)"ttytype") == 0) {
2586 #ifdef WITH_PINENTRY
2587 g_free(client
->pinentry
->ttytype
);
2588 client
->pinentry
->ttytype
= g_strdup(value
);
2590 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2593 else if (g_strcasecmp(name
, (gchar
*)"display") == 0) {
2594 #ifdef WITH_PINENTRY
2595 g_free(client
->pinentry
->display
);
2596 client
->pinentry
->display
= g_strdup(value
);
2598 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2601 else if (g_strcasecmp(name
, (gchar
*)"path") == 0) {
2602 #ifdef WITH_PINENTRY
2603 g_free(client
->pinentry
->path
);
2604 client
->pinentry
->path
= g_strdup(value
);
2606 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2609 else if (g_strcasecmp(name
, (gchar
*)"title") == 0) {
2610 #ifdef WITH_PINENTRY
2611 g_free(client
->pinentry
->title
);
2612 client
->pinentry
->title
= g_strdup(value
);
2614 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2617 else if (g_strcasecmp(name
, (gchar
*)"prompt") == 0) {
2618 #ifdef WITH_PINENTRY
2619 g_free(client
->pinentry
->prompt
);
2620 client
->pinentry
->prompt
= g_strdup(value
);
2622 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2625 else if (g_strcasecmp(name
, (gchar
*)"desc") == 0) {
2626 #ifdef WITH_PINENTRY
2627 g_free(client
->pinentry
->desc
);
2628 client
->pinentry
->desc
= g_strdup(value
);
2630 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2633 else if (g_strcasecmp(name
, (gchar
*)"timeout") == 0) {
2634 #ifndef WITH_PINENTRY
2635 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2638 gint n
= strtol(value
, &p
, 10);
2641 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_INV_VALUE
);
2643 client
->pinentry
->timeout
= n
;
2646 else if (g_strcasecmp(name
, (gchar
*)"pinentry") == 0) {
2647 #ifndef WITH_PINENTRY
2648 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_NOT_IMPLEMENTED
);
2651 gint n
= strtol(value
, &p
, 10);
2653 if (*p
|| n
< 0 || n
> 1)
2654 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_INV_VALUE
);
2656 client
->pinentry
->use
= n
== 0 ? FALSE
: TRUE
;
2659 else if (g_strcasecmp(name
, (gchar
*)"client") == 0)
2660 return parse_client_option(ctx
, value
);
2662 return gpg_err_make(GPG_ERR_SOURCE_USER_1
, GPG_ERR_UNKNOWN_OPTION
);
2664 log_write("OPTION %s=%s", name
, value
);
2668 gpg_error_t
register_commands(assuan_context_t ctx
)
2672 int (*handler
)(assuan_context_t
, char *line
);
2674 { "OPEN", open_command
},
2675 { "SAVE", save_command
},
2676 { "LIST", list_command
},
2677 { "REALPATH", realpath_command
},
2678 { "STORE", store_command
},
2679 { "DELETE", delete_command
},
2680 { "GET", get_command
},
2681 { "ATTR", attr_command
},
2682 { "ISCACHED", iscached_command
},
2683 { "CLEARCACHE", clearcache_command
},
2684 { "CACHETIMEOUT", cachetimeout_command
},
2685 { "GETCONFIG", getconfig_command
},
2686 { "DUMP", dump_command
},
2693 for (i
=0; table
[i
].name
; i
++) {
2694 rc
= assuan_register_command (ctx
, table
[i
].name
, table
[i
].handler
);
2700 rc
= assuan_register_bye_notify(ctx
, cleanup_assuan
);
2704 rc
= assuan_register_option_handler(ctx
, option_handler
);
2708 return assuan_register_reset_notify(ctx
, reset_notify
);
2711 gpg_error_t
try_xml_decrypt(assuan_context_t ctx
, gint fd
, struct stat st
,
2717 guchar tkey
[gcrykeysize
];
2718 struct client_s
*client
= ctx
? assuan_get_pointer(ctx
) : NULL
;
2719 gcry_cipher_hd_t gh
;
2720 guint iter
= 0, n_iter
= 0;
2722 void *outbuf
= NULL
;
2726 file_header_t file_header
;
2729 error
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0);
2737 lseek(fd
, 0, SEEK_SET
);
2738 insize
= st
.st_size
- sizeof(file_header_t
);
2739 iv
= gcry_malloc(gcryblocksize
);
2743 gcry_cipher_close(gh
);
2745 return gpg_error_from_errno(ENOMEM
);
2748 len
= pth_read(fd
, &file_header
, sizeof(file_header_t
));
2750 if (len
!= sizeof(file_header_t
)) {
2754 gcry_cipher_close(gh
);
2758 return gpg_error_from_errno(errno
);
2761 /* No encryption iterations. This is a plain (gzipped) file. */
2762 if (file_header
.iter
== -1) {
2764 * cache_file_count() needs both .used == TRUE and a valid key in
2765 * order for it to count as a used cache entry. Fixes CACHE status
2768 memset(key
, '!', gcrykeysize
);
2769 insize
= st
.st_size
- sizeof(file_header_t
);
2772 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
2773 inbuf
= gcry_malloc(insize
);
2777 gcry_cipher_close(gh
);
2780 return gpg_error_from_errno(ENOMEM
);
2783 len
= pth_read(fd
, inbuf
, insize
);
2785 if (len
!= insize
) {
2789 gcry_cipher_close(gh
);
2793 return gpg_error_from_errno(errno
);
2796 if (file_header
.iter
== -1)
2799 memcpy(tkey
, key
, sizeof(tkey
));
2802 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2804 gcry_cipher_close(gh
);
2805 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2808 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2815 if ((error
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
2817 gcry_cipher_close(gh
);
2818 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2821 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2827 gcry_cipher_close(gh
);
2832 iter_progress
= get_key_file_integer("default", "iteration_progress");
2834 if (ctx
&& iter_progress
> 0 && file_header
.iter
>= iter_progress
) {
2835 error
= assuan_write_status(client
->ctx
, "DECRYPT", "0");
2844 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2852 if ((error
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
2854 gcry_cipher_close(gh
);
2855 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2858 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2865 while (iter
< file_header
.iter
) {
2866 if (ctx
&& iter_progress
> 0 && iter
>= iter_progress
) {
2867 if (!(iter
% iter_progress
)) {
2868 error
= assuan_write_status(ctx
, "DECRYPT", print_fmt("%i",
2869 ++n_iter
* iter_progress
));
2879 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2881 gcry_cipher_close(gh
);
2882 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2885 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2892 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2896 gcry_cipher_close(gh
);
2897 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2900 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2911 if (ctx
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
2912 error
= assuan_write_status(ctx
, "DECRYPT", print_fmt("%i", file_header
.iter
));
2924 if (do_decompress(ctx
, inbuf
, insize
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
2926 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2928 if (zerror
== Z_MEM_ERROR
) {
2930 return gpg_error_from_errno(ENOMEM
);
2932 else if (zerror
!= Z_DATA_ERROR
) {
2936 gcry_cipher_close(gh
);
2938 return EPWMD_BADKEY
;
2947 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
2951 gcry_cipher_close(gh
);
2953 return EPWMD_BADKEY
;
2957 client
->xml
= inbuf
;
2958 client
->len
= insize
;
2961 gcry_cipher_close(gh
);
2969 * This is called after every Assuan command.
2971 void command_finalize(assuan_context_t ctx
, gint error
)
2973 struct client_s
*client
= assuan_get_pointer(ctx
);
2975 unlock_file_mutex(client
);