1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
3 Copyright (C) 2006-2008 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>
47 #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
)
75 if (client
->state
!= STATE_OPEN
)
78 rc
= lock_file_mutex(client
);
83 if (stat(client
->filename
, &st
) == 0 && client
->mtime
) {
84 if (client
->mtime
!= st
.st_mtime
)
85 return EPWMD_FILE_MODIFIED
;
91 static gboolean
encrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
92 void *inbuf
, gsize insize
)
96 if ((rc
= gcry_cipher_encrypt(gh
, outbuf
, outsize
, inbuf
, insize
))) {
97 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
104 static gpg_error_t
decrypt_xml(gcry_cipher_hd_t gh
, void *outbuf
, gsize outsize
,
105 void *inbuf
, gsize insize
)
109 if ((rc
= gcry_cipher_decrypt(gh
, outbuf
, outsize
, inbuf
, insize
)))
110 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
115 static gpg_error_t
parse_xml(assuan_context_t ctx
)
117 struct client_s
*client
= assuan_get_pointer(ctx
);
119 client
->doc
= xmlReadMemory(client
->xml
, client
->len
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
122 return EPWMD_LIBXML_ERROR
;
127 void unlock_file_mutex(struct client_s
*client
)
132 if (client
->has_lock
== FALSE
|| client
->pinentry
->status
!= PINENTRY_NONE
)
134 if (client
->has_lock
== FALSE
)
138 CACHE_LOCK(client
->ctx
);
140 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
146 pth_mutex_release(m
);
147 client
->has_lock
= client
->is_lock_cmd
= FALSE
;
150 gpg_error_t
lock_file_mutex(struct client_s
*client
)
154 if (client
->has_lock
== TRUE
)
157 CACHE_LOCK(client
->ctx
);
159 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
166 if (pth_mutex_acquire(m
, TRUE
, NULL
) == FALSE
) {
167 if (errno
== EBUSY
) {
169 assuan_write_status(client
->ctx
, "LOCKED", N_("Waiting for lock"));
171 pth_mutex_acquire(m
, FALSE
, NULL
);
175 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(errno
));
176 return gpg_error_from_errno(e
);
180 client
->has_lock
= TRUE
;
184 void free_client(struct client_s
*client
)
187 xmlFreeDoc(client
->doc
);
190 gcry_free(client
->xml
);
192 if (client
->filename
)
193 g_free(client
->filename
);
196 gcry_cipher_close(client
->gh
);
199 void cleanup_client(struct client_s
*client
)
201 assuan_context_t ctx
= client
->ctx
;
203 struct pinentry_s
*pin
= client
->pinentry
;
206 unlock_file_mutex(client
);
207 CACHE_LOCK(client
->ctx
);
208 cache_decr_refcount(client
->md5file
);
211 * This may be a new file so don't use a cache slot. save_command() will
212 * set this to FALSE on success.
214 if (client
->new == TRUE
)
215 cache_clear(client
->md5file
, 1);
218 memset(client
, 0, sizeof(struct client_s
));
219 client
->state
= STATE_CONNECTED
;
221 client
->freed
= TRUE
;
223 client
->pinentry
= pin
;
228 gboolean
do_decompress(assuan_context_t ctx
, gpointer in
, gint insize
,
229 gpointer
*out
, glong
*outsize
, gint
*rc
)
235 gchar str
[ASSUAN_LINELENGTH
];
241 z
.avail_out
= zlib_bufsize
;
242 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
245 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
250 *rc
= inflateInit2(&z
, 47);
253 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
258 memset(&h
, 0, sizeof(gz_header
));
259 h
.comment
= (guchar
*)buf
;
260 h
.comm_max
= sizeof(buf
);
261 *rc
= inflateGetHeader(&z
, &h
);
264 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
270 *rc
= inflate(&z
, Z_BLOCK
);
273 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
280 insize
= atoi((gchar
*)h
.comment
);
285 *rc
= inflate(&z
, Z_FINISH
);
292 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
300 z
.next_out
= pout
+ z
.total_out
;
301 z
.avail_out
= zlib_bufsize
;
304 *rc
= assuan_write_status(ctx
, "DECOMPRESS",
305 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
320 } while (*rc
!= Z_STREAM_END
);
323 *rc
= assuan_write_status(ctx
, "DECOMPRESS",
324 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
331 *outsize
= z
.total_out
;
337 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
343 gpg_error_t
read_file_header(const gchar
*filename
, file_header_t
*fh
)
348 fd
= open(filename
, O_RDONLY
);
351 return gpg_error_from_errno(errno
);
353 len
= pth_read(fd
, fh
, sizeof(file_header_t
));
356 if (len
!= sizeof(file_header_t
))
357 return gpg_error_from_errno(errno
);
362 static gpg_error_t
open_command_finalize(assuan_context_t ctx
, guchar shakey
[],
365 struct client_s
*client
= assuan_get_pointer(ctx
);
371 if ((fd
= open_file(client
->filename
, &st
)) == -1) {
373 if (errno
== ENOENT
) {
381 log_write("%s: %s", client
->filename
, strerror(errno
));
382 cleanup_client(client
);
383 memset(shakey
, 0, sizeof(shakey
));
384 return send_syserror(ctx
, rc
);
387 rc
= try_xml_decrypt(ctx
, fd
, st
, shakey
);
391 memset(shakey
, 0, sizeof(shakey
));
392 cleanup_client(client
);
393 return send_error(ctx
, rc
);
397 CACHE_LOCK(client
->ctx
);
399 if (cached
== FALSE
) {
400 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
401 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
402 cleanup_client(client
);
404 return send_error(ctx
, EPWMD_MAX_SLOTS
);
407 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
408 cache_reset_timeout(client
->md5file
, timeout
);
411 cache_set_timeout(client
->md5file
, -2);
416 memset(shakey
, 0, sizeof(shakey
));
420 gcry_free(client
->xml
);
425 if (client
->new == FALSE
)
426 send_cache_status_all();
428 client
->state
= STATE_OPEN
;
431 return send_error(ctx
, rc
);
434 static int open_command(assuan_context_t ctx
, char *line
)
437 guchar shakey
[gcrykeysize
];
438 gboolean cached
= FALSE
;
440 struct client_s
*client
= assuan_get_pointer(ctx
);
442 gchar
*filename
= NULL
;
443 file_header_t file_header
;
445 memset(shakey
, 0, sizeof(shakey
));
447 if ((req
= split_input_line(line
, " ", 2)) != NULL
)
450 if (!filename
|| !*filename
) {
452 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
455 if (valid_filename(filename
) == FALSE
) {
457 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
460 if (client
->state
== STATE_OPEN
)
461 cleanup_client(client
);
463 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
464 CACHE_LOCK(client
->ctx
);
466 if (cache_has_file(client
->md5file
) == FALSE
) {
467 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
470 return send_error(ctx
, EPWMD_MAX_SLOTS
);
474 cache_incr_refcount(client
->md5file
);
476 rc
= lock_file_mutex(client
);
480 return send_error(ctx
, rc
);
483 client
->freed
= FALSE
;
485 if ((rc
= gcry_cipher_open(&client
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
487 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
488 cleanup_client(client
);
489 return send_error(ctx
, rc
);
492 if (stat(filename
, &st
) == 0) {
493 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
494 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
496 cleanup_client(client
);
497 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
500 client
->mtime
= st
.st_mtime
;
503 client
->filename
= g_strdup(filename
);
505 if (!client
->filename
) {
506 memset(shakey
, 0, sizeof(shakey
));
507 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
508 cleanup_client(client
);
510 return send_syserror(ctx
, ENOMEM
);
514 client
->pinentry
->filename
= g_strdup(client
->filename
);
516 if (!client
->pinentry
->filename
) {
517 memset(shakey
, 0, sizeof(shakey
));
518 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
519 cleanup_client(client
);
521 return send_syserror(ctx
, ENOMEM
);
526 * New files don't need a key.
528 if (access(filename
, R_OK
) != 0) {
529 if (errno
!= ENOENT
) {
531 log_write("%s: %s", filename
, strerror(errno
));
533 cleanup_client(client
);
534 return send_syserror(ctx
, rc
);
537 if ((client
->xml
= new_document()) == NULL
) {
538 log_write("%s", strerror(ENOMEM
));
540 cleanup_client(client
);
541 return send_syserror(ctx
, ENOMEM
);
544 client
->len
= xmlStrlen(client
->xml
);
546 client
->filename
= g_strdup(filename
);
548 if (!client
->filename
) {
550 cleanup_client(client
);
551 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
552 return send_syserror(ctx
, ENOMEM
);
555 memset(shakey
, 0, sizeof(shakey
));
557 if (req
[1] && *req
[1])
558 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
561 return open_command_finalize(ctx
, shakey
, cached
);
564 rc
= read_file_header(filename
, &file_header
);
568 cleanup_client(client
);
569 return send_error(ctx
, rc
);
572 if (file_header
.iter
== -1)
575 CACHE_LOCK(client
->ctx
);
576 cached
= cache_get_key(client
->md5file
, shakey
);
579 if (cached
== FALSE
) {
581 * No key specified and no matching filename found in the cache. Use
582 * pinentry to retrieve the key. Cannot return assuan_process_done()
583 * here otherwise the command will be interrupted. The event loop in
584 * client_thread() will poll the file descriptor waiting for it to
585 * become ready to read a pinentry_key_s which will contain the
586 * entered key or rc. It will then call open_command_finalize() to
587 * to finish the command.
589 if (!req
[1] || !*req
[1]) {
591 gboolean b
= get_key_file_boolean(filename
, "enable_pinentry");
593 /* From set_pinentry_defaults(). */
594 if (client
->pinentry
->enable
== FALSE
||
595 (client
->pinentry
->enable
== -1 && b
== FALSE
)) {
596 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
601 lock_pin_mutex(client
);
602 client
->pinentry
->which
= PINENTRY_OPEN
;
603 rc
= pinentry_fork(ctx
);
606 unlock_pin_mutex(client
->pinentry
);
607 cleanup_client(client
);
608 return send_error(ctx
, rc
);
611 client
->pinentry
->cb
= open_command_finalize
;
612 client
->pinentry
->status
= PINENTRY_INIT
;
615 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
620 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
625 return open_command_finalize(ctx
, shakey
, cached
);
628 gboolean
do_compress(assuan_context_t ctx
, gint level
, gpointer data
,
629 gint size
, gpointer
*out
, glong
*outsize
, gint
*rc
)
635 gint cmd
= Z_NO_FLUSH
;
636 gchar str
[ASSUAN_LINELENGTH
];
640 z
.next_in
= pin
= data
;
641 z
.avail_in
= size
< zlib_bufsize
? size
: zlib_bufsize
;
642 z
.avail_out
= zlib_bufsize
;
643 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
646 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
651 *rc
= deflateInit2(&z
, level
, Z_DEFLATED
, 31, 8, Z_DEFAULT_STRATEGY
);
654 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
659 memset(&h
, 0, sizeof(gz_header
));
660 g_snprintf(buf
, sizeof(buf
), "%i", size
);
661 h
.comment
= (guchar
*)buf
;
662 *rc
= deflateSetHeader(&z
, &h
);
665 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
674 *rc
= deflate(&z
, cmd
);
681 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
689 z
.next_out
= pout
+ z
.total_out
;
690 z
.avail_out
= zlib_bufsize
;
693 if (!z
.avail_in
&& z
.total_in
< size
) {
694 if (z
.total_in
+ zlib_bufsize
> size
)
695 z
.avail_in
= size
- z
.total_in
;
697 z
.avail_in
= zlib_bufsize
;
700 *rc
= assuan_write_status(ctx
, "COMPRESS",
701 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
708 if (z
.total_in
>= size
)
719 } while (*rc
!= Z_STREAM_END
);
722 *rc
= assuan_write_status(ctx
, "COMPRESS",
723 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
730 *outsize
= z
.total_out
;
736 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
742 gpg_error_t
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
743 const gchar
*filename
, gpointer data
, size_t insize
, guchar
*shakey
,
749 guchar tkey
[gcrykeysize
];
752 guint iter_progress
= 0, n_iter
= 0, xiter
= 0;
753 gchar tmp
[FILENAME_MAX
];
756 file_header_t file_header
;
757 gchar str
[ASSUAN_LINELENGTH
];
761 * cache_file_count() needs both .used == TRUE and a valid key in
762 * order for it to count as a used cache entry. Fixes CACHE status
765 memset(shakey
, '!', gcrykeysize
);
767 file_header
.iter
= iter
;
771 if (insize
/ gcryblocksize
) {
772 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
774 if (insize
% gcryblocksize
)
775 len
+= gcryblocksize
;
779 * Resize the existing xml buffer to the block size required by gcrypt
780 * rather than duplicating it and wasting memory.
782 inbuf
= gcry_realloc(data
, len
);
785 return gpg_error_from_errno(ENOMEM
);
788 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
789 memcpy(tkey
, shakey
, sizeof(tkey
));
792 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
794 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
798 file_header
.iter
= iter
;
801 iter_progress
= get_key_file_integer("global", "iteration_progress");
803 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
804 rc
= assuan_write_status(client
->ctx
, "ENCRYPT", "0");
812 while (xiter
< file_header
.iter
) {
813 if (client
&& iter_progress
> 0 && xiter
>= iter_progress
) {
814 if (!(xiter
% iter_progress
)) {
815 rc
= assuan_write_status(client
->ctx
, "ENCRYPT",
816 print_fmt(str
, sizeof(str
), "%i", ++n_iter
* iter_progress
));
825 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
826 sizeof(file_header
.iv
)))) {
828 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
832 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
835 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
843 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
844 sizeof(file_header
.iv
)))) {
846 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
850 if ((rc
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
852 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
856 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
858 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
862 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
863 rc
= assuan_write_status(client
->ctx
, "ENCRYPT",
864 print_fmt(str
, sizeof(str
), "%i", file_header
.iter
));
874 if (stat(filename
, &st
) == 0) {
875 mode
= st
.st_mode
& (S_IRWXU
|S_IRWXG
|S_IRWXO
);
878 * FIXME What if the file has an ACL?
880 if (!(mode
& S_IWUSR
)) {
882 return gpg_error_from_errno(EACCES
);
886 if (errno
!= ENOENT
) {
889 return gpg_error_from_errno(rc
);
893 g_snprintf(tmp
, sizeof(tmp
), ".%s.tmp", filename
);
895 if ((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
898 p
= strrchr(tmp
, '/');
900 log_write("%s: %s", p
, strerror(rc
));
901 return gpg_error_from_errno(rc
);
906 * xml_import() from command line.
910 len
= pth_write(fd
, &file_header
, sizeof(file_header_t
));
912 if (len
!= sizeof(file_header
)) {
921 return gpg_error_from_errno(len
);
924 len
= pth_write(fd
, inbuf
, insize
);
935 return gpg_error_from_errno(len
);
938 if (fsync(fd
) == -1) {
947 return gpg_error_from_errno(len
);
953 if (mode
&& get_key_file_boolean(filename
, "backup") == TRUE
) {
954 gchar tmp2
[FILENAME_MAX
];
956 g_snprintf(tmp2
, sizeof(tmp2
), "%s.backup", filename
);
958 if (rename(filename
, tmp2
) == -1) {
962 return gpg_error_from_errno(len
);
966 if (rename(tmp
, filename
) == -1) {
970 return gpg_error_from_errno(len
);
974 chmod(filename
, mode
);
981 static gpg_error_t
save_command_finalize(assuan_context_t ctx
,
982 guchar shakey
[], gboolean cached
)
984 struct client_s
*client
= assuan_get_pointer(ctx
);
996 xmlDocDumpFormatMemory(client
->doc
, &p
, &len
, 0);
999 iter
= get_key_file_integer(client
->filename
, "compression_level");
1004 if (do_compress(ctx
, iter
, xmlbuf
, len
, &outbuf
, &outsize
, &zrc
) == FALSE
) {
1005 memset(shakey
, 0, sizeof(shakey
));
1008 if (zrc
== Z_MEM_ERROR
) {
1009 return send_syserror(ctx
, ENOMEM
);
1012 return send_error(ctx
, GPG_ERR_COMPR_ALGO
);
1020 iter
= get_key_file_integer(client
->filename
, "iterations");
1021 rc
= do_xml_encrypt(client
, client
->gh
, client
->filename
, xmlbuf
, len
, shakey
, iter
);
1024 memset(shakey
, 0, sizeof(shakey
));
1025 return send_error(ctx
, rc
);
1028 stat(client
->filename
, &st
);
1029 client
->mtime
= st
.st_mtime
;
1030 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
1031 CACHE_LOCK(client
->ctx
);
1034 memset(shakey
, 0, sizeof(shakey
));
1035 cache_reset_timeout(client
->md5file
, timeout
);
1038 if (client
->new == TRUE
)
1039 send_cache_status_all();
1041 client
->new = FALSE
;
1042 return send_error(ctx
, 0);
1045 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
1046 memset(shakey
, 0, sizeof(shakey
));
1047 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
1049 return send_error(ctx
, EPWMD_MAX_SLOTS
);
1052 client
->new = FALSE
;
1053 memset(shakey
, 0, sizeof(shakey
));
1054 cache_reset_timeout(client
->md5file
, timeout
);
1056 send_cache_status_all();
1057 return send_error(ctx
, 0);
1060 static int save_command(assuan_context_t ctx
, char *line
)
1062 gboolean cached
= FALSE
;
1063 guchar shakey
[gcrykeysize
];
1065 struct client_s
*client
= assuan_get_pointer(ctx
);
1068 memset(shakey
, 0, sizeof(shakey
));
1069 rc
= file_modified(client
);
1072 log_write("%s: %s", client
->filename
? client
->filename
: "",
1074 return send_error(ctx
, rc
);
1077 rc
= lock_file_mutex(client
);
1080 return send_error(ctx
, rc
);
1082 if (stat(client
->filename
, &st
) == -1 && errno
!= ENOENT
)
1083 return send_syserror(ctx
, errno
);
1085 if (errno
!= ENOENT
&& !S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
1086 log_write("%s: %s", client
->filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
1087 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
1090 if (get_key_file_integer(client
->filename
, "iterations") == -1)
1093 if (!line
|| !*line
) {
1094 guchar tmp
[sizeof(shakey
)];
1097 memset(tmp
, '!', sizeof(tmp
));
1099 if (cache_get_key(client
->md5file
, shakey
) == FALSE
||
1100 memcmp(shakey
, tmp
, sizeof(shakey
)) == 0) {
1102 #ifdef WITH_PINENTRY
1103 if (get_key_file_boolean(client
->filename
, "enable_pinentry") == FALSE
) {
1104 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1108 lock_pin_mutex(client
);
1109 client
->pinentry
->which
= PINENTRY_SAVE
;
1110 rc
= pinentry_fork(ctx
);
1113 unlock_pin_mutex(client
->pinentry
);
1114 return send_error(ctx
, rc
);
1117 client
->pinentry
->cb
= save_command_finalize
;
1118 client
->pinentry
->status
= PINENTRY_INIT
;
1121 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1131 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, line
, strlen(line
));
1132 memset(line
, 0, strlen(line
));
1136 return save_command_finalize(ctx
, shakey
, cached
);
1139 static int delete_command(assuan_context_t ctx
, char *line
)
1141 struct client_s
*client
= assuan_get_pointer(ctx
);
1146 rc
= file_modified(client
);
1149 log_write("%s: %s", client
->filename
? client
->filename
: "",
1151 return send_error(ctx
, rc
);
1154 if (strchr(line
, '\t'))
1155 req
= split_input_line(line
, "\t", -1);
1157 req
= split_input_line(line
, " ", -1);
1160 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1162 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1166 return send_error(ctx
, rc
);
1170 * No sub-node defined. Remove the entire node (account).
1179 return send_error(ctx
, 0);
1182 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1186 return send_error(ctx
, rc
);
1193 return send_error(ctx
, 0);
1197 * Don't return with assuan_process_done() here. This has been called from
1198 * assuan_process_next() and the command should be finished in
1201 static int store_command_finalize(gpointer data
, gint assuan_rc
, guchar
*line
,
1204 assuan_context_t ctx
= data
;
1205 struct client_s
*client
= assuan_get_pointer(ctx
);
1208 gpg_error_t rc
= file_modified(client
);
1210 if (assuan_rc
|| rc
) {
1217 return assuan_rc
? assuan_rc
: rc
;
1220 req
= split_input_line((gchar
*)line
, "\t", 0);
1228 return EPWMD_COMMAND_SYNTAX
;
1230 if (valid_xml_element((xmlChar
*)*req
) == FALSE
) {
1232 return EPWMD_INVALID_ELEMENT
;
1235 if (valid_element_path(req
+1, TRUE
) == FALSE
) {
1237 return EPWMD_INVALID_ELEMENT
;
1241 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1243 if (rc
&& rc
== EPWMD_ELEMENT_NOT_FOUND
) {
1244 rc
= new_account(client
->doc
, *req
);
1261 create_elements_cb(n
, req
+1, &rc
, NULL
);
1263 find_elements(client
->doc
, n
->children
, req
+1, &rc
,
1264 NULL
, NULL
, create_elements_cb
, FALSE
, 0, NULL
);
1268 client
->inquire_status
= INQUIRE_DONE
;
1272 static int store_command(assuan_context_t ctx
, char *line
)
1274 struct client_s
*client
= assuan_get_pointer(ctx
);
1275 gpg_error_t rc
= file_modified(client
);
1278 log_write("%s: %s", client
->filename
? client
->filename
: "",
1280 return send_error(ctx
, rc
);
1283 rc
= assuan_inquire_ext(ctx
, "STORE", 0, store_command_finalize
, ctx
);
1286 return send_error(ctx
, rc
);
1288 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1289 client
->inquire_status
= INQUIRE_BUSY
;
1293 static int get_command(assuan_context_t ctx
, char *line
)
1295 struct client_s
*client
= assuan_get_pointer(ctx
);
1300 rc
= file_modified(client
);
1303 log_write("%s: %s", client
->filename
? client
->filename
: "",
1305 return send_error(ctx
, rc
);
1308 req
= split_input_line(line
, "\t", -1);
1310 if (!req
|| !*req
) {
1312 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1315 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1319 return send_error(ctx
, rc
);
1323 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1328 return send_error(ctx
, rc
);
1330 if (!n
|| !n
->children
)
1331 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1333 n
= find_text_node(n
->children
);
1335 if (!n
|| !n
->content
|| !*n
->content
)
1336 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1338 rc
= assuan_send_data(ctx
, n
->content
, xmlStrlen(n
->content
));
1339 return send_error(ctx
, rc
);
1342 static xmlNodePtr
realpath_elements_cb(xmlNodePtr node
, gchar
**target
,
1343 gpg_error_t
*rc
, gchar
**req_orig
, void *data
)
1345 gchar
*path
= *(gchar
**)data
;
1346 gchar
*tmp
= NULL
, *result
;
1350 *(gchar
**)data
= NULL
;
1353 path
= g_strjoinv("\t", target
);
1356 *rc
= gpg_error_from_errno(ENOMEM
);
1361 tmp
= g_strjoinv("\t", req_orig
);
1365 *rc
= gpg_error_from_errno(ENOMEM
);
1371 result
= g_strdup_printf("%s\t%s", path
, tmp
);
1373 result
= g_strdup(path
);
1376 *rc
= gpg_error_from_errno(ENOMEM
);
1384 *(gchar
**)data
= result
;
1388 static int realpath_command(assuan_context_t ctx
, char *line
)
1391 struct client_s
*client
= assuan_get_pointer(ctx
);
1399 rc
= file_modified(client
);
1402 log_write("%s: %s", client
->filename
? client
->filename
: "",
1404 return send_error(ctx
, rc
);
1407 if (strchr(line
, '\t') != NULL
) {
1408 if ((req
= split_input_line(line
, "\t", 0)) == NULL
)
1409 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1412 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1413 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1416 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1420 return send_error(ctx
, rc
);
1423 rp
= g_strjoinv("\t", req
);
1427 return send_syserror(ctx
, ENOMEM
);
1431 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
,
1432 NULL
, realpath_elements_cb
, NULL
, FALSE
, 0, &rp
);
1437 return send_error(ctx
, rc
);
1441 string
= g_string_new(rp
);
1446 return send_syserror(ctx
, ENOMEM
);
1449 for (i
= 0, t
= string
->str
+ i
; *t
; t
++, i
++) {
1450 if ((!i
&& *t
!= '!') || (*t
== '\t' && *(t
+1) && *(t
+1) != '!')) {
1451 string
= g_string_insert_c(string
, !i
? i
++ : ++i
, '!');
1456 rc
= assuan_send_data(ctx
, string
->str
, string
->len
);
1457 g_string_free(string
, TRUE
);
1458 return send_error(ctx
, rc
);
1461 static int list_command(assuan_context_t ctx
, char *line
)
1463 struct client_s
*client
= assuan_get_pointer(ctx
);
1465 struct element_list_s
*elements
= NULL
;
1468 if (disable_list_and_dump
== TRUE
)
1469 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
1471 rc
= file_modified(client
);
1474 log_write("%s: %s", client
->filename
, pwmd_strerror(rc
));
1475 return send_error(ctx
, rc
);
1481 rc
= list_accounts(client
->doc
, &str
);
1484 return send_error(ctx
, rc
);
1486 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1487 g_string_free(str
, TRUE
);
1488 return send_error(ctx
, rc
);
1491 elements
= g_malloc0(sizeof(struct element_list_s
));
1494 rc
= gpg_err_code_from_errno(ENOMEM
);
1498 rc
= create_path_list(client
->doc
, elements
, line
);
1504 gint total
= g_slist_length(elements
->list
);
1509 rc
= EPWMD_EMPTY_ELEMENT
;
1513 str
= g_string_new(NULL
);
1516 rc
= gpg_err_code_from_errno(ENOMEM
);
1520 for (i
= 0; i
< total
; i
++) {
1521 tmp
= g_slist_nth_data(elements
->list
, i
);
1522 g_string_append_printf(str
, "%s%s", tmp
, i
+1 == total
? "" : "\n");
1525 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1526 g_string_free(str
, TRUE
);
1529 rc
= EPWMD_EMPTY_ELEMENT
;
1533 gint total
= g_slist_length(elements
->list
);
1536 for (i
= 0; i
< total
; i
++) {
1537 tmp
= g_slist_nth_data(elements
->list
, i
);
1541 g_slist_free(elements
->list
);
1543 if (elements
->prefix
)
1544 g_free(elements
->prefix
);
1549 return send_error(ctx
, rc
);
1552 static gpg_error_t
add_attribute(xmlNodePtr node
, const gchar
*name
,
1557 if ((a
= xmlHasProp(node
, (xmlChar
*)name
)) == NULL
) {
1558 a
= xmlNewProp(node
, (xmlChar
*)name
, (xmlChar
*)value
);
1561 return EPWMD_LIBXML_ERROR
;
1564 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1570 * req[0] - element path
1572 static int attribute_list(assuan_context_t ctx
, gchar
**req
)
1574 struct client_s
*client
= assuan_get_pointer(ctx
);
1575 gchar
**attrlist
= NULL
;
1577 gchar
**path
= NULL
;
1583 if (!req
|| !req
[0])
1584 return EPWMD_COMMAND_SYNTAX
;
1586 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1588 * The first argument may be only an account.
1590 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
1591 return EPWMD_COMMAND_SYNTAX
;
1594 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1602 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1603 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1613 for (a
= n
->properties
; a
; a
= a
->next
) {
1616 if ((pa
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1618 g_strfreev(attrlist
);
1620 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
1621 return gpg_error_from_errno(ENOMEM
);
1626 attrlist
[i
] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1629 g_strfreev(attrlist
);
1630 return gpg_error_from_errno(ENOMEM
);
1633 attrlist
[++i
] = NULL
;
1637 return EPWMD_EMPTY_ELEMENT
;
1639 line
= g_strjoinv("\n", attrlist
);
1642 g_strfreev(attrlist
);
1643 return gpg_error_from_errno(ENOMEM
);
1646 rc
= assuan_send_data(ctx
, line
, strlen(line
));
1648 g_strfreev(attrlist
);
1653 * req[0] - attribute
1654 * req[1] - element path
1656 static int attribute_delete(struct client_s
*client
, gchar
**req
)
1660 gchar
**path
= NULL
;
1663 if (!req
|| !req
[0] || !req
[1])
1664 return EPWMD_COMMAND_SYNTAX
;
1666 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1668 * The first argument may be only an account.
1670 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1671 return EPWMD_COMMAND_SYNTAX
;
1675 * Don't remove the "name" attribute for the account element. To remove an
1676 * account use DELETE <account>.
1678 if (!path
[1] && xmlStrEqual((xmlChar
*)req
[0], (xmlChar
*)"name")) {
1679 rc
= EPWMD_ATTR_SYNTAX
;
1683 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1689 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1690 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1698 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
)
1699 return EPWMD_ATTR_NOT_FOUND
;
1701 if (xmlRemoveProp(a
) == -1)
1702 return EPWMD_LIBXML_ERROR
;
1711 static xmlNodePtr
create_element_path(struct client_s
*client
, gchar
***path
,
1714 gchar
**src
= *path
;
1715 gchar
**src_orig
= g_strdupv(src
);
1716 xmlNodePtr n
= NULL
;
1721 *rc
= gpg_error_from_errno(ENOMEM
);
1726 n
= find_account(client
->doc
, &src
, rc
, NULL
, 0);
1729 if (*rc
== EPWMD_ELEMENT_NOT_FOUND
) {
1730 *rc
= new_account(client
->doc
, src
[0]);
1743 n
= create_target_elements_cb(n
, src
+1, rc
, NULL
);
1745 n
= find_elements(client
->doc
, n
->children
, src
+1, rc
,
1746 NULL
, NULL
, create_target_elements_cb
, FALSE
, 0, NULL
);
1752 * Reset the position of the element tree now that the elements
1753 * have been created.
1758 n
= find_account(client
->doc
, &src
, rc
, NULL
, 0);
1763 n
= find_elements(client
->doc
, n
->children
, src
+1, rc
,
1764 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1772 g_strfreev(src_orig
);
1779 * Creates a "target" attribute. When other commands encounter an element with
1780 * this attribute, the element path is modified to the target value. If the
1781 * source element path doesn't exist when using 'ATTR SET target', it is
1782 * created, but the destination element path must exist.
1784 * req[0] - source element path
1785 * req[1] - destination element path
1787 static gpg_error_t
target_attribute(struct client_s
*client
, gchar
**req
)
1789 gchar
**src
, **dst
, *line
= NULL
;
1793 if (!req
|| !req
[0] || !req
[1])
1794 return EPWMD_COMMAND_SYNTAX
;
1796 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1798 * The first argument may be only an account.
1800 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
)
1801 return EPWMD_COMMAND_SYNTAX
;
1804 if (valid_element_path(src
, FALSE
) == FALSE
) {
1806 return EPWMD_INVALID_ELEMENT
;
1809 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1811 * The first argument may be only an account.
1813 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1814 rc
= EPWMD_COMMAND_SYNTAX
;
1819 n
= find_account(client
->doc
, &dst
, &rc
, NULL
, 0);
1822 * Make sure the destination element path exists.
1828 n
= find_elements(client
->doc
, n
->children
, dst
+1, &rc
,
1829 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1835 n
= create_element_path(client
, &src
, &rc
);
1840 line
= g_strjoinv("\t", dst
);
1843 rc
= gpg_error_from_errno(ENOMEM
);
1847 rc
= add_attribute(n
, "target", line
);
1857 * req[0] - account name
1860 static gpg_error_t
name_attribute(struct client_s
*client
, gchar
**req
)
1866 tmp
= g_strdupv(req
);
1869 return gpg_error_from_errno(ENOMEM
);
1871 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1877 if (g_utf8_collate(req
[0], req
[1]) == 0)
1881 * Will not overwrite an existing account.
1883 tmp
= g_strdupv(req
+1);
1886 return gpg_error_from_errno(ENOMEM
);
1888 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1891 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
)
1895 return EPWMD_ACCOUNT_EXISTS
;
1898 * Whitespace not allowed in account names.
1900 if (contains_whitespace(req
[1]) == TRUE
)
1901 return EPWMD_ATTR_SYNTAX
;
1903 tmp
= g_strdupv(req
);
1906 return gpg_error_from_errno(ENOMEM
);
1908 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1912 return EPWMD_ELEMENT_NOT_FOUND
;
1914 return add_attribute(n
, "name", req
[1]);
1918 * req[0] - attribute
1919 * req[1] - element path
1921 static int attribute_get(assuan_context_t ctx
, gchar
**req
)
1923 struct client_s
*client
= assuan_get_pointer(ctx
);
1929 if (!req
|| !req
[0] || !req
[1])
1930 return EPWMD_COMMAND_SYNTAX
;
1932 if (strchr(req
[1], '\t')) {
1933 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
)
1934 return EPWMD_COMMAND_SYNTAX
;
1937 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1938 return EPWMD_COMMAND_SYNTAX
;
1941 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1947 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1948 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1956 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
)
1957 return EPWMD_ATTR_NOT_FOUND
;
1959 rc
= assuan_send_data(ctx
, a
, xmlStrlen(a
));
1969 * req[0] - attribute
1970 * req[1] - element path
1973 static int attribute_set(struct client_s
*client
, gchar
**req
)
1975 gchar
**path
= NULL
;
1979 if (!req
|| !req
[0] || !req
[1] || !req
[2])
1980 return EPWMD_COMMAND_SYNTAX
;
1983 * Reserved attribute names.
1985 if (g_utf8_collate(req
[0], "name") == 0) {
1987 * Only reserved for the account element. Not the rest of the
1990 if (strchr(req
[1], '\t') == NULL
)
1991 return name_attribute(client
, req
+ 1);
1993 else if (g_utf8_collate(req
[0], "target") == 0)
1994 return target_attribute(client
, req
+ 1);
1996 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1998 * The first argument may be only an account.
2000 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2001 return EPWMD_COMMAND_SYNTAX
;
2004 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
2010 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
2011 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2018 return add_attribute(n
, req
[0], req
[2]);
2027 * req[1] - attribute name or element path if command is LIST
2028 * req[2] - element path
2029 * req[2] - element path or value
2031 static int attr_command(assuan_context_t ctx
, char *line
)
2033 struct client_s
*client
= assuan_get_pointer(ctx
);
2037 rc
= file_modified(client
);
2040 log_write("%s: %s", client
->filename
? client
->filename
: "",
2042 return send_error(ctx
, rc
);
2045 req
= split_input_line(line
, " ", 4);
2047 if (!req
|| !req
[0] || !req
[1]) {
2049 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2052 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
2053 rc
= attribute_set(client
, req
+1);
2054 else if (g_ascii_strcasecmp(req
[0], "GET") == 0)
2055 rc
= attribute_get(ctx
, req
+1);
2056 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
2057 rc
= attribute_delete(client
, req
+1);
2058 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
2059 rc
= attribute_list(ctx
, req
+1);
2061 rc
= EPWMD_COMMAND_SYNTAX
;
2064 return send_error(ctx
, rc
);
2067 static int iscached_command(assuan_context_t ctx
, char *line
)
2069 gchar
**req
= split_input_line(line
, " ", 0);
2072 if (!req
|| !*req
) {
2074 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2077 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2081 if (cache_iscached(md5file
) == FALSE
) {
2083 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2087 return send_error(ctx
, 0);
2090 gpg_error_t
send_cache_status(assuan_context_t ctx
)
2093 struct client_s
*client
= assuan_get_pointer(ctx
);
2094 gchar buf
[ASSUAN_LINELENGTH
];
2096 CACHE_LOCK(client
->ctx
);
2097 tmp
= print_fmt(buf
, sizeof(buf
), "%i %i",
2099 (cache_size
/ sizeof(file_cache_t
)) - cache_file_count());
2102 return assuan_write_status(ctx
, "CACHE", buf
);
2105 static int clearcache_command(assuan_context_t ctx
, char *line
)
2107 struct client_s
*client
= assuan_get_pointer(ctx
);
2108 gchar
**req
= split_input_line(line
, " ", 0);
2113 if (!req
|| !*req
) {
2115 cache_clear(client
->md5file
, 2);
2117 return send_error(ctx
, 0);
2120 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2123 if (cache_clear(md5file
, 1) == FALSE
) {
2125 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2129 return send_error(ctx
, 0);
2132 static int cachetimeout_command(assuan_context_t ctx
, char *line
)
2136 gchar
**req
= split_input_line(line
, " ", 0);
2138 struct client_s
*client
= assuan_get_pointer(ctx
);
2140 if (!req
|| !*req
|| !req
[1]) {
2142 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2146 timeout
= strtol(req
[0], &p
, 10);
2148 if (errno
!= 0 || *p
!= 0) {
2150 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2153 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2155 CACHE_LOCK(client
->ctx
);
2157 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2159 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2163 return send_error(ctx
, 0);
2166 static int dump_command(assuan_context_t ctx
, char *line
)
2170 struct client_s
*client
= assuan_get_pointer(ctx
);
2173 if (disable_list_and_dump
== TRUE
)
2174 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2176 rc
= file_modified(client
);
2179 log_write("%s: %s", client
->filename
? client
->filename
: "",
2181 return send_error(ctx
, rc
);
2184 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2187 return send_syserror(ctx
, ENOMEM
);
2189 rc
= assuan_send_data(ctx
, xml
, len
);
2191 return send_error(ctx
, rc
);
2194 static int getconfig_command(assuan_context_t ctx
, gchar
*line
)
2196 struct client_s
*client
= assuan_get_pointer(ctx
);
2200 if (strcmp(line
, "key") == 0 || strcmp(line
, "key_file") == 0)
2201 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2203 p
= get_key_file_string(client
->filename
? client
->filename
: "global", line
);
2206 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2208 tmp
= expand_homedir(p
);
2212 return send_syserror(ctx
, ENOMEM
);
2217 rc
= assuan_send_data(ctx
, p
, strlen(p
));
2219 return send_error(ctx
, rc
);
2222 static int xpath_command(assuan_context_t ctx
, gchar
*line
)
2224 struct client_s
*client
= assuan_get_pointer(ctx
);
2226 xmlXPathContextPtr xp
;
2227 xmlXPathObjectPtr result
;
2228 xmlBufferPtr buf
= NULL
;
2231 rc
= file_modified(client
);
2234 log_write("%s: %s", client
->filename
? client
->filename
: "",
2236 return send_error(ctx
, rc
);
2239 if (!line
|| !*line
)
2240 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2242 if ((req
= split_input_line(line
, "\t", 2)) == NULL
) {
2243 if (strv_printf(&req
, "%s", line
) == FALSE
)
2244 return send_syserror(ctx
, ENOMEM
);
2247 xp
= xmlXPathNewContext(client
->doc
);
2250 return send_error(ctx
, EPWMD_LIBXML_ERROR
);
2252 result
= xmlXPathEvalExpression((xmlChar
*)req
[0], xp
);
2255 xmlXPathFreeContext(xp
);
2256 return send_error(ctx
, EPWMD_LIBXML_ERROR
);
2259 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2260 rc
= EPWMD_EMPTY_ELEMENT
;
2264 rc
= recurse_xpath_nodeset(client
->doc
, result
->nodesetval
,
2265 (xmlChar
*)req
[1], &buf
);
2269 else if (!req
[1] && !xmlBufferLength(buf
)) {
2270 rc
= EPWMD_EMPTY_ELEMENT
;
2276 rc
= assuan_send_data(ctx
, xmlBufferContent(buf
), xmlBufferLength(buf
));
2285 xmlXPathFreeObject(result
);
2288 xmlXPathFreeContext(xp
);
2290 return send_error(ctx
, rc
);
2293 static int import_command_finalize(gpointer data
, gint assuan_rc
, guchar
*line
,
2296 struct client_s
*client
= assuan_get_pointer((assuan_context_t
)data
);
2297 gpg_error_t rc
= file_modified(client
);
2298 gchar
**req
, **path
= NULL
, **path_orig
= NULL
, *content
;
2300 xmlNodePtr n
, root
, copy
;
2302 if (assuan_rc
|| rc
) {
2309 return assuan_rc
? assuan_rc
: rc
;
2312 req
= split_input_line((gchar
*)line
, " ", 2);
2320 return EPWMD_COMMAND_SYNTAX
;
2322 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
2323 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
2324 return EPWMD_COMMAND_SYNTAX
;
2329 if (!content
|| !*content
) {
2330 rc
= EPWMD_COMMAND_SYNTAX
;
2334 if (valid_xml_element((xmlChar
*)*path
) == FALSE
) {
2335 rc
= EPWMD_INVALID_ELEMENT
;
2339 if (valid_element_path(path
+1, FALSE
) == FALSE
) {
2340 rc
= EPWMD_INVALID_ELEMENT
;
2344 doc
= xmlReadDoc((xmlChar
*)content
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
2347 rc
= EPWMD_LIBXML_ERROR
;
2351 root
= xmlDocGetRootElement(doc
);
2352 path_orig
= g_strdupv(path
);
2356 rc
= gpg_error_from_errno(ENOMEM
);
2360 if (strv_printf(&path
, "%s", (gchar
*)root
->name
) == FALSE
) {
2361 g_strfreev(path_orig
);
2363 rc
= gpg_error_from_errno(ENOMEM
);
2367 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
2369 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
) {
2370 g_strfreev(path_orig
);
2375 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2377 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
) {
2378 g_strfreev(path_orig
);
2383 xmlNodePtr parent
= n
->parent
;
2394 if (rc
== EPWMD_ELEMENT_NOT_FOUND
) {
2395 n
= create_element_path(client
, &path
, &rc
);
2403 copy
= xmlCopyNode(root
, 1);
2404 n
= xmlAddChild(n
, copy
);
2408 rc
= EPWMD_LIBXML_ERROR
;
2413 client
->inquire_status
= INQUIRE_DONE
;
2417 static int import_command(assuan_context_t ctx
, gchar
*line
)
2420 struct client_s
*client
= assuan_get_pointer(ctx
);
2422 rc
= file_modified(client
);
2425 log_write("%s: %s", client
->filename
? client
->filename
: "",
2427 return send_error(ctx
, rc
);
2430 rc
= assuan_inquire_ext(ctx
, "IMPORT", 0, import_command_finalize
, ctx
);
2433 return send_error(ctx
, rc
);
2435 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2436 client
->inquire_status
= INQUIRE_BUSY
;
2440 static int lock_command(assuan_context_t ctx
, gchar
*line
)
2443 struct client_s
*client
= assuan_get_pointer(ctx
);
2445 rc
= file_modified(client
);
2448 log_write("%s: %s", client
->filename
? client
->filename
: "",
2450 return send_error(ctx
, rc
);
2453 rc
= lock_file_mutex(client
);
2456 client
->is_lock_cmd
= TRUE
;
2458 return send_error(ctx
, rc
);
2461 static int unlock_command(assuan_context_t ctx
, gchar
*line
)
2464 struct client_s
*client
= assuan_get_pointer(ctx
);
2466 rc
= file_modified(client
);
2469 log_write("%s: %s", client
->filename
? client
->filename
: "",
2471 return send_error(ctx
, rc
);
2474 unlock_file_mutex(client
);
2475 return send_error(ctx
, 0);
2478 void cleanup_assuan(assuan_context_t ctx
)
2480 struct client_s
*cl
= assuan_get_pointer(ctx
);
2486 static void reset_notify(assuan_context_t ctx
)
2488 struct client_s
*cl
= assuan_get_pointer(ctx
);
2494 static gpg_error_t
parse_client_option(assuan_context_t ctx
, const gchar
*line
)
2496 gchar name
[32] = {0}, value
[256] = {0};
2498 if (sscanf(line
, " %31[a-zA-Z] = %255c", name
, value
) != 2)
2499 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_SYNTAX
);
2501 if (g_strcasecmp(name
, (gchar
*)"NAME") == 0) {
2502 pth_attr_t attr
= pth_attr_of(pth_self());
2504 log_write("OPTION CLIENT %s", line
);
2505 pth_attr_set(attr
, PTH_ATTR_NAME
, value
);
2506 pth_attr_destroy(attr
);
2509 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2514 static int option_handler(assuan_context_t ctx
, const gchar
*name
,
2517 #ifdef WITH_PINENTRY
2518 struct client_s
*client
= assuan_get_pointer(ctx
);
2521 if (!value
|| !*value
)
2522 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2524 if (g_strcasecmp(name
, (gchar
*)"client") == 0)
2525 return parse_client_option(ctx
, value
);
2527 #ifdef WITH_PINENTRY
2528 if (g_strcasecmp(name
, (gchar
*)"ttyname") == 0) {
2529 g_free(client
->pinentry
->ttyname
);
2530 client
->pinentry
->ttyname
= g_strdup(value
);
2532 else if (g_strcasecmp(name
, (gchar
*)"ttytype") == 0) {
2533 g_free(client
->pinentry
->ttytype
);
2534 client
->pinentry
->ttytype
= g_strdup(value
);
2536 else if (g_strcasecmp(name
, (gchar
*)"display") == 0) {
2537 g_free(client
->pinentry
->display
);
2538 client
->pinentry
->display
= g_strdup(value
);
2540 else if (g_strcasecmp(name
, (gchar
*)"path") == 0) {
2541 g_free(client
->pinentry
->path
);
2542 client
->pinentry
->path
= g_strdup(value
);
2544 else if (g_strcasecmp(name
, (gchar
*)"title") == 0) {
2545 g_free(client
->pinentry
->title
);
2546 client
->pinentry
->title
= g_strdup(value
);
2548 else if (g_strcasecmp(name
, (gchar
*)"prompt") == 0) {
2549 g_free(client
->pinentry
->prompt
);
2550 client
->pinentry
->prompt
= g_strdup(value
);
2552 else if (g_strcasecmp(name
, (gchar
*)"desc") == 0) {
2553 g_free(client
->pinentry
->desc
);
2554 client
->pinentry
->desc
= g_strdup(value
);
2557 /* Need to wait for pinentry to support a --timeout option so it can
2558 * terminate itself. Cannot do it here because assuan_pipe_connect() calls
2559 * execv() which replaces the pid of the fork()ed thread from
2560 * pinentry_fork(). So pinentry will become a real process after the
2561 * thread terminates and won't be able to be kill()ed from pwmd.
2563 else if (g_strcasecmp(name
, (gchar
*)"timeout") == 0) {
2565 gint n
= strtol(value
, &p
, 10);
2568 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2570 client
->pinentry
->timeout
= n
;
2573 else if (g_strcasecmp(name
, (gchar
*)"pinentry") == 0) {
2575 gint n
= strtol(value
, &p
, 10);
2577 if (*p
|| n
< 0 || n
> 1)
2578 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2580 client
->pinentry
->enable
= n
== 0 ? FALSE
: TRUE
;
2583 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2585 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_NOT_IMPLEMENTED
);
2588 log_write("OPTION %s=%s", name
, value
);
2592 gpg_error_t
register_commands(assuan_context_t ctx
)
2596 gint (*handler
)(assuan_context_t
, gchar
*line
);
2598 { "OPEN", open_command
},
2599 { "SAVE", save_command
},
2600 { "LIST", list_command
},
2601 { "REALPATH", realpath_command
},
2602 { "STORE", store_command
},
2603 { "DELETE", delete_command
},
2604 { "GET", get_command
},
2605 { "ATTR", attr_command
},
2606 { "ISCACHED", iscached_command
},
2607 { "CLEARCACHE", clearcache_command
},
2608 { "CACHETIMEOUT", cachetimeout_command
},
2609 { "GETCONFIG", getconfig_command
},
2610 { "DUMP", dump_command
},
2611 { "XPATH", xpath_command
},
2612 { "IMPORT", import_command
},
2613 { "LOCK", lock_command
},
2614 { "UNLOCK", unlock_command
},
2621 for (i
=0; table
[i
].name
; i
++) {
2622 rc
= assuan_register_command (ctx
, table
[i
].name
, table
[i
].handler
);
2628 rc
= assuan_register_bye_notify(ctx
, cleanup_assuan
);
2633 rc
= assuan_register_option_handler(ctx
, option_handler
);
2638 return assuan_register_reset_notify(ctx
, reset_notify
);
2641 gpg_error_t
try_xml_decrypt(assuan_context_t ctx
, gint fd
, struct stat st
,
2647 guchar tkey
[gcrykeysize
];
2648 struct client_s
*client
= ctx
? assuan_get_pointer(ctx
) : NULL
;
2649 gcry_cipher_hd_t gh
;
2650 guint iter
= 0, n_iter
= 0;
2652 void *outbuf
= NULL
;
2656 file_header_t file_header
;
2657 gchar str
[ASSUAN_LINELENGTH
];
2660 rc
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0);
2668 lseek(fd
, 0, SEEK_SET
);
2669 insize
= st
.st_size
- sizeof(file_header_t
);
2670 iv
= gcry_malloc(gcryblocksize
);
2674 gcry_cipher_close(gh
);
2676 return gpg_error_from_errno(ENOMEM
);
2679 len
= pth_read(fd
, &file_header
, sizeof(file_header_t
));
2681 if (len
!= sizeof(file_header_t
)) {
2685 gcry_cipher_close(gh
);
2689 return gpg_error_from_errno(errno
);
2692 /* No encryption iterations. This is a plain (gzipped) file. */
2693 if (file_header
.iter
== -1) {
2695 * cache_file_count() needs both .used == TRUE and a valid key in
2696 * order for it to count as a used cache entry. Fixes CACHE status
2699 memset(key
, '!', gcrykeysize
);
2700 insize
= st
.st_size
- sizeof(file_header_t
);
2703 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
2704 inbuf
= gcry_malloc(insize
);
2708 gcry_cipher_close(gh
);
2711 return gpg_error_from_errno(ENOMEM
);
2714 len
= pth_read(fd
, inbuf
, insize
);
2716 if (len
!= insize
) {
2720 gcry_cipher_close(gh
);
2724 return gpg_error_from_errno(errno
);
2727 if (file_header
.iter
== -1)
2730 memcpy(tkey
, key
, sizeof(tkey
));
2733 if ((rc
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2735 gcry_cipher_close(gh
);
2736 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2739 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2746 if ((rc
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
2748 gcry_cipher_close(gh
);
2749 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2752 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2758 gcry_cipher_close(gh
);
2763 iter_progress
= get_key_file_integer("global", "iteration_progress");
2765 if (ctx
&& iter_progress
> 0 && file_header
.iter
>= iter_progress
) {
2766 rc
= assuan_write_status(client
->ctx
, "DECRYPT", "0");
2775 rc
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2783 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
2785 gcry_cipher_close(gh
);
2786 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2789 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2796 while (iter
< file_header
.iter
) {
2797 if (ctx
&& iter_progress
> 0 && iter
>= iter_progress
) {
2798 if (!(iter
% iter_progress
)) {
2799 rc
= assuan_write_status(ctx
, "DECRYPT",
2800 print_fmt(str
, sizeof(str
), "%i", ++n_iter
* iter_progress
));
2810 if ((rc
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2812 gcry_cipher_close(gh
);
2813 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2816 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2823 rc
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2827 gcry_cipher_close(gh
);
2828 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2831 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2842 if (ctx
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
2843 rc
= assuan_write_status(ctx
, "DECRYPT",
2844 print_fmt(str
, sizeof(str
), "%i", file_header
.iter
));
2856 if (do_decompress(ctx
, inbuf
, insize
, &outbuf
, &outsize
, &zrc
) == FALSE
) {
2858 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2860 if (zrc
== Z_MEM_ERROR
) {
2862 return gpg_error_from_errno(ENOMEM
);
2864 else if (zrc
!= Z_DATA_ERROR
) {
2868 gcry_cipher_close(gh
);
2870 return EPWMD_BADKEY
;
2879 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
2883 gcry_cipher_close(gh
);
2885 return EPWMD_BADKEY
;
2889 client
->xml
= inbuf
;
2890 client
->len
= insize
;
2893 gcry_cipher_close(gh
);
2901 * This is called after every Assuan command.
2903 void command_finalize(assuan_context_t ctx
, gint rc
)
2905 struct client_s
*client
= assuan_get_pointer(ctx
);
2907 if (!client
->is_lock_cmd
)
2908 unlock_file_mutex(client
);