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
)
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 static 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 void unlock_file_mutex(struct client_s
*client
)
126 if (client
->has_lock
== FALSE
|| client
->pinentry
->status
!= PINENTRY_NONE
)
128 if (client
->has_lock
== FALSE
)
132 CACHE_LOCK(client
->ctx
);
134 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
140 pth_mutex_release(m
);
141 client
->has_lock
= FALSE
;
144 gpg_error_t
lock_file_mutex(struct client_s
*client
)
148 if (client
->has_lock
== TRUE
)
151 CACHE_LOCK(client
->ctx
);
153 if (cache_get_mutex(client
->md5file
, &m
) == FALSE
) {
160 if (pth_mutex_acquire(m
, TRUE
, NULL
) == FALSE
) {
161 if (errno
== EBUSY
) {
163 assuan_write_status(client
->ctx
, "LOCKED", N_("Waiting for lock"));
165 pth_mutex_acquire(m
, FALSE
, NULL
);
169 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(errno
));
170 return gpg_error_from_errno(e
);
174 client
->has_lock
= TRUE
;
178 void free_client(struct client_s
*client
)
181 xmlFreeDoc(client
->doc
);
184 gcry_free(client
->xml
);
186 if (client
->filename
)
187 g_free(client
->filename
);
190 gcry_cipher_close(client
->gh
);
193 void cleanup_client(struct client_s
*client
)
195 assuan_context_t ctx
= client
->ctx
;
197 struct pinentry_s
*pin
= client
->pinentry
;
200 unlock_file_mutex(client
);
201 CACHE_LOCK(client
->ctx
);
202 cache_decr_refcount(client
->md5file
);
205 * This may be a new file so don't use a cache slot. save_command() will
206 * set this to FALSE on success.
208 if (client
->new == TRUE
)
209 cache_clear(client
->md5file
, 1);
212 memset(client
, 0, sizeof(struct client_s
));
213 client
->state
= STATE_CONNECTED
;
215 client
->freed
= TRUE
;
217 client
->pinentry
= pin
;
222 gboolean
do_decompress(assuan_context_t ctx
, gpointer in
, gint insize
,
223 gpointer
*out
, glong
*outsize
, gint
*rc
)
229 gchar str
[ASSUAN_LINELENGTH
];
235 z
.avail_out
= zlib_bufsize
;
236 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
239 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
244 *rc
= inflateInit2(&z
, 47);
247 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
252 memset(&h
, 0, sizeof(gz_header
));
253 h
.comment
= (guchar
*)buf
;
254 h
.comm_max
= sizeof(buf
);
255 *rc
= inflateGetHeader(&z
, &h
);
258 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
264 *rc
= inflate(&z
, Z_BLOCK
);
267 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
274 insize
= atoi((gchar
*)h
.comment
);
279 *rc
= inflate(&z
, Z_FINISH
);
286 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
294 z
.next_out
= pout
+ z
.total_out
;
295 z
.avail_out
= zlib_bufsize
;
298 *rc
= assuan_write_status(ctx
, "DECOMPRESS",
299 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
314 } while (*rc
!= Z_STREAM_END
);
317 *rc
= assuan_write_status(ctx
, "DECOMPRESS",
318 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
325 *outsize
= z
.total_out
;
331 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
337 gpg_error_t
read_file_header(const gchar
*filename
, file_header_t
*fh
)
342 fd
= open(filename
, O_RDONLY
);
345 return gpg_error_from_errno(errno
);
347 len
= pth_read(fd
, fh
, sizeof(file_header_t
));
350 if (len
!= sizeof(file_header_t
))
351 return gpg_error_from_errno(errno
);
356 static gpg_error_t
open_command_finalize(assuan_context_t ctx
, guchar shakey
[],
359 struct client_s
*client
= assuan_get_pointer(ctx
);
365 if ((fd
= open_file(client
->filename
, &st
)) == -1) {
367 if (errno
== ENOENT
) {
375 log_write("%s: %s", client
->filename
, strerror(errno
));
376 cleanup_client(client
);
377 memset(shakey
, 0, sizeof(shakey
));
378 return send_syserror(ctx
, rc
);
381 rc
= try_xml_decrypt(ctx
, fd
, st
, shakey
);
385 memset(shakey
, 0, sizeof(shakey
));
386 cleanup_client(client
);
387 return send_error(ctx
, rc
);
391 CACHE_LOCK(client
->ctx
);
393 if (cached
== FALSE
) {
394 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
395 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
396 cleanup_client(client
);
398 return send_error(ctx
, EPWMD_MAX_SLOTS
);
401 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
402 cache_reset_timeout(client
->md5file
, timeout
);
405 cache_set_timeout(client
->md5file
, -2);
410 memset(shakey
, 0, sizeof(shakey
));
414 gcry_free(client
->xml
);
419 if (client
->new == FALSE
)
420 send_cache_status_all();
422 client
->state
= STATE_OPEN
;
425 return send_error(ctx
, rc
);
428 static int open_command(assuan_context_t ctx
, char *line
)
431 guchar shakey
[gcrykeysize
];
432 gboolean cached
= FALSE
;
434 struct client_s
*client
= assuan_get_pointer(ctx
);
436 gchar
*filename
= NULL
;
437 file_header_t file_header
;
439 memset(shakey
, 0, sizeof(shakey
));
441 if ((req
= split_input_line(line
, " ", 2)) != NULL
)
444 if (!filename
|| !*filename
) {
446 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
449 if (valid_filename(filename
) == FALSE
) {
451 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
454 if (client
->state
== STATE_OPEN
)
455 cleanup_client(client
);
457 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
458 CACHE_LOCK(client
->ctx
);
460 if (cache_has_file(client
->md5file
) == FALSE
) {
461 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
464 return send_error(ctx
, EPWMD_MAX_SLOTS
);
468 cache_incr_refcount(client
->md5file
);
470 rc
= lock_file_mutex(client
);
474 return send_error(ctx
, rc
);
477 client
->freed
= FALSE
;
479 if ((rc
= gcry_cipher_open(&client
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
481 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
482 cleanup_client(client
);
483 return send_error(ctx
, rc
);
486 if (stat(filename
, &st
) == 0) {
487 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
488 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
490 cleanup_client(client
);
491 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
494 client
->mtime
= st
.st_mtime
;
497 client
->filename
= g_strdup(filename
);
499 if (!client
->filename
) {
500 memset(shakey
, 0, sizeof(shakey
));
501 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
502 cleanup_client(client
);
504 return send_syserror(ctx
, ENOMEM
);
508 client
->pinentry
->filename
= g_strdup(client
->filename
);
510 if (!client
->pinentry
->filename
) {
511 memset(shakey
, 0, sizeof(shakey
));
512 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
513 cleanup_client(client
);
515 return send_syserror(ctx
, ENOMEM
);
520 * New files don't need a key.
522 if (access(filename
, R_OK
) != 0) {
523 if (errno
!= ENOENT
) {
525 log_write("%s: %s", filename
, strerror(errno
));
527 cleanup_client(client
);
528 return send_syserror(ctx
, rc
);
531 if ((client
->xml
= new_document()) == NULL
) {
532 log_write("%s", strerror(ENOMEM
));
534 cleanup_client(client
);
535 return send_syserror(ctx
, ENOMEM
);
538 client
->len
= xmlStrlen(client
->xml
);
540 client
->filename
= g_strdup(filename
);
542 if (!client
->filename
) {
544 cleanup_client(client
);
545 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
546 return send_syserror(ctx
, ENOMEM
);
549 memset(shakey
, 0, sizeof(shakey
));
551 if (req
[1] && *req
[1])
552 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
555 return open_command_finalize(ctx
, shakey
, cached
);
558 rc
= read_file_header(filename
, &file_header
);
562 cleanup_client(client
);
563 return send_error(ctx
, rc
);
566 if (file_header
.iter
== -1)
569 CACHE_LOCK(client
->ctx
);
570 cached
= cache_get_key(client
->md5file
, shakey
);
573 if (cached
== FALSE
) {
575 * No key specified and no matching filename found in the cache. Use
576 * pinentry to retrieve the key. Cannot return assuan_process_done()
577 * here otherwise the command will be interrupted. The event loop in
578 * client_thread() will poll the file descriptor waiting for it to
579 * become ready to read a pinentry_key_s which will contain the
580 * entered key or rc. It will then call open_command_finalize() to
581 * to finish the command.
583 if (!req
[1] || !*req
[1]) {
585 gboolean b
= get_key_file_boolean(filename
, "enable_pinentry");
587 /* From set_pinentry_defaults(). */
588 if (client
->pinentry
->enable
== FALSE
||
589 (client
->pinentry
->enable
== -1 && b
== FALSE
)) {
590 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
595 lock_pin_mutex(client
);
596 client
->pinentry
->which
= PINENTRY_OPEN
;
597 rc
= pinentry_fork(ctx
);
600 unlock_pin_mutex(client
->pinentry
);
601 cleanup_client(client
);
602 return send_error(ctx
, rc
);
605 client
->pinentry
->cb
= open_command_finalize
;
606 client
->pinentry
->status
= PINENTRY_INIT
;
609 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
614 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
619 return open_command_finalize(ctx
, shakey
, cached
);
622 gboolean
do_compress(assuan_context_t ctx
, gint level
, gpointer data
,
623 gint size
, gpointer
*out
, glong
*outsize
, gint
*rc
)
629 gint cmd
= Z_NO_FLUSH
;
630 gchar str
[ASSUAN_LINELENGTH
];
634 z
.next_in
= pin
= data
;
635 z
.avail_in
= size
< zlib_bufsize
? size
: zlib_bufsize
;
636 z
.avail_out
= zlib_bufsize
;
637 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
640 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
645 *rc
= deflateInit2(&z
, level
, Z_DEFLATED
, 31, 8, Z_DEFAULT_STRATEGY
);
648 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
653 memset(&h
, 0, sizeof(gz_header
));
654 g_snprintf(buf
, sizeof(buf
), "%i", size
);
655 h
.comment
= (guchar
*)buf
;
656 *rc
= deflateSetHeader(&z
, &h
);
659 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
668 *rc
= deflate(&z
, cmd
);
675 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
683 z
.next_out
= pout
+ z
.total_out
;
684 z
.avail_out
= zlib_bufsize
;
687 if (!z
.avail_in
&& z
.total_in
< size
) {
688 if (z
.total_in
+ zlib_bufsize
> size
)
689 z
.avail_in
= size
- z
.total_in
;
691 z
.avail_in
= zlib_bufsize
;
694 *rc
= assuan_write_status(ctx
, "COMPRESS",
695 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
702 if (z
.total_in
>= size
)
713 } while (*rc
!= Z_STREAM_END
);
716 *rc
= assuan_write_status(ctx
, "COMPRESS",
717 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
724 *outsize
= z
.total_out
;
730 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
736 gpg_error_t
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
737 const gchar
*filename
, gpointer data
, size_t insize
, guchar
*shakey
,
743 guchar tkey
[gcrykeysize
];
746 guint iter_progress
= 0, n_iter
= 0, xiter
= 0;
747 gchar tmp
[FILENAME_MAX
];
750 file_header_t file_header
;
751 gchar str
[ASSUAN_LINELENGTH
];
755 * cache_file_count() needs both .used == TRUE and a valid key in
756 * order for it to count as a used cache entry. Fixes CACHE status
759 memset(shakey
, '!', gcrykeysize
);
761 file_header
.iter
= iter
;
765 if (insize
/ gcryblocksize
) {
766 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
768 if (insize
% gcryblocksize
)
769 len
+= gcryblocksize
;
773 * Resize the existing xml buffer to the block size required by gcrypt
774 * rather than duplicating it and wasting memory.
776 inbuf
= gcry_realloc(data
, len
);
779 return gpg_error_from_errno(ENOMEM
);
782 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
783 memcpy(tkey
, shakey
, sizeof(tkey
));
786 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
788 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
792 file_header
.iter
= iter
;
795 iter_progress
= get_key_file_integer("global", "iteration_progress");
797 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
798 rc
= assuan_write_status(client
->ctx
, "ENCRYPT", "0");
806 while (xiter
< file_header
.iter
) {
807 if (client
&& iter_progress
> 0 && xiter
>= iter_progress
) {
808 if (!(xiter
% iter_progress
)) {
809 rc
= assuan_write_status(client
->ctx
, "ENCRYPT",
810 print_fmt(str
, sizeof(str
), "%i", ++n_iter
* iter_progress
));
819 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
820 sizeof(file_header
.iv
)))) {
822 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
826 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
829 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
837 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
838 sizeof(file_header
.iv
)))) {
840 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
844 if ((rc
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
846 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
850 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
852 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
856 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
857 rc
= assuan_write_status(client
->ctx
, "ENCRYPT",
858 print_fmt(str
, sizeof(str
), "%i", file_header
.iter
));
868 if (stat(filename
, &st
) == 0) {
869 mode
= st
.st_mode
& (S_IRWXU
|S_IRWXG
|S_IRWXO
);
872 * FIXME What if the file has an ACL?
874 if (!(mode
& S_IWUSR
)) {
876 return gpg_error_from_errno(EACCES
);
880 if (errno
!= ENOENT
) {
883 return gpg_error_from_errno(rc
);
887 g_snprintf(tmp
, sizeof(tmp
), ".%s.tmp", filename
);
889 if ((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
892 p
= strrchr(tmp
, '/');
894 log_write("%s: %s", p
, strerror(rc
));
895 return gpg_error_from_errno(rc
);
900 * xml_import() from command line.
904 len
= pth_write(fd
, &file_header
, sizeof(file_header_t
));
906 if (len
!= sizeof(file_header
)) {
915 return gpg_error_from_errno(len
);
918 len
= pth_write(fd
, inbuf
, insize
);
929 return gpg_error_from_errno(len
);
932 if (fsync(fd
) == -1) {
941 return gpg_error_from_errno(len
);
947 if (mode
&& get_key_file_boolean(filename
, "backup") == TRUE
) {
948 gchar tmp2
[FILENAME_MAX
];
950 g_snprintf(tmp2
, sizeof(tmp2
), "%s.backup", filename
);
952 if (rename(filename
, tmp2
) == -1) {
956 return gpg_error_from_errno(len
);
960 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
, &zrc
) == FALSE
) {
999 memset(shakey
, 0, sizeof(shakey
));
1002 if (zrc
== 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 rc
= do_xml_encrypt(client
, client
->gh
, client
->filename
, xmlbuf
, len
, shakey
, iter
);
1018 memset(shakey
, 0, sizeof(shakey
));
1019 return send_error(ctx
, rc
);
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 rc
= file_modified(client
);
1066 log_write("%s: %s", client
->filename
? client
->filename
: "",
1068 return send_error(ctx
, rc
);
1071 rc
= lock_file_mutex(client
);
1074 return send_error(ctx
, rc
);
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 (get_key_file_boolean(client
->filename
, "enable_pinentry") == FALSE
) {
1098 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1102 lock_pin_mutex(client
);
1103 client
->pinentry
->which
= PINENTRY_SAVE
;
1104 rc
= pinentry_fork(ctx
);
1107 unlock_pin_mutex(client
->pinentry
);
1108 return send_error(ctx
, rc
);
1111 client
->pinentry
->cb
= save_command_finalize
;
1112 client
->pinentry
->status
= PINENTRY_INIT
;
1115 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1125 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, line
, strlen(line
));
1126 memset(line
, 0, strlen(line
));
1130 return save_command_finalize(ctx
, shakey
, cached
);
1133 static int delete_command(assuan_context_t ctx
, char *line
)
1135 struct client_s
*client
= assuan_get_pointer(ctx
);
1140 rc
= file_modified(client
);
1143 log_write("%s: %s", client
->filename
? client
->filename
: "",
1145 return send_error(ctx
, rc
);
1148 if (strchr(line
, '\t'))
1149 req
= split_input_line(line
, "\t", -1);
1151 req
= split_input_line(line
, " ", -1);
1154 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1156 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1160 return send_error(ctx
, rc
);
1164 * No sub-node defined. Remove the entire node (account).
1173 return send_error(ctx
, 0);
1176 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1180 return send_error(ctx
, rc
);
1187 return send_error(ctx
, 0);
1191 * Don't return with assuan_process_done() here. This has been called from
1192 * assuan_process_next() and the command should be finished in
1195 static int store_command_finalize(gpointer data
, gint assuan_rc
, guchar
*line
,
1198 assuan_context_t ctx
= data
;
1199 struct client_s
*client
= assuan_get_pointer(ctx
);
1202 gpg_error_t rc
= file_modified(client
);
1204 if (assuan_rc
|| rc
) {
1211 return assuan_rc
? assuan_rc
: rc
;
1214 req
= split_input_line((gchar
*)line
, "\t", 0);
1222 return EPWMD_COMMAND_SYNTAX
;
1224 if (valid_xml_element((xmlChar
*)*req
) == FALSE
) {
1226 return EPWMD_INVALID_ELEMENT
;
1229 if (valid_element_path(req
+1, TRUE
) == FALSE
) {
1231 return EPWMD_INVALID_ELEMENT
;
1235 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1237 if (rc
&& rc
== EPWMD_ELEMENT_NOT_FOUND
) {
1238 rc
= new_account(client
->doc
, *req
);
1255 create_elements_cb(n
, req
+1, &rc
, NULL
);
1257 find_elements(client
->doc
, n
->children
, req
+1, &rc
,
1258 NULL
, NULL
, create_elements_cb
, FALSE
, 0, NULL
);
1262 client
->inquire_status
= INQUIRE_DONE
;
1266 static int store_command(assuan_context_t ctx
, char *line
)
1268 struct client_s
*client
= assuan_get_pointer(ctx
);
1269 gpg_error_t rc
= file_modified(client
);
1272 log_write("%s: %s", client
->filename
? client
->filename
: "",
1274 return send_error(ctx
, rc
);
1277 rc
= assuan_inquire_ext(ctx
, "STORE", 0, store_command_finalize
, ctx
);
1280 return send_error(ctx
, rc
);
1282 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1283 client
->inquire_status
= INQUIRE_BUSY
;
1287 static int get_command(assuan_context_t ctx
, char *line
)
1289 struct client_s
*client
= assuan_get_pointer(ctx
);
1294 rc
= file_modified(client
);
1297 log_write("%s: %s", client
->filename
? client
->filename
: "",
1299 return send_error(ctx
, rc
);
1302 req
= split_input_line(line
, "\t", -1);
1304 if (!req
|| !*req
) {
1306 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1309 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1313 return send_error(ctx
, rc
);
1317 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1322 return send_error(ctx
, rc
);
1324 if (!n
|| !n
->children
)
1325 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1327 n
= find_text_node(n
->children
);
1329 if (!n
|| !n
->content
|| !*n
->content
)
1330 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1332 rc
= assuan_send_data(ctx
, n
->content
, xmlStrlen(n
->content
));
1333 return send_error(ctx
, rc
);
1336 static xmlNodePtr
realpath_elements_cb(xmlNodePtr node
, gchar
**target
,
1337 gpg_error_t
*rc
, gchar
**req_orig
, void *data
)
1339 gchar
*path
= *(gchar
**)data
;
1340 gchar
*tmp
= NULL
, *result
;
1344 *(gchar
**)data
= NULL
;
1347 path
= g_strjoinv("\t", target
);
1350 *rc
= gpg_error_from_errno(ENOMEM
);
1355 tmp
= g_strjoinv("\t", req_orig
);
1359 *rc
= gpg_error_from_errno(ENOMEM
);
1365 result
= g_strdup_printf("%s\t%s", path
, tmp
);
1367 result
= g_strdup(path
);
1370 *rc
= gpg_error_from_errno(ENOMEM
);
1378 *(gchar
**)data
= result
;
1382 static int realpath_command(assuan_context_t ctx
, char *line
)
1385 struct client_s
*client
= assuan_get_pointer(ctx
);
1393 rc
= file_modified(client
);
1396 log_write("%s: %s", client
->filename
? client
->filename
: "",
1398 return send_error(ctx
, rc
);
1401 if (strchr(line
, '\t') != NULL
) {
1402 if ((req
= split_input_line(line
, "\t", 0)) == NULL
)
1403 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1406 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1407 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1410 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1414 return send_error(ctx
, rc
);
1417 rp
= g_strjoinv("\t", req
);
1421 return send_syserror(ctx
, ENOMEM
);
1425 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
,
1426 NULL
, realpath_elements_cb
, NULL
, FALSE
, 0, &rp
);
1431 return send_error(ctx
, rc
);
1435 string
= g_string_new(rp
);
1440 return send_syserror(ctx
, ENOMEM
);
1443 for (i
= 0, t
= string
->str
+ i
; *t
; t
++, i
++) {
1444 if ((!i
&& *t
!= '!') || (*t
== '\t' && *(t
+1) && *(t
+1) != '!')) {
1445 string
= g_string_insert_c(string
, !i
? i
++ : ++i
, '!');
1450 rc
= assuan_send_data(ctx
, string
->str
, string
->len
);
1451 g_string_free(string
, TRUE
);
1452 return send_error(ctx
, rc
);
1455 static int list_command(assuan_context_t ctx
, char *line
)
1457 struct client_s
*client
= assuan_get_pointer(ctx
);
1459 struct element_list_s
*elements
= NULL
;
1462 if (disable_list_and_dump
== TRUE
)
1463 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
1465 rc
= file_modified(client
);
1468 log_write("%s: %s", client
->filename
, pwmd_strerror(rc
));
1469 return send_error(ctx
, rc
);
1475 rc
= list_accounts(client
->doc
, &str
);
1478 return send_error(ctx
, rc
);
1480 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1481 g_string_free(str
, TRUE
);
1482 return send_error(ctx
, rc
);
1485 elements
= g_malloc0(sizeof(struct element_list_s
));
1488 rc
= gpg_err_code_from_errno(ENOMEM
);
1492 rc
= create_path_list(client
->doc
, elements
, line
);
1498 gint total
= g_slist_length(elements
->list
);
1503 rc
= EPWMD_EMPTY_ELEMENT
;
1507 str
= g_string_new(NULL
);
1510 rc
= gpg_err_code_from_errno(ENOMEM
);
1514 for (i
= 0; i
< total
; i
++) {
1515 tmp
= g_slist_nth_data(elements
->list
, i
);
1516 g_string_append_printf(str
, "%s%s", tmp
, i
+1 == total
? "" : "\n");
1519 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1520 g_string_free(str
, TRUE
);
1523 rc
= EPWMD_EMPTY_ELEMENT
;
1527 gint total
= g_slist_length(elements
->list
);
1530 for (i
= 0; i
< total
; i
++) {
1531 tmp
= g_slist_nth_data(elements
->list
, i
);
1535 g_slist_free(elements
->list
);
1537 if (elements
->prefix
)
1538 g_free(elements
->prefix
);
1543 return send_error(ctx
, rc
);
1546 static gpg_error_t
add_attribute(xmlNodePtr node
, const gchar
*name
,
1551 if ((a
= xmlHasProp(node
, (xmlChar
*)name
)) == NULL
) {
1552 a
= xmlNewProp(node
, (xmlChar
*)name
, (xmlChar
*)value
);
1555 return EPWMD_LIBXML_ERROR
;
1558 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1564 * req[0] - element path
1566 static int attribute_list(assuan_context_t ctx
, gchar
**req
)
1568 struct client_s
*client
= assuan_get_pointer(ctx
);
1569 gchar
**attrlist
= NULL
;
1571 gchar
**path
= NULL
;
1577 if (!req
|| !req
[0])
1578 return EPWMD_COMMAND_SYNTAX
;
1580 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1582 * The first argument may be only an account.
1584 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
1585 return EPWMD_COMMAND_SYNTAX
;
1588 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1596 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1597 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1607 for (a
= n
->properties
; a
; a
= a
->next
) {
1610 if ((pa
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1612 g_strfreev(attrlist
);
1614 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
1615 return gpg_error_from_errno(ENOMEM
);
1620 attrlist
[i
] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1623 g_strfreev(attrlist
);
1624 return gpg_error_from_errno(ENOMEM
);
1627 attrlist
[++i
] = NULL
;
1631 return EPWMD_EMPTY_ELEMENT
;
1633 line
= g_strjoinv("\n", attrlist
);
1636 g_strfreev(attrlist
);
1637 return gpg_error_from_errno(ENOMEM
);
1640 rc
= assuan_send_data(ctx
, line
, strlen(line
));
1642 g_strfreev(attrlist
);
1647 * req[0] - attribute
1648 * req[1] - element path
1650 static int attribute_delete(struct client_s
*client
, gchar
**req
)
1654 gchar
**path
= NULL
;
1657 if (!req
|| !req
[0] || !req
[1])
1658 return EPWMD_COMMAND_SYNTAX
;
1660 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1662 * The first argument may be only an account.
1664 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1665 return EPWMD_COMMAND_SYNTAX
;
1669 * Don't remove the "name" attribute for the account element. To remove an
1670 * account use DELETE <account>.
1672 if (!path
[1] && xmlStrEqual((xmlChar
*)req
[0], (xmlChar
*)"name")) {
1673 rc
= EPWMD_ATTR_SYNTAX
;
1677 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1683 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1684 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1692 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
)
1693 return EPWMD_ATTR_NOT_FOUND
;
1695 if (xmlRemoveProp(a
) == -1)
1696 return EPWMD_LIBXML_ERROR
;
1705 static xmlNodePtr
create_element_path(struct client_s
*client
, gchar
***path
,
1708 gchar
**src
= *path
;
1709 gchar
**src_orig
= g_strdupv(src
);
1710 xmlNodePtr n
= NULL
;
1715 *rc
= gpg_error_from_errno(ENOMEM
);
1720 n
= find_account(client
->doc
, &src
, rc
, NULL
, 0);
1723 if (*rc
== EPWMD_ELEMENT_NOT_FOUND
) {
1724 *rc
= new_account(client
->doc
, src
[0]);
1737 n
= create_target_elements_cb(n
, src
+1, rc
, NULL
);
1739 n
= find_elements(client
->doc
, n
->children
, src
+1, rc
,
1740 NULL
, NULL
, create_target_elements_cb
, FALSE
, 0, NULL
);
1746 * Reset the position of the element tree now that the elements
1747 * have been created.
1752 n
= find_account(client
->doc
, &src
, rc
, NULL
, 0);
1757 n
= find_elements(client
->doc
, n
->children
, src
+1, rc
,
1758 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1766 g_strfreev(src_orig
);
1773 * Creates a "target" attribute. When other commands encounter an element with
1774 * this attribute, the element path is modified to the target value. If the
1775 * source element path doesn't exist when using 'ATTR SET target', it is
1776 * created, but the destination element path must exist.
1778 * req[0] - source element path
1779 * req[1] - destination element path
1781 static gpg_error_t
target_attribute(struct client_s
*client
, gchar
**req
)
1783 gchar
**src
, **dst
, *line
= NULL
;
1787 if (!req
|| !req
[0] || !req
[1])
1788 return EPWMD_COMMAND_SYNTAX
;
1790 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1792 * The first argument may be only an account.
1794 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
)
1795 return EPWMD_COMMAND_SYNTAX
;
1798 if (valid_element_path(src
, FALSE
) == FALSE
) {
1800 return EPWMD_INVALID_ELEMENT
;
1803 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1805 * The first argument may be only an account.
1807 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1808 rc
= EPWMD_COMMAND_SYNTAX
;
1813 n
= find_account(client
->doc
, &dst
, &rc
, NULL
, 0);
1816 * Make sure the destination element path exists.
1822 n
= find_elements(client
->doc
, n
->children
, dst
+1, &rc
,
1823 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1829 n
= create_element_path(client
, &src
, &rc
);
1834 line
= g_strjoinv("\t", dst
);
1837 rc
= gpg_error_from_errno(ENOMEM
);
1841 rc
= add_attribute(n
, "target", line
);
1851 * req[0] - account name
1854 static gpg_error_t
name_attribute(struct client_s
*client
, gchar
**req
)
1860 tmp
= g_strdupv(req
);
1863 return gpg_error_from_errno(ENOMEM
);
1865 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1871 if (g_utf8_collate(req
[0], req
[1]) == 0)
1875 * Will not overwrite an existing account.
1877 tmp
= g_strdupv(req
+1);
1880 return gpg_error_from_errno(ENOMEM
);
1882 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1885 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
)
1889 return EPWMD_ACCOUNT_EXISTS
;
1892 * Whitespace not allowed in account names.
1894 if (contains_whitespace(req
[1]) == TRUE
)
1895 return EPWMD_ATTR_SYNTAX
;
1897 tmp
= g_strdupv(req
);
1900 return gpg_error_from_errno(ENOMEM
);
1902 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1906 return EPWMD_ELEMENT_NOT_FOUND
;
1908 return add_attribute(n
, "name", req
[1]);
1912 * req[0] - attribute
1913 * req[1] - element path
1915 static int attribute_get(assuan_context_t ctx
, gchar
**req
)
1917 struct client_s
*client
= assuan_get_pointer(ctx
);
1923 if (!req
|| !req
[0] || !req
[1])
1924 return EPWMD_COMMAND_SYNTAX
;
1926 if (strchr(req
[1], '\t')) {
1927 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
)
1928 return EPWMD_COMMAND_SYNTAX
;
1931 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1932 return EPWMD_COMMAND_SYNTAX
;
1935 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1941 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1942 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1950 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
)
1951 return EPWMD_ATTR_NOT_FOUND
;
1953 rc
= assuan_send_data(ctx
, a
, xmlStrlen(a
));
1963 * req[0] - attribute
1964 * req[1] - element path
1967 static int attribute_set(struct client_s
*client
, gchar
**req
)
1969 gchar
**path
= NULL
;
1973 if (!req
|| !req
[0] || !req
[1] || !req
[2])
1974 return EPWMD_COMMAND_SYNTAX
;
1977 * Reserved attribute names.
1979 if (g_utf8_collate(req
[0], "name") == 0) {
1981 * Only reserved for the account element. Not the rest of the
1984 if (strchr(req
[1], '\t') == NULL
)
1985 return name_attribute(client
, req
+ 1);
1987 else if (g_utf8_collate(req
[0], "target") == 0)
1988 return target_attribute(client
, req
+ 1);
1990 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1992 * The first argument may be only an account.
1994 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1995 return EPWMD_COMMAND_SYNTAX
;
1998 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
2004 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
2005 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2012 return add_attribute(n
, req
[0], req
[2]);
2021 * req[1] - attribute name or element path if command is LIST
2022 * req[2] - element path
2023 * req[2] - element path or value
2025 static int attr_command(assuan_context_t ctx
, char *line
)
2027 struct client_s
*client
= assuan_get_pointer(ctx
);
2028 gchar
**req
= split_input_line(line
, " ", 4);
2031 rc
= file_modified(client
);
2034 log_write("%s: %s", client
->filename
? client
->filename
: "",
2037 return send_error(ctx
, rc
);
2040 if (!req
|| !req
[0] || !req
[1]) {
2042 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2045 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
2046 rc
= attribute_set(client
, req
+1);
2047 else if (g_ascii_strcasecmp(req
[0], "GET") == 0)
2048 rc
= attribute_get(ctx
, req
+1);
2049 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
2050 rc
= attribute_delete(client
, req
+1);
2051 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
2052 rc
= attribute_list(ctx
, req
+1);
2054 rc
= EPWMD_COMMAND_SYNTAX
;
2057 return send_error(ctx
, rc
);
2060 static int iscached_command(assuan_context_t ctx
, char *line
)
2062 gchar
**req
= split_input_line(line
, " ", 0);
2065 if (!req
|| !*req
) {
2067 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2070 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2074 if (cache_iscached(md5file
) == FALSE
) {
2076 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2080 return send_error(ctx
, 0);
2083 gpg_error_t
send_cache_status(assuan_context_t ctx
)
2086 struct client_s
*client
= assuan_get_pointer(ctx
);
2087 gchar buf
[ASSUAN_LINELENGTH
];
2089 CACHE_LOCK(client
->ctx
);
2090 tmp
= print_fmt(buf
, sizeof(buf
), "%i %i",
2092 (cache_size
/ sizeof(file_cache_t
)) - cache_file_count());
2095 return assuan_write_status(ctx
, "CACHE", buf
);
2098 static int clearcache_command(assuan_context_t ctx
, char *line
)
2100 struct client_s
*client
= assuan_get_pointer(ctx
);
2101 gchar
**req
= split_input_line(line
, " ", 0);
2106 if (!req
|| !*req
) {
2108 cache_clear(client
->md5file
, 2);
2110 return send_error(ctx
, 0);
2113 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2116 if (cache_clear(md5file
, 1) == FALSE
) {
2118 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2122 return send_error(ctx
, 0);
2125 static int cachetimeout_command(assuan_context_t ctx
, char *line
)
2129 gchar
**req
= split_input_line(line
, " ", 0);
2131 struct client_s
*client
= assuan_get_pointer(ctx
);
2133 if (!req
|| !*req
|| !req
[1]) {
2135 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2139 timeout
= strtol(req
[0], &p
, 10);
2141 if (errno
!= 0 || *p
!= 0) {
2143 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2146 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2148 CACHE_LOCK(client
->ctx
);
2150 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2152 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2156 return send_error(ctx
, 0);
2159 static int dump_command(assuan_context_t ctx
, char *line
)
2163 struct client_s
*client
= assuan_get_pointer(ctx
);
2166 if (disable_list_and_dump
== TRUE
)
2167 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2169 rc
= file_modified(client
);
2172 log_write("%s: %s", client
->filename
? client
->filename
: "",
2174 return send_error(ctx
, rc
);
2177 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2180 return send_syserror(ctx
, ENOMEM
);
2182 rc
= assuan_send_data(ctx
, xml
, len
);
2184 return send_error(ctx
, rc
);
2187 static int getconfig_command(assuan_context_t ctx
, gchar
*line
)
2189 struct client_s
*client
= assuan_get_pointer(ctx
);
2193 if (strcmp(line
, "key") == 0 || strcmp(line
, "key_file") == 0)
2194 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2196 p
= get_key_file_string(client
->filename
? client
->filename
: "global", line
);
2199 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2201 tmp
= expand_homedir(p
);
2205 return send_syserror(ctx
, ENOMEM
);
2210 rc
= assuan_send_data(ctx
, p
, strlen(p
));
2212 return send_error(ctx
, rc
);
2215 static int xpath_command(assuan_context_t ctx
, gchar
*line
)
2217 struct client_s
*client
= assuan_get_pointer(ctx
);
2219 xmlXPathContextPtr xp
;
2220 xmlXPathObjectPtr result
;
2221 xmlBufferPtr buf
= NULL
;
2224 rc
= file_modified(client
);
2227 log_write("%s: %s", client
->filename
? client
->filename
: "",
2229 return send_error(ctx
, rc
);
2232 if (!line
|| !*line
)
2233 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2235 if ((req
= split_input_line(line
, "\t", 2)) == NULL
) {
2236 if (strv_printf(&req
, "%s", line
) == FALSE
)
2237 return send_syserror(ctx
, ENOMEM
);
2240 xp
= xmlXPathNewContext(client
->doc
);
2243 return send_error(ctx
, EPWMD_LIBXML_ERROR
);
2245 result
= xmlXPathEvalExpression((xmlChar
*)req
[0], xp
);
2248 xmlXPathFreeContext(xp
);
2249 return send_error(ctx
, EPWMD_LIBXML_ERROR
);
2252 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2253 rc
= EPWMD_EMPTY_ELEMENT
;
2257 rc
= recurse_xpath_nodeset(client
->doc
, result
->nodesetval
,
2258 (xmlChar
*)req
[1], &buf
);
2262 else if (!req
[1] && !xmlBufferLength(buf
)) {
2263 rc
= EPWMD_EMPTY_ELEMENT
;
2269 rc
= assuan_send_data(ctx
, xmlBufferContent(buf
), xmlBufferLength(buf
));
2278 xmlXPathFreeObject(result
);
2281 xmlXPathFreeContext(xp
);
2283 return send_error(ctx
, rc
);
2286 static int import_command_finalize(gpointer data
, gint assuan_rc
, guchar
*line
,
2289 struct client_s
*client
= assuan_get_pointer((assuan_context_t
)data
);
2290 gpg_error_t rc
= file_modified(client
);
2291 gchar
**req
, **path
= NULL
, **path_orig
= NULL
, *content
;
2293 xmlNodePtr n
, root
, copy
;
2295 if (assuan_rc
|| rc
) {
2302 return assuan_rc
? assuan_rc
: rc
;
2305 req
= split_input_line((gchar
*)line
, " ", 2);
2313 return EPWMD_COMMAND_SYNTAX
;
2315 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
2316 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
2317 return EPWMD_COMMAND_SYNTAX
;
2322 if (!content
|| !*content
) {
2323 rc
= EPWMD_COMMAND_SYNTAX
;
2327 if (valid_xml_element((xmlChar
*)*path
) == FALSE
) {
2328 rc
= EPWMD_INVALID_ELEMENT
;
2332 if (valid_element_path(path
+1, FALSE
) == FALSE
) {
2333 rc
= EPWMD_INVALID_ELEMENT
;
2337 doc
= xmlReadDoc((xmlChar
*)content
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
2340 rc
= EPWMD_LIBXML_ERROR
;
2344 root
= xmlDocGetRootElement(doc
);
2345 path_orig
= g_strdupv(path
);
2349 rc
= gpg_error_from_errno(ENOMEM
);
2353 if (strv_printf(&path
, "%s", (gchar
*)root
->name
) == FALSE
) {
2354 g_strfreev(path_orig
);
2356 rc
= gpg_error_from_errno(ENOMEM
);
2360 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
2362 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
) {
2363 g_strfreev(path_orig
);
2368 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2370 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
) {
2371 g_strfreev(path_orig
);
2376 xmlNodePtr parent
= n
->parent
;
2387 if (rc
== EPWMD_ELEMENT_NOT_FOUND
) {
2388 n
= create_element_path(client
, &path
, &rc
);
2396 copy
= xmlCopyNode(root
, 1);
2397 n
= xmlAddChild(n
, copy
);
2401 rc
= EPWMD_LIBXML_ERROR
;
2406 client
->inquire_status
= INQUIRE_DONE
;
2410 static int import_command(assuan_context_t ctx
, gchar
*line
)
2413 struct client_s
*client
= assuan_get_pointer(ctx
);
2415 rc
= file_modified(client
);
2418 log_write("%s: %s", client
->filename
? client
->filename
: "",
2420 return send_error(ctx
, rc
);
2423 rc
= assuan_inquire_ext(ctx
, "IMPORT", 0, import_command_finalize
, ctx
);
2426 return send_error(ctx
, rc
);
2428 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2429 client
->inquire_status
= INQUIRE_BUSY
;
2433 void cleanup_assuan(assuan_context_t ctx
)
2435 struct client_s
*cl
= assuan_get_pointer(ctx
);
2441 static void reset_notify(assuan_context_t ctx
)
2443 struct client_s
*cl
= assuan_get_pointer(ctx
);
2449 static gpg_error_t
parse_client_option(assuan_context_t ctx
, const gchar
*line
)
2451 gchar name
[32] = {0}, value
[256] = {0};
2453 if (sscanf(line
, " %31[a-zA-Z] = %255c", name
, value
) != 2)
2454 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_SYNTAX
);
2456 if (g_strcasecmp(name
, (gchar
*)"NAME") == 0) {
2457 pth_attr_t attr
= pth_attr_of(pth_self());
2459 log_write("OPTION CLIENT %s", line
);
2460 pth_attr_set(attr
, PTH_ATTR_NAME
, value
);
2461 pth_attr_destroy(attr
);
2464 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2469 static int option_handler(assuan_context_t ctx
, const gchar
*name
,
2472 #ifdef WITH_PINENTRY
2473 struct client_s
*client
= assuan_get_pointer(ctx
);
2476 if (!value
|| !*value
)
2477 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2479 if (g_strcasecmp(name
, (gchar
*)"client") == 0)
2480 return parse_client_option(ctx
, value
);
2482 #ifdef WITH_PINENTRY
2483 if (g_strcasecmp(name
, (gchar
*)"ttyname") == 0) {
2484 g_free(client
->pinentry
->ttyname
);
2485 client
->pinentry
->ttyname
= g_strdup(value
);
2487 else if (g_strcasecmp(name
, (gchar
*)"ttytype") == 0) {
2488 g_free(client
->pinentry
->ttytype
);
2489 client
->pinentry
->ttytype
= g_strdup(value
);
2491 else if (g_strcasecmp(name
, (gchar
*)"display") == 0) {
2492 g_free(client
->pinentry
->display
);
2493 client
->pinentry
->display
= g_strdup(value
);
2495 else if (g_strcasecmp(name
, (gchar
*)"path") == 0) {
2496 g_free(client
->pinentry
->path
);
2497 client
->pinentry
->path
= g_strdup(value
);
2499 else if (g_strcasecmp(name
, (gchar
*)"title") == 0) {
2500 g_free(client
->pinentry
->title
);
2501 client
->pinentry
->title
= g_strdup(value
);
2503 else if (g_strcasecmp(name
, (gchar
*)"prompt") == 0) {
2504 g_free(client
->pinentry
->prompt
);
2505 client
->pinentry
->prompt
= g_strdup(value
);
2507 else if (g_strcasecmp(name
, (gchar
*)"desc") == 0) {
2508 g_free(client
->pinentry
->desc
);
2509 client
->pinentry
->desc
= g_strdup(value
);
2512 /* Need to wait for pinentry to support a --timeout option so it can
2513 * terminate itself. Cannot do it here because assuan_pipe_connect() calls
2514 * execv() which replaces the pid of the fork()ed thread from
2515 * pinentry_fork(). So pinentry will become a real process after the
2516 * thread terminates and won't be able to be kill()ed from pwmd.
2518 else if (g_strcasecmp(name
, (gchar
*)"timeout") == 0) {
2520 gint n
= strtol(value
, &p
, 10);
2523 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2525 client
->pinentry
->timeout
= n
;
2528 else if (g_strcasecmp(name
, (gchar
*)"pinentry") == 0) {
2530 gint n
= strtol(value
, &p
, 10);
2532 if (*p
|| n
< 0 || n
> 1)
2533 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2535 client
->pinentry
->enable
= n
== 0 ? FALSE
: TRUE
;
2538 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2540 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_NOT_IMPLEMENTED
);
2543 log_write("OPTION %s=%s", name
, value
);
2547 gpg_error_t
register_commands(assuan_context_t ctx
)
2551 gint (*handler
)(assuan_context_t
, gchar
*line
);
2553 { "OPEN", open_command
},
2554 { "SAVE", save_command
},
2555 { "LIST", list_command
},
2556 { "REALPATH", realpath_command
},
2557 { "STORE", store_command
},
2558 { "DELETE", delete_command
},
2559 { "GET", get_command
},
2560 { "ATTR", attr_command
},
2561 { "ISCACHED", iscached_command
},
2562 { "CLEARCACHE", clearcache_command
},
2563 { "CACHETIMEOUT", cachetimeout_command
},
2564 { "GETCONFIG", getconfig_command
},
2565 { "DUMP", dump_command
},
2566 { "XPATH", xpath_command
},
2567 { "IMPORT", import_command
},
2574 for (i
=0; table
[i
].name
; i
++) {
2575 rc
= assuan_register_command (ctx
, table
[i
].name
, table
[i
].handler
);
2581 rc
= assuan_register_bye_notify(ctx
, cleanup_assuan
);
2586 rc
= assuan_register_option_handler(ctx
, option_handler
);
2591 return assuan_register_reset_notify(ctx
, reset_notify
);
2594 gpg_error_t
try_xml_decrypt(assuan_context_t ctx
, gint fd
, struct stat st
,
2600 guchar tkey
[gcrykeysize
];
2601 struct client_s
*client
= ctx
? assuan_get_pointer(ctx
) : NULL
;
2602 gcry_cipher_hd_t gh
;
2603 guint iter
= 0, n_iter
= 0;
2605 void *outbuf
= NULL
;
2609 file_header_t file_header
;
2610 gchar str
[ASSUAN_LINELENGTH
];
2613 rc
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0);
2621 lseek(fd
, 0, SEEK_SET
);
2622 insize
= st
.st_size
- sizeof(file_header_t
);
2623 iv
= gcry_malloc(gcryblocksize
);
2627 gcry_cipher_close(gh
);
2629 return gpg_error_from_errno(ENOMEM
);
2632 len
= pth_read(fd
, &file_header
, sizeof(file_header_t
));
2634 if (len
!= sizeof(file_header_t
)) {
2638 gcry_cipher_close(gh
);
2642 return gpg_error_from_errno(errno
);
2645 /* No encryption iterations. This is a plain (gzipped) file. */
2646 if (file_header
.iter
== -1) {
2648 * cache_file_count() needs both .used == TRUE and a valid key in
2649 * order for it to count as a used cache entry. Fixes CACHE status
2652 memset(key
, '!', gcrykeysize
);
2653 insize
= st
.st_size
- sizeof(file_header_t
);
2656 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
2657 inbuf
= gcry_malloc(insize
);
2661 gcry_cipher_close(gh
);
2664 return gpg_error_from_errno(ENOMEM
);
2667 len
= pth_read(fd
, inbuf
, insize
);
2669 if (len
!= insize
) {
2673 gcry_cipher_close(gh
);
2677 return gpg_error_from_errno(errno
);
2680 if (file_header
.iter
== -1)
2683 memcpy(tkey
, key
, sizeof(tkey
));
2686 if ((rc
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2688 gcry_cipher_close(gh
);
2689 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2692 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2699 if ((rc
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
2701 gcry_cipher_close(gh
);
2702 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2705 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2711 gcry_cipher_close(gh
);
2716 iter_progress
= get_key_file_integer("global", "iteration_progress");
2718 if (ctx
&& iter_progress
> 0 && file_header
.iter
>= iter_progress
) {
2719 rc
= assuan_write_status(client
->ctx
, "DECRYPT", "0");
2728 rc
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2736 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
2738 gcry_cipher_close(gh
);
2739 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2742 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2749 while (iter
< file_header
.iter
) {
2750 if (ctx
&& iter_progress
> 0 && iter
>= iter_progress
) {
2751 if (!(iter
% iter_progress
)) {
2752 rc
= assuan_write_status(ctx
, "DECRYPT",
2753 print_fmt(str
, sizeof(str
), "%i", ++n_iter
* iter_progress
));
2763 if ((rc
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2765 gcry_cipher_close(gh
);
2766 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2769 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2776 rc
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2780 gcry_cipher_close(gh
);
2781 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2784 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2795 if (ctx
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
2796 rc
= assuan_write_status(ctx
, "DECRYPT",
2797 print_fmt(str
, sizeof(str
), "%i", file_header
.iter
));
2809 if (do_decompress(ctx
, inbuf
, insize
, &outbuf
, &outsize
, &zrc
) == FALSE
) {
2811 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2813 if (zrc
== Z_MEM_ERROR
) {
2815 return gpg_error_from_errno(ENOMEM
);
2817 else if (zrc
!= Z_DATA_ERROR
) {
2821 gcry_cipher_close(gh
);
2823 return EPWMD_BADKEY
;
2832 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
2836 gcry_cipher_close(gh
);
2838 return EPWMD_BADKEY
;
2842 client
->xml
= inbuf
;
2843 client
->len
= insize
;
2846 gcry_cipher_close(gh
);
2854 * This is called after every Assuan command.
2856 void command_finalize(assuan_context_t ctx
, gint rc
)
2858 struct client_s
*client
= assuan_get_pointer(ctx
);
2860 unlock_file_mutex(client
);