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
) {
170 * If a client disconnects unexpectedly while waiting for a
171 * lock, this lets the thread terminate because send_status()
172 * will return an error. We can't use an PTH_EVENT_FUNC here
173 * because the thread that would call the callback uses it's
174 * own stack space which messes up access to the client data
175 * (if I understand it correctly).
177 while (pth_mutex_acquire(m
, TRUE
, NULL
) == FALSE
) {
178 gpg_error_t rc
= send_status(client
->ctx
, STATUS_LOCKED
);
187 pth_mutex_acquire(m
, FALSE
, NULL
);
191 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(errno
));
192 return gpg_error_from_errno(e
);
196 client
->has_lock
= TRUE
;
200 void free_client(struct client_s
*client
)
203 xmlFreeDoc(client
->doc
);
206 gcry_free(client
->xml
);
208 if (client
->filename
)
209 g_free(client
->filename
);
212 gcry_cipher_close(client
->gh
);
215 void cleanup_client(struct client_s
*client
)
217 assuan_context_t ctx
= client
->ctx
;
219 struct pinentry_s
*pin
= client
->pinentry
;
222 unlock_file_mutex(client
);
223 CACHE_LOCK(client
->ctx
);
224 cache_decr_refcount(client
->md5file
);
227 * This may be a new file so don't use a cache slot. save_command() will
228 * set this to FALSE on success.
230 if (client
->new == TRUE
)
231 cache_clear(client
->md5file
, 1);
234 memset(client
, 0, sizeof(struct client_s
));
235 client
->state
= STATE_CONNECTED
;
237 client
->freed
= TRUE
;
239 client
->pinentry
= pin
;
244 gboolean
do_decompress(assuan_context_t ctx
, gpointer in
, gint insize
,
245 gpointer
*out
, glong
*outsize
, gint
*rc
)
251 gchar str
[ASSUAN_LINELENGTH
];
257 z
.avail_out
= zlib_bufsize
;
258 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
261 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
266 *rc
= inflateInit2(&z
, 47);
269 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
274 memset(&h
, 0, sizeof(gz_header
));
275 h
.comment
= (guchar
*)buf
;
276 h
.comm_max
= sizeof(buf
);
277 *rc
= inflateGetHeader(&z
, &h
);
280 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
286 *rc
= inflate(&z
, Z_BLOCK
);
289 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
296 insize
= atoi((gchar
*)h
.comment
);
301 *rc
= inflate(&z
, Z_FINISH
);
308 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
316 z
.next_out
= pout
+ z
.total_out
;
317 z
.avail_out
= zlib_bufsize
;
320 *rc
= assuan_write_status(ctx
, "DECOMPRESS",
321 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
336 } while (*rc
!= Z_STREAM_END
);
339 *rc
= assuan_write_status(ctx
, "DECOMPRESS",
340 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
347 *outsize
= z
.total_out
;
353 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
359 gpg_error_t
read_file_header(const gchar
*filename
, file_header_t
*fh
)
364 fd
= open(filename
, O_RDONLY
);
367 return gpg_error_from_errno(errno
);
369 len
= pth_read(fd
, fh
, sizeof(file_header_t
));
372 if (len
!= sizeof(file_header_t
))
373 return gpg_error_from_errno(errno
);
378 static gpg_error_t
open_command_finalize(assuan_context_t ctx
, guchar shakey
[],
381 struct client_s
*client
= assuan_get_pointer(ctx
);
388 if ((fd
= open_file(client
->filename
, &st
)) == -1) {
390 if (errno
== ENOENT
) {
398 log_write("%s: %s", client
->filename
, strerror(errno
));
399 cleanup_client(client
);
400 memset(shakey
, 0, sizeof(shakey
));
401 return send_syserror(ctx
, rc
);
404 rc
= try_xml_decrypt(ctx
, fd
, st
, shakey
, &iter
);
408 memset(shakey
, 0, sizeof(shakey
));
409 cleanup_client(client
);
410 return send_error(ctx
, rc
);
414 CACHE_LOCK(client
->ctx
);
416 if (cached
== FALSE
) {
417 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
418 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
419 cleanup_client(client
);
421 return send_error(ctx
, EPWMD_MAX_SLOTS
);
424 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
425 cache_reset_timeout(client
->md5file
, timeout
);
428 cache_set_timeout(client
->md5file
, -2);
433 memset(shakey
, 0, sizeof(shakey
));
437 gcry_free(client
->xml
);
442 if (client
->new == FALSE
)
443 send_status_all(STATUS_CACHE
);
445 client
->state
= STATE_OPEN
;
448 if (!rc
&& client
->new == FALSE
&&
449 iter
!= get_key_file_integer(client
->filename
, "iterations")) {
450 g_key_file_set_integer(keyfileh
, client
->filename
, "iterations", iter
);
451 send_status_all(STATUS_CONFIG
);
454 return send_error(ctx
, rc
);
457 static int open_command(assuan_context_t ctx
, char *line
)
460 guchar shakey
[gcrykeysize
];
461 gboolean cached
= FALSE
;
463 struct client_s
*client
= assuan_get_pointer(ctx
);
465 gchar
*filename
= NULL
;
466 file_header_t file_header
;
468 memset(shakey
, 0, sizeof(shakey
));
470 if ((req
= split_input_line(line
, " ", 2)) != NULL
)
473 if (!filename
|| !*filename
) {
475 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
478 if (valid_filename(filename
) == FALSE
) {
480 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
483 if (client
->state
== STATE_OPEN
)
484 cleanup_client(client
);
486 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
487 CACHE_LOCK(client
->ctx
);
489 if (cache_has_file(client
->md5file
) == FALSE
) {
490 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
493 return send_error(ctx
, EPWMD_MAX_SLOTS
);
497 cache_incr_refcount(client
->md5file
);
499 rc
= lock_file_mutex(client
);
503 return send_error(ctx
, rc
);
506 client
->freed
= FALSE
;
508 if ((rc
= gcry_cipher_open(&client
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
510 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
511 cleanup_client(client
);
512 return send_error(ctx
, rc
);
515 if (stat(filename
, &st
) == 0) {
516 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
517 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
519 cleanup_client(client
);
520 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
523 client
->mtime
= st
.st_mtime
;
526 client
->filename
= g_strdup(filename
);
528 if (!client
->filename
) {
529 memset(shakey
, 0, sizeof(shakey
));
530 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
531 cleanup_client(client
);
533 return send_syserror(ctx
, ENOMEM
);
537 client
->pinentry
->filename
= g_strdup(client
->filename
);
539 if (!client
->pinentry
->filename
) {
540 memset(shakey
, 0, sizeof(shakey
));
541 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
542 cleanup_client(client
);
544 return send_syserror(ctx
, ENOMEM
);
549 * New files don't need a key.
551 if (access(filename
, R_OK
) != 0) {
552 if (errno
!= ENOENT
) {
554 log_write("%s: %s", filename
, strerror(errno
));
556 cleanup_client(client
);
557 return send_syserror(ctx
, rc
);
560 if ((client
->xml
= new_document()) == NULL
) {
561 log_write("%s", strerror(ENOMEM
));
563 cleanup_client(client
);
564 return send_syserror(ctx
, ENOMEM
);
567 client
->len
= xmlStrlen(client
->xml
);
569 client
->filename
= g_strdup(filename
);
571 if (!client
->filename
) {
573 cleanup_client(client
);
574 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
575 return send_syserror(ctx
, ENOMEM
);
578 memset(shakey
, 0, sizeof(shakey
));
580 if (req
[1] && *req
[1])
581 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
584 return open_command_finalize(ctx
, shakey
, cached
);
587 rc
= read_file_header(filename
, &file_header
);
591 cleanup_client(client
);
592 return send_error(ctx
, rc
);
595 if (file_header
.iter
== -1)
598 CACHE_LOCK(client
->ctx
);
599 cached
= cache_get_key(client
->md5file
, shakey
);
602 if (cached
== FALSE
) {
604 * No key specified and no matching filename found in the cache. Use
605 * pinentry to retrieve the key. Cannot return assuan_process_done()
606 * here otherwise the command will be interrupted. The event loop in
607 * client_thread() will poll the file descriptor waiting for it to
608 * become ready to read a pinentry_key_s which will contain the
609 * entered key or rc. It will then call open_command_finalize() to
610 * to finish the command.
612 if (!req
[1] || !*req
[1]) {
614 gboolean b
= get_key_file_boolean(filename
, "enable_pinentry");
616 /* From set_pinentry_defaults(). */
617 if (client
->pinentry
->enable
== FALSE
||
618 (client
->pinentry
->enable
== -1 && b
== FALSE
)) {
619 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
624 lock_pin_mutex(client
);
625 client
->pinentry
->which
= PINENTRY_OPEN
;
626 rc
= pinentry_fork(ctx
);
629 unlock_pin_mutex(client
->pinentry
);
630 cleanup_client(client
);
631 return send_error(ctx
, rc
);
634 client
->pinentry
->cb
= open_command_finalize
;
635 client
->pinentry
->status
= PINENTRY_INIT
;
638 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
643 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
648 return open_command_finalize(ctx
, shakey
, cached
);
651 gboolean
do_compress(assuan_context_t ctx
, gint level
, gpointer data
,
652 gint size
, gpointer
*out
, glong
*outsize
, gint
*rc
)
658 gint cmd
= Z_NO_FLUSH
;
659 gchar str
[ASSUAN_LINELENGTH
];
663 z
.next_in
= pin
= data
;
664 z
.avail_in
= size
< zlib_bufsize
? size
: zlib_bufsize
;
665 z
.avail_out
= zlib_bufsize
;
666 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
669 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
674 *rc
= deflateInit2(&z
, level
, Z_DEFLATED
, 31, 8, Z_DEFAULT_STRATEGY
);
677 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
682 memset(&h
, 0, sizeof(gz_header
));
683 g_snprintf(buf
, sizeof(buf
), "%i", size
);
684 h
.comment
= (guchar
*)buf
;
685 *rc
= deflateSetHeader(&z
, &h
);
688 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
697 *rc
= deflate(&z
, cmd
);
704 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
712 z
.next_out
= pout
+ z
.total_out
;
713 z
.avail_out
= zlib_bufsize
;
716 if (!z
.avail_in
&& z
.total_in
< size
) {
717 if (z
.total_in
+ zlib_bufsize
> size
)
718 z
.avail_in
= size
- z
.total_in
;
720 z
.avail_in
= zlib_bufsize
;
723 *rc
= assuan_write_status(ctx
, "COMPRESS",
724 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
731 if (z
.total_in
>= size
)
742 } while (*rc
!= Z_STREAM_END
);
745 *rc
= assuan_write_status(ctx
, "COMPRESS",
746 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
753 *outsize
= z
.total_out
;
759 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
765 gpg_error_t
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
766 const gchar
*filename
, gpointer data
, size_t insize
, guchar
*shakey
,
772 guchar tkey
[gcrykeysize
];
775 guint iter_progress
= 0, n_iter
= 0, xiter
= 0;
776 gchar tmp
[FILENAME_MAX
];
779 file_header_t file_header
;
780 gchar str
[ASSUAN_LINELENGTH
];
784 * cache_file_count() needs both .used == TRUE and a valid key in
785 * order for it to count as a used cache entry. Fixes CACHE status
788 memset(shakey
, '!', gcrykeysize
);
790 file_header
.iter
= iter
;
794 if (insize
/ gcryblocksize
) {
795 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
797 if (insize
% gcryblocksize
)
798 len
+= gcryblocksize
;
802 * Resize the existing xml buffer to the block size required by gcrypt
803 * rather than duplicating it and wasting memory.
805 inbuf
= gcry_realloc(data
, len
);
808 return gpg_error_from_errno(ENOMEM
);
811 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
812 memcpy(tkey
, shakey
, sizeof(tkey
));
815 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
816 memset(tkey
, 0, sizeof(tkey
));
818 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
822 memset(tkey
, 0, sizeof(tkey
));
823 file_header
.iter
= iter
;
826 iter_progress
= get_key_file_integer(client
->filename
, "iteration_progress");
828 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
829 rc
= assuan_write_status(client
->ctx
, "ENCRYPT",
830 print_fmt(str
, sizeof(str
), "%i %i", 0, file_header
.iter
));
838 while (xiter
< file_header
.iter
) {
839 if (client
&& iter_progress
> 0 && xiter
>= iter_progress
) {
840 if (!(xiter
% iter_progress
)) {
841 rc
= assuan_write_status(client
->ctx
, "ENCRYPT",
842 print_fmt(str
, sizeof(str
), "%i %i", ++n_iter
* iter_progress
, file_header
.iter
));
851 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
852 sizeof(file_header
.iv
)))) {
854 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
858 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
861 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
869 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
870 sizeof(file_header
.iv
)))) {
872 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
876 if ((rc
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
878 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
882 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
884 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
888 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
889 rc
= assuan_write_status(client
->ctx
, "ENCRYPT",
890 print_fmt(str
, sizeof(str
), "%i %i", file_header
.iter
, file_header
.iter
));
900 if (stat(filename
, &st
) == 0) {
901 mode
= st
.st_mode
& (S_IRWXU
|S_IRWXG
|S_IRWXO
);
904 * FIXME What if the file has an ACL?
906 if (!(mode
& S_IWUSR
)) {
908 return gpg_error_from_errno(EACCES
);
912 if (errno
!= ENOENT
) {
915 return gpg_error_from_errno(rc
);
919 g_snprintf(tmp
, sizeof(tmp
), ".%s.tmp", filename
);
921 if ((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
924 p
= strrchr(tmp
, '/');
926 log_write("%s: %s", p
, strerror(rc
));
927 return gpg_error_from_errno(rc
);
932 * xml_import() from command line.
936 len
= pth_write(fd
, &file_header
, sizeof(file_header_t
));
938 if (len
!= sizeof(file_header
)) {
947 return gpg_error_from_errno(len
);
950 len
= pth_write(fd
, inbuf
, insize
);
961 return gpg_error_from_errno(len
);
964 if (fsync(fd
) == -1) {
973 return gpg_error_from_errno(len
);
979 if (mode
&& get_key_file_boolean(filename
, "backup") == TRUE
) {
980 gchar tmp2
[FILENAME_MAX
];
982 g_snprintf(tmp2
, sizeof(tmp2
), "%s.backup", filename
);
984 if (rename(filename
, tmp2
) == -1) {
988 return gpg_error_from_errno(len
);
992 if (rename(tmp
, filename
) == -1) {
996 return gpg_error_from_errno(len
);
1000 chmod(filename
, mode
);
1007 static gpg_error_t
save_command_finalize(assuan_context_t ctx
,
1008 guchar shakey
[], gboolean cached
)
1010 struct client_s
*client
= assuan_get_pointer(ctx
);
1022 xmlDocDumpFormatMemory(client
->doc
, &p
, &len
, 0);
1025 iter
= get_key_file_integer(client
->filename
, "compression_level");
1030 if (do_compress(ctx
, iter
, xmlbuf
, len
, &outbuf
, &outsize
, &zrc
) == FALSE
) {
1031 memset(shakey
, 0, sizeof(shakey
));
1034 if (zrc
== Z_MEM_ERROR
) {
1035 return send_syserror(ctx
, ENOMEM
);
1038 return send_error(ctx
, GPG_ERR_COMPR_ALGO
);
1046 iter
= get_key_file_integer(client
->filename
, "iterations");
1047 rc
= do_xml_encrypt(client
, client
->gh
, client
->filename
, xmlbuf
, len
, shakey
, iter
);
1050 memset(shakey
, 0, sizeof(shakey
));
1051 return send_error(ctx
, rc
);
1054 stat(client
->filename
, &st
);
1055 client
->mtime
= st
.st_mtime
;
1056 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
1057 CACHE_LOCK(client
->ctx
);
1060 memset(shakey
, 0, sizeof(shakey
));
1061 cache_reset_timeout(client
->md5file
, timeout
);
1064 if (client
->new == TRUE
)
1065 send_status_all(STATUS_CACHE
);
1067 client
->new = FALSE
;
1068 return send_error(ctx
, 0);
1071 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
1072 memset(shakey
, 0, sizeof(shakey
));
1073 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
1075 return send_error(ctx
, EPWMD_MAX_SLOTS
);
1078 client
->new = FALSE
;
1079 memset(shakey
, 0, sizeof(shakey
));
1080 cache_reset_timeout(client
->md5file
, timeout
);
1082 send_status_all(STATUS_CACHE
);
1083 return send_error(ctx
, 0);
1086 static int save_command(assuan_context_t ctx
, char *line
)
1088 gboolean cached
= FALSE
;
1089 guchar shakey
[gcrykeysize
];
1091 struct client_s
*client
= assuan_get_pointer(ctx
);
1094 memset(shakey
, 0, sizeof(shakey
));
1095 rc
= file_modified(client
);
1098 log_write("%s: %s", client
->filename
? client
->filename
: "",
1100 return send_error(ctx
, rc
);
1103 rc
= lock_file_mutex(client
);
1106 return send_error(ctx
, rc
);
1108 if (stat(client
->filename
, &st
) == -1 && errno
!= ENOENT
)
1109 return send_syserror(ctx
, errno
);
1111 if (errno
!= ENOENT
&& !S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
1112 log_write("%s: %s", client
->filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
1113 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
1116 if (get_key_file_integer(client
->filename
, "iterations") == -1)
1119 if (!line
|| !*line
) {
1120 guchar tmp
[sizeof(shakey
)];
1123 memset(tmp
, '!', sizeof(tmp
));
1125 if (cache_get_key(client
->md5file
, shakey
) == FALSE
||
1126 memcmp(shakey
, tmp
, sizeof(shakey
)) == 0) {
1128 #ifdef WITH_PINENTRY
1129 if (get_key_file_boolean(client
->filename
, "enable_pinentry") == FALSE
) {
1130 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1134 lock_pin_mutex(client
);
1135 client
->pinentry
->which
= PINENTRY_SAVE
;
1136 rc
= pinentry_fork(ctx
);
1139 unlock_pin_mutex(client
->pinentry
);
1140 return send_error(ctx
, rc
);
1143 client
->pinentry
->cb
= save_command_finalize
;
1144 client
->pinentry
->status
= PINENTRY_INIT
;
1147 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1157 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, line
, strlen(line
));
1158 memset(line
, 0, strlen(line
));
1162 return save_command_finalize(ctx
, shakey
, cached
);
1165 static int delete_command(assuan_context_t ctx
, char *line
)
1167 struct client_s
*client
= assuan_get_pointer(ctx
);
1172 rc
= file_modified(client
);
1175 log_write("%s: %s", client
->filename
? client
->filename
: "",
1177 return send_error(ctx
, rc
);
1180 if (strchr(line
, '\t'))
1181 req
= split_input_line(line
, "\t", -1);
1183 req
= split_input_line(line
, " ", -1);
1186 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1188 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1192 return send_error(ctx
, rc
);
1196 * No sub-node defined. Remove the entire node (account).
1205 return send_error(ctx
, 0);
1208 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1212 return send_error(ctx
, rc
);
1219 return send_error(ctx
, 0);
1223 * Don't return with assuan_process_done() here. This has been called from
1224 * assuan_process_next() and the command should be finished in
1227 static int store_command_finalize(gpointer data
, gint assuan_rc
, guchar
*line
,
1230 assuan_context_t ctx
= data
;
1231 struct client_s
*client
= assuan_get_pointer(ctx
);
1234 gpg_error_t rc
= file_modified(client
);
1236 if (assuan_rc
|| rc
) {
1243 return assuan_rc
? assuan_rc
: rc
;
1246 req
= split_input_line((gchar
*)line
, "\t", 0);
1254 return EPWMD_COMMAND_SYNTAX
;
1256 if (valid_xml_element((xmlChar
*)*req
) == FALSE
) {
1258 return EPWMD_INVALID_ELEMENT
;
1261 if (valid_element_path(req
+1, TRUE
) == FALSE
) {
1263 return EPWMD_INVALID_ELEMENT
;
1267 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1269 if (rc
&& rc
== EPWMD_ELEMENT_NOT_FOUND
) {
1270 rc
= new_account(client
->doc
, *req
);
1287 create_elements_cb(n
, req
+1, &rc
, NULL
);
1289 find_elements(client
->doc
, n
->children
, req
+1, &rc
,
1290 NULL
, NULL
, create_elements_cb
, FALSE
, 0, NULL
);
1294 client
->inquire_status
= INQUIRE_DONE
;
1298 static int store_command(assuan_context_t ctx
, char *line
)
1300 struct client_s
*client
= assuan_get_pointer(ctx
);
1301 gpg_error_t rc
= file_modified(client
);
1304 log_write("%s: %s", client
->filename
? client
->filename
: "",
1306 return send_error(ctx
, rc
);
1309 rc
= assuan_inquire_ext(ctx
, "STORE", 0, store_command_finalize
, ctx
);
1312 return send_error(ctx
, rc
);
1314 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1315 client
->inquire_status
= INQUIRE_BUSY
;
1319 static int get_command(assuan_context_t ctx
, char *line
)
1321 struct client_s
*client
= assuan_get_pointer(ctx
);
1326 rc
= file_modified(client
);
1329 log_write("%s: %s", client
->filename
? client
->filename
: "",
1331 return send_error(ctx
, rc
);
1334 req
= split_input_line(line
, "\t", -1);
1336 if (!req
|| !*req
) {
1338 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1341 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1345 return send_error(ctx
, rc
);
1349 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1354 return send_error(ctx
, rc
);
1356 if (!n
|| !n
->children
)
1357 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1359 n
= find_text_node(n
->children
);
1361 if (!n
|| !n
->content
|| !*n
->content
)
1362 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1364 rc
= assuan_send_data(ctx
, n
->content
, xmlStrlen(n
->content
));
1365 return send_error(ctx
, rc
);
1368 static xmlNodePtr
realpath_elements_cb(xmlNodePtr node
, gchar
**target
,
1369 gpg_error_t
*rc
, gchar
**req_orig
, void *data
)
1371 gchar
*path
= *(gchar
**)data
;
1372 gchar
*tmp
= NULL
, *result
;
1376 *(gchar
**)data
= NULL
;
1379 path
= g_strjoinv("\t", target
);
1382 *rc
= gpg_error_from_errno(ENOMEM
);
1387 tmp
= g_strjoinv("\t", req_orig
);
1391 *rc
= gpg_error_from_errno(ENOMEM
);
1397 result
= g_strdup_printf("%s\t%s", path
, tmp
);
1399 result
= g_strdup(path
);
1402 *rc
= gpg_error_from_errno(ENOMEM
);
1410 *(gchar
**)data
= result
;
1414 static int realpath_command(assuan_context_t ctx
, char *line
)
1417 struct client_s
*client
= assuan_get_pointer(ctx
);
1425 rc
= file_modified(client
);
1428 log_write("%s: %s", client
->filename
? client
->filename
: "",
1430 return send_error(ctx
, rc
);
1433 if (strchr(line
, '\t') != NULL
) {
1434 if ((req
= split_input_line(line
, "\t", 0)) == NULL
)
1435 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1438 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1439 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1442 n
= find_account(client
->doc
, &req
, &rc
, NULL
, 0);
1446 return send_error(ctx
, rc
);
1449 rp
= g_strjoinv("\t", req
);
1453 return send_syserror(ctx
, ENOMEM
);
1457 n
= find_elements(client
->doc
, n
->children
, req
+1, &rc
,
1458 NULL
, realpath_elements_cb
, NULL
, FALSE
, 0, &rp
);
1463 return send_error(ctx
, rc
);
1467 string
= g_string_new(rp
);
1472 return send_syserror(ctx
, ENOMEM
);
1475 for (i
= 0, t
= string
->str
+ i
; *t
; t
++, i
++) {
1476 if ((!i
&& *t
!= '!') || (*t
== '\t' && *(t
+1) && *(t
+1) != '!')) {
1477 string
= g_string_insert_c(string
, !i
? i
++ : ++i
, '!');
1482 rc
= assuan_send_data(ctx
, string
->str
, string
->len
);
1483 g_string_free(string
, TRUE
);
1484 return send_error(ctx
, rc
);
1487 static int list_command(assuan_context_t ctx
, char *line
)
1489 struct client_s
*client
= assuan_get_pointer(ctx
);
1491 struct element_list_s
*elements
= NULL
;
1494 if (disable_list_and_dump
== TRUE
)
1495 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
1497 rc
= file_modified(client
);
1500 log_write("%s: %s", client
->filename
, pwmd_strerror(rc
));
1501 return send_error(ctx
, rc
);
1507 rc
= list_accounts(client
->doc
, &str
);
1510 return send_error(ctx
, rc
);
1512 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1513 g_string_free(str
, TRUE
);
1514 return send_error(ctx
, rc
);
1517 elements
= g_malloc0(sizeof(struct element_list_s
));
1520 rc
= gpg_err_code_from_errno(ENOMEM
);
1524 rc
= create_path_list(client
->doc
, elements
, line
);
1530 gint total
= g_slist_length(elements
->list
);
1535 rc
= EPWMD_EMPTY_ELEMENT
;
1539 str
= g_string_new(NULL
);
1542 rc
= gpg_err_code_from_errno(ENOMEM
);
1546 for (i
= 0; i
< total
; i
++) {
1547 tmp
= g_slist_nth_data(elements
->list
, i
);
1548 g_string_append_printf(str
, "%s%s", tmp
, i
+1 == total
? "" : "\n");
1551 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1552 g_string_free(str
, TRUE
);
1555 rc
= EPWMD_EMPTY_ELEMENT
;
1559 gint total
= g_slist_length(elements
->list
);
1562 for (i
= 0; i
< total
; i
++) {
1563 tmp
= g_slist_nth_data(elements
->list
, i
);
1567 g_slist_free(elements
->list
);
1569 if (elements
->prefix
)
1570 g_free(elements
->prefix
);
1575 return send_error(ctx
, rc
);
1578 static gpg_error_t
add_attribute(xmlNodePtr node
, const gchar
*name
,
1583 if ((a
= xmlHasProp(node
, (xmlChar
*)name
)) == NULL
) {
1584 a
= xmlNewProp(node
, (xmlChar
*)name
, (xmlChar
*)value
);
1587 return EPWMD_LIBXML_ERROR
;
1590 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1596 * req[0] - element path
1598 static int attribute_list(assuan_context_t ctx
, gchar
**req
)
1600 struct client_s
*client
= assuan_get_pointer(ctx
);
1601 gchar
**attrlist
= NULL
;
1603 gchar
**path
= NULL
;
1609 if (!req
|| !req
[0])
1610 return EPWMD_COMMAND_SYNTAX
;
1612 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1614 * The first argument may be only an account.
1616 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
1617 return EPWMD_COMMAND_SYNTAX
;
1620 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1628 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1629 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1639 for (a
= n
->properties
; a
; a
= a
->next
) {
1642 if ((pa
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1644 g_strfreev(attrlist
);
1646 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
1647 return gpg_error_from_errno(ENOMEM
);
1652 attrlist
[i
] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1655 g_strfreev(attrlist
);
1656 return gpg_error_from_errno(ENOMEM
);
1659 attrlist
[++i
] = NULL
;
1663 return EPWMD_EMPTY_ELEMENT
;
1665 line
= g_strjoinv("\n", attrlist
);
1668 g_strfreev(attrlist
);
1669 return gpg_error_from_errno(ENOMEM
);
1672 rc
= assuan_send_data(ctx
, line
, strlen(line
));
1674 g_strfreev(attrlist
);
1679 * req[0] - attribute
1680 * req[1] - element path
1682 static int attribute_delete(struct client_s
*client
, gchar
**req
)
1686 gchar
**path
= NULL
;
1689 if (!req
|| !req
[0] || !req
[1])
1690 return EPWMD_COMMAND_SYNTAX
;
1692 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1694 * The first argument may be only an account.
1696 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1697 return EPWMD_COMMAND_SYNTAX
;
1701 * Don't remove the "name" attribute for the account element. To remove an
1702 * account use DELETE <account>.
1704 if (!path
[1] && xmlStrEqual((xmlChar
*)req
[0], (xmlChar
*)"name")) {
1705 rc
= EPWMD_ATTR_SYNTAX
;
1709 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1715 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1716 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1724 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
)
1725 return EPWMD_ATTR_NOT_FOUND
;
1727 if (xmlRemoveProp(a
) == -1)
1728 return EPWMD_LIBXML_ERROR
;
1737 static xmlNodePtr
create_element_path(struct client_s
*client
, gchar
***path
,
1740 gchar
**src
= *path
;
1741 gchar
**src_orig
= g_strdupv(src
);
1742 xmlNodePtr n
= NULL
;
1747 *rc
= gpg_error_from_errno(ENOMEM
);
1752 n
= find_account(client
->doc
, &src
, rc
, NULL
, 0);
1755 if (*rc
== EPWMD_ELEMENT_NOT_FOUND
) {
1756 *rc
= new_account(client
->doc
, src
[0]);
1769 n
= create_target_elements_cb(n
, src
+1, rc
, NULL
);
1771 n
= find_elements(client
->doc
, n
->children
, src
+1, rc
,
1772 NULL
, NULL
, create_target_elements_cb
, FALSE
, 0, NULL
);
1778 * Reset the position of the element tree now that the elements
1779 * have been created.
1784 n
= find_account(client
->doc
, &src
, rc
, NULL
, 0);
1789 n
= find_elements(client
->doc
, n
->children
, src
+1, rc
,
1790 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1798 g_strfreev(src_orig
);
1805 * Creates a "target" attribute. When other commands encounter an element with
1806 * this attribute, the element path is modified to the target value. If the
1807 * source element path doesn't exist when using 'ATTR SET target', it is
1808 * created, but the destination element path must exist.
1810 * req[0] - source element path
1811 * req[1] - destination element path
1813 static gpg_error_t
target_attribute(struct client_s
*client
, gchar
**req
)
1815 gchar
**src
, **dst
, *line
= NULL
;
1819 if (!req
|| !req
[0] || !req
[1])
1820 return EPWMD_COMMAND_SYNTAX
;
1822 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1824 * The first argument may be only an account.
1826 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
)
1827 return EPWMD_COMMAND_SYNTAX
;
1830 if (valid_element_path(src
, FALSE
) == FALSE
) {
1832 return EPWMD_INVALID_ELEMENT
;
1835 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1837 * The first argument may be only an account.
1839 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1840 rc
= EPWMD_COMMAND_SYNTAX
;
1845 n
= find_account(client
->doc
, &dst
, &rc
, NULL
, 0);
1848 * Make sure the destination element path exists.
1854 n
= find_elements(client
->doc
, n
->children
, dst
+1, &rc
,
1855 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1861 n
= create_element_path(client
, &src
, &rc
);
1866 line
= g_strjoinv("\t", dst
);
1869 rc
= gpg_error_from_errno(ENOMEM
);
1873 rc
= add_attribute(n
, "target", line
);
1883 * req[0] - account name
1886 static gpg_error_t
name_attribute(struct client_s
*client
, gchar
**req
)
1892 tmp
= g_strdupv(req
);
1895 return gpg_error_from_errno(ENOMEM
);
1897 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1903 if (g_utf8_collate(req
[0], req
[1]) == 0)
1907 * Will not overwrite an existing account.
1909 tmp
= g_strdupv(req
+1);
1912 return gpg_error_from_errno(ENOMEM
);
1914 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1917 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
)
1921 return EPWMD_ACCOUNT_EXISTS
;
1924 * Whitespace not allowed in account names.
1926 if (contains_whitespace(req
[1]) == TRUE
)
1927 return EPWMD_ATTR_SYNTAX
;
1929 tmp
= g_strdupv(req
);
1932 return gpg_error_from_errno(ENOMEM
);
1934 n
= find_account(client
->doc
, &tmp
, &rc
, NULL
, 0);
1938 return EPWMD_ELEMENT_NOT_FOUND
;
1940 return add_attribute(n
, "name", req
[1]);
1944 * req[0] - attribute
1945 * req[1] - element path
1947 static int attribute_get(assuan_context_t ctx
, gchar
**req
)
1949 struct client_s
*client
= assuan_get_pointer(ctx
);
1955 if (!req
|| !req
[0] || !req
[1])
1956 return EPWMD_COMMAND_SYNTAX
;
1958 if (strchr(req
[1], '\t')) {
1959 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
)
1960 return EPWMD_COMMAND_SYNTAX
;
1963 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1964 return EPWMD_COMMAND_SYNTAX
;
1967 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
1973 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
1974 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1982 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
)
1983 return EPWMD_ATTR_NOT_FOUND
;
1985 rc
= assuan_send_data(ctx
, a
, xmlStrlen(a
));
1995 * req[0] - attribute
1996 * req[1] - element path
1999 static int attribute_set(struct client_s
*client
, gchar
**req
)
2001 gchar
**path
= NULL
;
2005 if (!req
|| !req
[0] || !req
[1] || !req
[2])
2006 return EPWMD_COMMAND_SYNTAX
;
2009 * Reserved attribute names.
2011 if (g_utf8_collate(req
[0], "name") == 0) {
2013 * Only reserved for the account element. Not the rest of the
2016 if (strchr(req
[1], '\t') == NULL
)
2017 return name_attribute(client
, req
+ 1);
2019 else if (g_utf8_collate(req
[0], "target") == 0)
2020 return target_attribute(client
, req
+ 1);
2022 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
2024 * The first argument may be only an account.
2026 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
2027 return EPWMD_COMMAND_SYNTAX
;
2030 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
2036 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
,
2037 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2044 return add_attribute(n
, req
[0], req
[2]);
2053 * req[1] - attribute name or element path if command is LIST
2054 * req[2] - element path
2055 * req[2] - element path or value
2057 static int attr_command(assuan_context_t ctx
, char *line
)
2059 struct client_s
*client
= assuan_get_pointer(ctx
);
2063 rc
= file_modified(client
);
2066 log_write("%s: %s", client
->filename
? client
->filename
: "",
2068 return send_error(ctx
, rc
);
2071 req
= split_input_line(line
, " ", 4);
2073 if (!req
|| !req
[0] || !req
[1]) {
2075 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2078 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
2079 rc
= attribute_set(client
, req
+1);
2080 else if (g_ascii_strcasecmp(req
[0], "GET") == 0)
2081 rc
= attribute_get(ctx
, req
+1);
2082 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
2083 rc
= attribute_delete(client
, req
+1);
2084 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
2085 rc
= attribute_list(ctx
, req
+1);
2087 rc
= EPWMD_COMMAND_SYNTAX
;
2090 return send_error(ctx
, rc
);
2093 static int iscached_command(assuan_context_t ctx
, char *line
)
2095 gchar
**req
= split_input_line(line
, " ", 0);
2098 if (!req
|| !*req
) {
2100 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2103 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2107 if (cache_iscached(md5file
) == FALSE
) {
2109 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2113 return send_error(ctx
, 0);
2116 static int clearcache_command(assuan_context_t ctx
, char *line
)
2118 struct client_s
*client
= assuan_get_pointer(ctx
);
2119 gchar
**req
= split_input_line(line
, " ", 0);
2124 if (!req
|| !*req
) {
2126 cache_clear(client
->md5file
, 2);
2128 return send_error(ctx
, 0);
2131 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2134 if (cache_clear(md5file
, 1) == FALSE
) {
2136 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2140 return send_error(ctx
, 0);
2143 static int cachetimeout_command(assuan_context_t ctx
, char *line
)
2147 gchar
**req
= split_input_line(line
, " ", 0);
2149 struct client_s
*client
= assuan_get_pointer(ctx
);
2151 if (!req
|| !*req
|| !req
[1]) {
2153 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2157 timeout
= strtol(req
[0], &p
, 10);
2159 if (errno
!= 0 || *p
!= 0) {
2161 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2164 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2166 CACHE_LOCK(client
->ctx
);
2168 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2170 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2174 return send_error(ctx
, 0);
2177 static int dump_command(assuan_context_t ctx
, char *line
)
2181 struct client_s
*client
= assuan_get_pointer(ctx
);
2184 if (disable_list_and_dump
== TRUE
)
2185 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2187 rc
= file_modified(client
);
2190 log_write("%s: %s", client
->filename
? client
->filename
: "",
2192 return send_error(ctx
, rc
);
2195 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2198 return send_syserror(ctx
, ENOMEM
);
2200 rc
= assuan_send_data(ctx
, xml
, len
);
2202 return send_error(ctx
, rc
);
2205 static int getconfig_command(assuan_context_t ctx
, gchar
*line
)
2207 struct client_s
*client
= assuan_get_pointer(ctx
);
2209 gchar filename
[255]={0}, param
[747]={0};
2210 gchar
*p
, *tmp
, *fp
= client
->filename
, *paramp
= line
;
2212 if (strchr(line
, ' ')) {
2213 sscanf(line
, " %254[a-zA-Z] %746c", filename
, param
);
2218 paramp
= g_ascii_strdown(paramp
, -1);
2221 return send_syserror(ctx
, ENOMEM
);
2223 if (strcmp(paramp
, "key") == 0 || strcmp(paramp
, "key_file") == 0) {
2225 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2228 p
= get_key_file_string(fp
? fp
: "global", paramp
);
2232 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2234 tmp
= expand_homedir(p
);
2238 return send_syserror(ctx
, ENOMEM
);
2243 rc
= assuan_send_data(ctx
, p
, strlen(p
));
2245 return send_error(ctx
, rc
);
2248 static int xpath_command(assuan_context_t ctx
, gchar
*line
)
2250 struct client_s
*client
= assuan_get_pointer(ctx
);
2252 xmlXPathContextPtr xp
;
2253 xmlXPathObjectPtr result
;
2254 xmlBufferPtr buf
= NULL
;
2257 rc
= file_modified(client
);
2260 log_write("%s: %s", client
->filename
? client
->filename
: "",
2262 return send_error(ctx
, rc
);
2265 if (!line
|| !*line
)
2266 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2268 if ((req
= split_input_line(line
, "\t", 2)) == NULL
) {
2269 if (strv_printf(&req
, "%s", line
) == FALSE
)
2270 return send_syserror(ctx
, ENOMEM
);
2273 xp
= xmlXPathNewContext(client
->doc
);
2276 return send_error(ctx
, EPWMD_LIBXML_ERROR
);
2278 result
= xmlXPathEvalExpression((xmlChar
*)req
[0], xp
);
2281 xmlXPathFreeContext(xp
);
2282 return send_error(ctx
, EPWMD_LIBXML_ERROR
);
2285 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2286 rc
= EPWMD_EMPTY_ELEMENT
;
2290 rc
= recurse_xpath_nodeset(client
->doc
, result
->nodesetval
,
2291 (xmlChar
*)req
[1], &buf
);
2295 else if (!req
[1] && !xmlBufferLength(buf
)) {
2296 rc
= EPWMD_EMPTY_ELEMENT
;
2302 rc
= assuan_send_data(ctx
, xmlBufferContent(buf
), xmlBufferLength(buf
));
2311 xmlXPathFreeObject(result
);
2314 xmlXPathFreeContext(xp
);
2316 return send_error(ctx
, rc
);
2319 static int import_command_finalize(gpointer data
, gint assuan_rc
, guchar
*line
,
2322 struct client_s
*client
= assuan_get_pointer((assuan_context_t
)data
);
2323 gpg_error_t rc
= file_modified(client
);
2324 gchar
**req
, **path
= NULL
, **path_orig
= NULL
, *content
;
2326 xmlNodePtr n
, root
, copy
;
2328 if (assuan_rc
|| rc
) {
2335 return assuan_rc
? assuan_rc
: rc
;
2338 req
= split_input_line((gchar
*)line
, " ", 2);
2346 return EPWMD_COMMAND_SYNTAX
;
2348 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
2349 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
2350 return EPWMD_COMMAND_SYNTAX
;
2355 if (!content
|| !*content
) {
2356 rc
= EPWMD_COMMAND_SYNTAX
;
2360 if (valid_xml_element((xmlChar
*)*path
) == FALSE
) {
2361 rc
= EPWMD_INVALID_ELEMENT
;
2365 if (valid_element_path(path
+1, FALSE
) == FALSE
) {
2366 rc
= EPWMD_INVALID_ELEMENT
;
2370 doc
= xmlReadDoc((xmlChar
*)content
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
);
2373 rc
= EPWMD_LIBXML_ERROR
;
2377 root
= xmlDocGetRootElement(doc
);
2378 path_orig
= g_strdupv(path
);
2382 rc
= gpg_error_from_errno(ENOMEM
);
2386 if (strv_printf(&path
, "%s", (gchar
*)root
->name
) == FALSE
) {
2387 g_strfreev(path_orig
);
2389 rc
= gpg_error_from_errno(ENOMEM
);
2393 n
= find_account(client
->doc
, &path
, &rc
, NULL
, 0);
2395 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
) {
2396 g_strfreev(path_orig
);
2401 n
= find_elements(client
->doc
, n
->children
, path
+1, &rc
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
2403 if (rc
&& rc
!= EPWMD_ELEMENT_NOT_FOUND
) {
2404 g_strfreev(path_orig
);
2409 xmlNodePtr parent
= n
->parent
;
2420 if (rc
== EPWMD_ELEMENT_NOT_FOUND
) {
2421 n
= create_element_path(client
, &path
, &rc
);
2429 copy
= xmlCopyNode(root
, 1);
2430 n
= xmlAddChild(n
, copy
);
2434 rc
= EPWMD_LIBXML_ERROR
;
2439 client
->inquire_status
= INQUIRE_DONE
;
2443 static int import_command(assuan_context_t ctx
, gchar
*line
)
2446 struct client_s
*client
= assuan_get_pointer(ctx
);
2448 rc
= file_modified(client
);
2451 log_write("%s: %s", client
->filename
? client
->filename
: "",
2453 return send_error(ctx
, rc
);
2456 rc
= assuan_inquire_ext(ctx
, "IMPORT", 0, import_command_finalize
, ctx
);
2459 return send_error(ctx
, rc
);
2461 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2462 client
->inquire_status
= INQUIRE_BUSY
;
2466 static int lock_command(assuan_context_t ctx
, gchar
*line
)
2469 struct client_s
*client
= assuan_get_pointer(ctx
);
2471 rc
= file_modified(client
);
2474 log_write("%s: %s", client
->filename
? client
->filename
: "",
2476 return send_error(ctx
, rc
);
2479 rc
= lock_file_mutex(client
);
2482 client
->is_lock_cmd
= TRUE
;
2484 return send_error(ctx
, rc
);
2487 static int unlock_command(assuan_context_t ctx
, gchar
*line
)
2490 struct client_s
*client
= assuan_get_pointer(ctx
);
2492 rc
= file_modified(client
);
2495 log_write("%s: %s", client
->filename
? client
->filename
: "",
2497 return send_error(ctx
, rc
);
2500 unlock_file_mutex(client
);
2501 return send_error(ctx
, 0);
2504 static int getpid_command(assuan_context_t ctx
, gchar
*line
)
2508 pid_t pid
= getpid();
2510 print_fmt(buf
, sizeof(buf
), "%i", pid
);
2511 rc
= assuan_send_data(ctx
, buf
, strlen(buf
));
2512 return send_error(ctx
, rc
);
2515 void cleanup_assuan(assuan_context_t ctx
)
2517 struct client_s
*cl
= assuan_get_pointer(ctx
);
2523 static void reset_notify(assuan_context_t ctx
)
2525 struct client_s
*cl
= assuan_get_pointer(ctx
);
2531 static gpg_error_t
parse_client_option(assuan_context_t ctx
, const gchar
*line
)
2533 gchar name
[32] = {0}, value
[256] = {0};
2535 if (sscanf(line
, " %31[a-zA-Z] = %255c", name
, value
) != 2)
2536 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_SYNTAX
);
2538 if (g_strcasecmp(name
, (gchar
*)"NAME") == 0) {
2539 pth_attr_t attr
= pth_attr_of(pth_self());
2541 log_write("OPTION CLIENT %s", line
);
2542 pth_attr_set(attr
, PTH_ATTR_NAME
, value
);
2543 pth_attr_destroy(attr
);
2546 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2551 static int option_handler(assuan_context_t ctx
, const gchar
*name
,
2554 #ifdef WITH_PINENTRY
2555 struct client_s
*client
= assuan_get_pointer(ctx
);
2558 if (!value
|| !*value
)
2559 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2561 if (g_strcasecmp(name
, (gchar
*)"client") == 0)
2562 return parse_client_option(ctx
, value
);
2564 if (g_strcasecmp(name
, (gchar
*)"iterations") == 0) {
2569 n
= strtol(value
, &p
, 10);
2571 if (errno
|| (p
&& *p
))
2572 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2574 g_key_file_set_integer(keyfileh
, client
->filename
? client
->filename
: "global", "iterations", n
);
2575 send_status_all(STATUS_CONFIG
);
2577 #ifdef WITH_PINENTRY
2578 else if (g_strcasecmp(name
, (gchar
*)"ttyname") == 0) {
2579 g_free(client
->pinentry
->ttyname
);
2580 client
->pinentry
->ttyname
= g_strdup(value
);
2582 else if (g_strcasecmp(name
, (gchar
*)"ttytype") == 0) {
2583 g_free(client
->pinentry
->ttytype
);
2584 client
->pinentry
->ttytype
= g_strdup(value
);
2586 else if (g_strcasecmp(name
, (gchar
*)"display") == 0) {
2587 g_free(client
->pinentry
->display
);
2588 client
->pinentry
->display
= g_strdup(value
);
2590 else if (g_strcasecmp(name
, (gchar
*)"path") == 0) {
2591 g_free(client
->pinentry
->path
);
2592 client
->pinentry
->path
= g_strdup(value
);
2594 else if (g_strcasecmp(name
, (gchar
*)"title") == 0) {
2595 g_free(client
->pinentry
->title
);
2596 client
->pinentry
->title
= g_strdup(value
);
2598 else if (g_strcasecmp(name
, (gchar
*)"prompt") == 0) {
2599 g_free(client
->pinentry
->prompt
);
2600 client
->pinentry
->prompt
= g_strdup(value
);
2602 else if (g_strcasecmp(name
, (gchar
*)"desc") == 0) {
2603 g_free(client
->pinentry
->desc
);
2604 client
->pinentry
->desc
= g_strdup(value
);
2607 /* Need to wait for pinentry to support a --timeout option so it can
2608 * terminate itself. Cannot do it here because assuan_pipe_connect() calls
2609 * execv() which replaces the pid of the fork()ed thread from
2610 * pinentry_fork(). So pinentry will become a real process after the
2611 * thread terminates and won't be able to be kill()ed from pwmd.
2613 else if (g_strcasecmp(name
, (gchar
*)"timeout") == 0) {
2615 gint n
= strtol(value
, &p
, 10);
2618 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2620 client
->pinentry
->timeout
= n
;
2623 else if (g_strcasecmp(name
, (gchar
*)"pinentry") == 0) {
2625 gint n
= strtol(value
, &p
, 10);
2627 if (*p
|| n
< 0 || n
> 1)
2628 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2630 client
->pinentry
->enable
= n
== 0 ? FALSE
: TRUE
;
2634 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2637 log_write("OPTION %s=%s", name
, value
);
2641 gpg_error_t
register_commands(assuan_context_t ctx
)
2645 gint (*handler
)(assuan_context_t
, gchar
*line
);
2647 { "OPEN", open_command
},
2648 { "SAVE", save_command
},
2649 { "LIST", list_command
},
2650 { "REALPATH", realpath_command
},
2651 { "STORE", store_command
},
2652 { "DELETE", delete_command
},
2653 { "GET", get_command
},
2654 { "ATTR", attr_command
},
2655 { "ISCACHED", iscached_command
},
2656 { "CLEARCACHE", clearcache_command
},
2657 { "CACHETIMEOUT", cachetimeout_command
},
2658 { "GETCONFIG", getconfig_command
},
2659 { "DUMP", dump_command
},
2660 { "XPATH", xpath_command
},
2661 { "IMPORT", import_command
},
2662 { "LOCK", lock_command
},
2663 { "UNLOCK", unlock_command
},
2664 { "GETPID", getpid_command
},
2671 for (i
=0; table
[i
].name
; i
++) {
2672 rc
= assuan_register_command (ctx
, table
[i
].name
, table
[i
].handler
);
2678 rc
= assuan_register_bye_notify(ctx
, cleanup_assuan
);
2683 rc
= assuan_register_option_handler(ctx
, option_handler
);
2688 return assuan_register_reset_notify(ctx
, reset_notify
);
2691 gpg_error_t
try_xml_decrypt(assuan_context_t ctx
, gint fd
, struct stat st
,
2692 guchar
*key
, gint
*dst_iter
)
2697 guchar tkey
[gcrykeysize
];
2698 struct client_s
*client
= ctx
? assuan_get_pointer(ctx
) : NULL
;
2699 gcry_cipher_hd_t gh
;
2700 guint iter
= 0, n_iter
= 0;
2702 void *outbuf
= NULL
;
2706 file_header_t file_header
;
2707 gchar str
[ASSUAN_LINELENGTH
];
2710 rc
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0);
2718 lseek(fd
, 0, SEEK_SET
);
2719 insize
= st
.st_size
- sizeof(file_header_t
);
2720 iv
= gcry_malloc(gcryblocksize
);
2724 gcry_cipher_close(gh
);
2726 return gpg_error_from_errno(ENOMEM
);
2729 len
= pth_read(fd
, &file_header
, sizeof(file_header_t
));
2731 if (len
!= sizeof(file_header_t
)) {
2735 gcry_cipher_close(gh
);
2739 return gpg_error_from_errno(errno
);
2742 *dst_iter
= file_header
.iter
;
2744 /* No encryption iterations. This is a plain (gzipped) file. */
2745 if (file_header
.iter
== -1) {
2747 * cache_file_count() needs both .used == TRUE and a valid key in
2748 * order for it to count as a used cache entry. Fixes CACHE status
2751 memset(key
, '!', gcrykeysize
);
2752 insize
= st
.st_size
- sizeof(file_header_t
);
2755 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
2756 inbuf
= gcry_malloc(insize
);
2760 gcry_cipher_close(gh
);
2763 return gpg_error_from_errno(ENOMEM
);
2766 len
= pth_read(fd
, inbuf
, insize
);
2768 if (len
!= insize
) {
2772 gcry_cipher_close(gh
);
2776 return gpg_error_from_errno(errno
);
2779 if (file_header
.iter
== -1)
2782 if ((rc
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2784 gcry_cipher_close(gh
);
2785 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2788 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2795 if ((rc
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
2797 gcry_cipher_close(gh
);
2798 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2801 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2807 gcry_cipher_close(gh
);
2812 iter_progress
= get_key_file_integer(client
->filename
, "iteration_progress");
2814 if (ctx
&& iter_progress
> 0 && file_header
.iter
>= iter_progress
) {
2815 rc
= assuan_write_status(client
->ctx
, "DECRYPT",
2816 print_fmt(str
, sizeof(str
), "%i %i", 0, file_header
.iter
));
2825 rc
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2833 memcpy(tkey
, key
, sizeof(tkey
));
2836 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
2838 gcry_cipher_close(gh
);
2839 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2842 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2844 memset(tkey
, 0, sizeof(tkey
));
2850 memset(tkey
, 0, sizeof(tkey
));
2852 while (iter
< file_header
.iter
) {
2853 if (ctx
&& iter_progress
> 0 && iter
>= iter_progress
) {
2854 if (!(iter
% iter_progress
)) {
2855 rc
= assuan_write_status(ctx
, "DECRYPT",
2856 print_fmt(str
, sizeof(str
), "%i %i", ++n_iter
* iter_progress
, file_header
.iter
));
2866 if ((rc
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2868 gcry_cipher_close(gh
);
2869 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2872 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2879 rc
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2883 gcry_cipher_close(gh
);
2884 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2887 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
2898 if (ctx
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
2899 rc
= assuan_write_status(ctx
, "DECRYPT",
2900 print_fmt(str
, sizeof(str
), "%i %i", file_header
.iter
, file_header
.iter
));
2912 if (do_decompress(ctx
, inbuf
, insize
, &outbuf
, &outsize
, &zrc
) == FALSE
) {
2914 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2916 if (zrc
== Z_MEM_ERROR
) {
2918 return gpg_error_from_errno(ENOMEM
);
2920 else if (zrc
!= Z_DATA_ERROR
) {
2924 gcry_cipher_close(gh
);
2926 return EPWMD_BADKEY
;
2935 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
2939 gcry_cipher_close(gh
);
2941 return EPWMD_BADKEY
;
2945 client
->xml
= inbuf
;
2946 client
->len
= insize
;
2949 gcry_cipher_close(gh
);
2957 * This is called after every Assuan command.
2959 void command_finalize(assuan_context_t ctx
, gint rc
)
2961 struct client_s
*client
= assuan_get_pointer(ctx
);
2963 if (!client
->is_lock_cmd
)
2964 unlock_file_mutex(client
);