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 static 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
*error
)
231 gchar str
[ASSUAN_LINELENGTH
];
237 z
.avail_out
= zlib_bufsize
;
238 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
241 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
242 *error
= Z_MEM_ERROR
;
246 ret
= inflateInit2(&z
, 47);
249 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
254 memset(&h
, 0, sizeof(gz_header
));
255 h
.comment
= (guchar
*)buf
;
256 h
.comm_max
= sizeof(buf
);
257 ret
= inflateGetHeader(&z
, &h
);
260 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
266 ret
= inflate(&z
, Z_BLOCK
);
269 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
276 insize
= atoi((gchar
*)h
.comment
);
281 ret
= inflate(&z
, Z_FINISH
);
288 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
296 z
.next_out
= pout
+ z
.total_out
;
297 z
.avail_out
= zlib_bufsize
;
300 rc
= assuan_write_status(ctx
, "DECOMPRESS",
301 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
318 } while (ret
!= Z_STREAM_END
);
321 rc
= assuan_write_status(ctx
, "DECOMPRESS",
322 print_fmt(str
, sizeof(str
), "%i %i", z
.total_out
, insize
));
331 *outsize
= z
.total_out
;
336 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
343 gpg_error_t
read_file_header(const gchar
*filename
, file_header_t
*fh
)
348 fd
= open(filename
, O_RDONLY
);
351 return gpg_error_from_errno(errno
);
353 len
= pth_read(fd
, fh
, sizeof(file_header_t
));
356 if (len
!= sizeof(file_header_t
))
357 return gpg_error_from_errno(errno
);
362 static gpg_error_t
open_command_finalize(assuan_context_t ctx
, guchar shakey
[],
365 struct client_s
*client
= assuan_get_pointer(ctx
);
371 if ((fd
= open_file(client
->filename
, &st
)) == -1) {
373 if (errno
== ENOENT
) {
381 log_write("%s: %s", client
->filename
, strerror(errno
));
382 cleanup_client(client
);
383 memset(shakey
, 0, sizeof(shakey
));
384 return send_syserror(ctx
, error
);
387 error
= try_xml_decrypt(ctx
, fd
, st
, shakey
);
391 memset(shakey
, 0, sizeof(shakey
));
392 cleanup_client(client
);
393 return send_error(ctx
, error
);
397 CACHE_LOCK(client
->ctx
);
399 if (cached
== FALSE
) {
400 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
401 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
402 cleanup_client(client
);
404 return send_error(ctx
, EPWMD_MAX_SLOTS
);
407 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
408 cache_reset_timeout(client
->md5file
, timeout
);
411 cache_set_timeout(client
->md5file
, -2);
416 memset(shakey
, 0, sizeof(shakey
));
417 error
= parse_xml(ctx
);
420 gcry_free(client
->xml
);
425 if (client
->new == FALSE
)
426 send_cache_status_all();
428 client
->state
= STATE_OPEN
;
431 return send_error(ctx
, error
);
434 static int open_command(assuan_context_t ctx
, char *line
)
437 guchar shakey
[gcrykeysize
];
438 gboolean cached
= FALSE
;
440 struct client_s
*client
= assuan_get_pointer(ctx
);
442 gchar
*filename
= NULL
;
443 file_header_t file_header
;
445 memset(shakey
, 0, sizeof(shakey
));
447 if ((req
= split_input_line(line
, " ", 2)) != NULL
)
450 if (!filename
|| !*filename
) {
452 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
455 if (valid_filename(filename
) == FALSE
) {
457 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
460 if (client
->state
== STATE_OPEN
)
461 cleanup_client(client
);
463 gcry_md_hash_buffer(GCRY_MD_MD5
, client
->md5file
, filename
, strlen(filename
));
464 CACHE_LOCK(client
->ctx
);
466 if (cache_has_file(client
->md5file
) == FALSE
) {
467 if (cache_add_file(client
->md5file
, NULL
) == FALSE
) {
470 return send_error(ctx
, EPWMD_MAX_SLOTS
);
474 cache_incr_refcount(client
->md5file
);
476 error
= lock_file_mutex(client
);
480 return send_error(ctx
, error
);
483 client
->freed
= FALSE
;
485 if ((error
= gcry_cipher_open(&client
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
487 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
488 cleanup_client(client
);
489 return send_error(ctx
, error
);
492 if (stat(filename
, &st
) == 0) {
493 if (!S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
494 log_write("%s: %s", filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
496 cleanup_client(client
);
497 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
500 client
->mtime
= st
.st_mtime
;
503 client
->filename
= g_strdup(filename
);
505 if (!client
->filename
) {
506 memset(shakey
, 0, sizeof(shakey
));
507 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
508 cleanup_client(client
);
510 return send_syserror(ctx
, ENOMEM
);
514 client
->pinentry
->filename
= g_strdup(client
->filename
);
516 if (!client
->pinentry
->filename
) {
517 memset(shakey
, 0, sizeof(shakey
));
518 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
519 cleanup_client(client
);
521 return send_syserror(ctx
, ENOMEM
);
526 * New files don't need a key.
528 if (access(filename
, R_OK
|W_OK
) != 0) {
529 if (errno
!= ENOENT
) {
531 log_write("%s: %s", filename
, strerror(errno
));
533 cleanup_client(client
);
534 return send_syserror(ctx
, error
);
537 if ((client
->xml
= new_document()) == NULL
) {
538 log_write("%s", strerror(ENOMEM
));
540 cleanup_client(client
);
541 return send_syserror(ctx
, ENOMEM
);
544 client
->len
= xmlStrlen(client
->xml
);
546 client
->filename
= g_strdup(filename
);
548 if (!client
->filename
) {
550 cleanup_client(client
);
551 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(ENOMEM
));
552 return send_syserror(ctx
, ENOMEM
);
555 memset(shakey
, 0, sizeof(shakey
));
557 if (req
[1] && *req
[1])
558 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, req
[1], strlen(req
[1]));
561 return open_command_finalize(ctx
, shakey
, cached
);
564 error
= read_file_header(filename
, &file_header
);
568 cleanup_client(client
);
569 return send_error(ctx
, error
);
572 if (file_header
.iter
== -1)
575 CACHE_LOCK(client
->ctx
);
576 cached
= cache_get_key(client
->md5file
, shakey
);
579 if (cached
== FALSE
) {
581 * No key specified and no matching filename found in the cache. Use
582 * pinentry to retrieve the key. Cannot return assuan_process_done()
583 * here otherwise the command will be interrupted. The event loop in
584 * client_thread() will poll the file descriptor waiting for it to
585 * become ready to read a pinentry_key_s which will contain the
586 * entered key or error. It will then call open_command_finalize() to
587 * to finish the command.
589 if (!req
[1] || !*req
[1]) {
591 if (get_key_file_boolean(filename
, "enable_pinentry") == FALSE
) {
592 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
597 client
->pinentry
->which
= PINENTRY_OPEN
;
598 error
= pinentry_fork(ctx
);
601 cleanup_client(client
);
602 return send_error(ctx
, error
);
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
*error
)
630 gint cmd
= Z_NO_FLUSH
;
632 gchar str
[ASSUAN_LINELENGTH
];
636 z
.next_in
= pin
= data
;
637 z
.avail_in
= size
< zlib_bufsize
? size
: zlib_bufsize
;
638 z
.avail_out
= zlib_bufsize
;
639 z
.next_out
= pout
= g_malloc(zlib_bufsize
);
642 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, strerror(ENOMEM
));
643 *error
= Z_MEM_ERROR
;
647 ret
= deflateInit2(&z
, level
, Z_DEFLATED
, 31, 8, Z_DEFAULT_STRATEGY
);
650 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
656 memset(&h
, 0, sizeof(gz_header
));
657 snprintf(buf
, sizeof(buf
), "%i", size
);
658 h
.comment
= (guchar
*)buf
;
659 ret
= deflateSetHeader(&z
, &h
);
662 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
672 ret
= deflate(&z
, cmd
);
679 p
= g_realloc(pout
, z
.total_out
+ zlib_bufsize
);
687 z
.next_out
= pout
+ z
.total_out
;
688 z
.avail_out
= zlib_bufsize
;
691 if (!z
.avail_in
&& z
.total_in
< size
) {
692 if (z
.total_in
+ zlib_bufsize
> size
)
693 z
.avail_in
= size
- z
.total_in
;
695 z
.avail_in
= zlib_bufsize
;
698 rc
= assuan_write_status(ctx
, "COMPRESS",
699 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
708 if (z
.total_in
>= size
)
719 } while (ret
!= Z_STREAM_END
);
722 rc
= assuan_write_status(ctx
, "COMPRESS",
723 print_fmt(str
, sizeof(str
), "%i %i", z
.total_in
, size
));
732 *outsize
= z
.total_out
;
737 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, z
.msg
);
744 gpg_error_t
do_xml_encrypt(struct client_s
*client
, gcry_cipher_hd_t gh
,
745 const gchar
*filename
, gpointer data
, size_t insize
, guchar
*shakey
,
751 guchar tkey
[gcrykeysize
];
755 guint iter_progress
= 0, n_iter
= 0, xiter
= 0;
756 gchar tmp
[FILENAME_MAX
];
757 file_header_t file_header
;
758 gchar str
[ASSUAN_LINELENGTH
];
762 * cache_file_count() needs both .used == TRUE and a valid key in
763 * order for it to count as a used cache entry. Fixes CACHE status
766 memset(shakey
, '!', gcrykeysize
);
768 file_header
.iter
= iter
;
772 if (insize
/ gcryblocksize
) {
773 len
= (insize
/ gcryblocksize
) * gcryblocksize
;
775 if (insize
% gcryblocksize
)
776 len
+= gcryblocksize
;
780 * Resize the existing xml buffer to the block size required by gcrypt
781 * rather than duplicating it and wasting memory.
783 inbuf
= gcry_realloc(data
, len
);
786 return gpg_error_from_errno(ENOMEM
);
789 gcry_create_nonce(file_header
.iv
, sizeof(file_header
.iv
));
790 memcpy(tkey
, shakey
, sizeof(tkey
));
793 if ((rc
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
795 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
799 file_header
.iter
= iter
;
802 iter_progress
= get_key_file_integer("global", "iteration_progress");
804 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
805 error
= assuan_write_status(client
->ctx
, "ENCRYPT", "0");
813 while (xiter
< file_header
.iter
) {
814 if (client
&& iter_progress
> 0 && xiter
>= iter_progress
) {
815 if (!(xiter
% iter_progress
)) {
816 error
= assuan_write_status(client
->ctx
, "ENCRYPT",
817 print_fmt(str
, sizeof(str
), "%i", ++n_iter
* iter_progress
));
826 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
827 sizeof(file_header
.iv
)))) {
829 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
833 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0)
836 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
844 if ((rc
= gcry_cipher_setiv(gh
, file_header
.iv
,
845 sizeof(file_header
.iv
)))) {
847 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
851 if ((rc
= gcry_cipher_setkey(gh
, shakey
, gcrykeysize
))) {
853 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
857 if (encrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
859 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(rc
));
863 if (client
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
864 error
= assuan_write_status(client
->ctx
, "ENCRYPT",
865 print_fmt(str
, sizeof(str
), "%i", file_header
.iter
));
875 g_snprintf(tmp
, sizeof(tmp
), ".%s.tmp", filename
);
877 if ((fd
= open(tmp
, O_WRONLY
|O_CREAT
|O_TRUNC
, 0600)) == -1) {
880 p
= strrchr(tmp
, '/');
882 log_write("%s: %s", p
, strerror(errno
));
883 return gpg_error_from_errno(error
);
888 * xml_import() from command line.
892 len
= pth_write(fd
, &file_header
, sizeof(file_header_t
));
894 if (len
!= sizeof(file_header
)) {
901 return gpg_error_from_errno(len
);
904 len
= pth_write(fd
, inbuf
, insize
);
913 return gpg_error_from_errno(len
);
916 if (fsync(fd
) == -1) {
920 return gpg_error_from_errno(len
);
929 if (stat(filename
, &st
) == 0)
930 mode
= st
.st_mode
& (S_IRWXU
|S_IRWXG
|S_IRWXO
);
932 if (rename(tmp
, filename
) == -1) {
935 return gpg_error_from_errno(len
);
939 chmod(filename
, mode
);
946 static gpg_error_t
save_command_finalize(assuan_context_t ctx
,
947 guchar shakey
[], gboolean cached
)
949 struct client_s
*client
= assuan_get_pointer(ctx
);
961 xmlDocDumpFormatMemory(client
->doc
, &p
, &len
, 0);
964 iter
= get_key_file_integer(client
->filename
, "compression_level");
969 if (do_compress(ctx
, iter
, xmlbuf
, len
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
970 memset(shakey
, 0, sizeof(shakey
));
973 if (zerror
== Z_MEM_ERROR
) {
974 return send_syserror(ctx
, ENOMEM
);
977 return send_error(ctx
, GPG_ERR_COMPR_ALGO
);
985 iter
= get_key_file_integer(client
->filename
, "iterations");
986 error
= do_xml_encrypt(client
, client
->gh
, client
->filename
, xmlbuf
, len
, shakey
, iter
);
989 memset(shakey
, 0, sizeof(shakey
));
990 return send_error(ctx
, error
);
993 stat(client
->filename
, &st
);
994 client
->mtime
= st
.st_mtime
;
995 timeout
= get_key_file_integer(client
->filename
, "cache_timeout");
996 CACHE_LOCK(client
->ctx
);
999 memset(shakey
, 0, sizeof(shakey
));
1000 cache_reset_timeout(client
->md5file
, timeout
);
1003 if (client
->new == TRUE
)
1004 send_cache_status_all();
1006 client
->new = FALSE
;
1007 return send_error(ctx
, 0);
1010 if (cache_update_key(client
->md5file
, shakey
) == FALSE
) {
1011 memset(shakey
, 0, sizeof(shakey
));
1012 log_write("%s(%i): %s", __FILE__
, __LINE__
, pwmd_strerror(EPWMD_MAX_SLOTS
));
1014 return send_error(ctx
, EPWMD_MAX_SLOTS
);
1017 client
->new = FALSE
;
1018 memset(shakey
, 0, sizeof(shakey
));
1019 cache_reset_timeout(client
->md5file
, timeout
);
1021 send_cache_status_all();
1022 return send_error(ctx
, 0);
1025 static int save_command(assuan_context_t ctx
, char *line
)
1027 gboolean cached
= FALSE
;
1028 guchar shakey
[gcrykeysize
];
1030 struct client_s
*client
= assuan_get_pointer(ctx
);
1033 memset(shakey
, 0, sizeof(shakey
));
1034 error
= file_modified(client
);
1037 log_write("%s: %s", client
->filename
? client
->filename
: "",
1038 pwmd_strerror(error
));
1039 return send_error(ctx
, error
);
1042 error
= lock_file_mutex(client
);
1045 return send_error(ctx
, error
);
1047 if (stat(client
->filename
, &st
) == -1 && errno
!= ENOENT
)
1048 return send_syserror(ctx
, errno
);
1050 if (errno
!= ENOENT
&& !S_ISREG(st
.st_mode
) && !S_ISLNK(st
.st_mode
)) {
1051 log_write("%s: %s", client
->filename
, pwmd_strerror(EPWMD_INVALID_FILENAME
));
1052 return send_error(ctx
, EPWMD_INVALID_FILENAME
);
1055 if (get_key_file_integer(client
->filename
, "iterations") == -1)
1058 if (!line
|| !*line
) {
1059 guchar tmp
[sizeof(shakey
)];
1062 memset(tmp
, '!', sizeof(tmp
));
1064 if (cache_get_key(client
->md5file
, shakey
) == FALSE
||
1065 memcmp(shakey
, tmp
, sizeof(shakey
)) == 0) {
1067 #ifdef WITH_PINENTRY
1068 if (get_key_file_boolean(client
->filename
, "enable_pinentry") == FALSE
) {
1069 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1073 client
->pinentry
->which
= PINENTRY_SAVE
;
1074 error
= pinentry_fork(ctx
);
1077 return send_error(ctx
, error
);
1079 client
->pinentry
->cb
= save_command_finalize
;
1080 client
->pinentry
->status
= PINENTRY_INIT
;
1083 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, "", 1);
1093 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, line
, strlen(line
));
1094 memset(line
, 0, strlen(line
));
1098 return save_command_finalize(ctx
, shakey
, cached
);
1101 static int delete_command(assuan_context_t ctx
, char *line
)
1103 struct client_s
*client
= assuan_get_pointer(ctx
);
1108 error
= file_modified(client
);
1111 log_write("%s: %s", client
->filename
? client
->filename
: "",
1112 pwmd_strerror(error
));
1113 return send_error(ctx
, error
);
1116 if (strchr(line
, '\t'))
1117 req
= split_input_line(line
, "\t", -1);
1119 req
= split_input_line(line
, " ", -1);
1122 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1124 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1128 return send_error(ctx
, error
);
1132 * No sub-node defined. Remove the entire node (account).
1141 return send_error(ctx
, 0);
1144 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1148 return send_error(ctx
, error
);
1155 return send_error(ctx
, 0);
1159 * Don't return with assuan_process_done() here. This has been called from
1160 * assuan_process_next() and the command should be finished in
1163 static int store_command_finalize(gpointer data
, gint rc
, guchar
*line
,
1166 assuan_context_t ctx
= data
;
1167 struct client_s
*client
= assuan_get_pointer(ctx
);
1169 guchar
*result
= line
;
1171 gpg_error_t error
= file_modified(client
);
1183 req
= split_input_line((gchar
*)result
, "\t", 0);
1198 return EPWMD_COMMAND_SYNTAX
;
1200 if (valid_xml_element((xmlChar
*)*req
) == FALSE
) {
1202 return EPWMD_INVALID_ELEMENT
;
1205 if (valid_element_path(req
+1, TRUE
) == FALSE
) {
1207 return EPWMD_INVALID_ELEMENT
;
1211 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1213 if (error
&& error
== EPWMD_ELEMENT_NOT_FOUND
) {
1214 error
= new_account(client
->doc
, *req
);
1231 create_elements_cb(n
, req
+1, &error
, NULL
);
1233 find_elements(client
->doc
, n
->children
, req
+1, &error
,
1234 NULL
, NULL
, create_elements_cb
, FALSE
, 0, NULL
);
1238 client
->inquire_status
= INQUIRE_DONE
;
1242 static int store_command(assuan_context_t ctx
, char *line
)
1244 struct client_s
*client
= assuan_get_pointer(ctx
);
1245 gpg_error_t error
= file_modified(client
);
1248 log_write("%s: %s", client
->filename
? client
->filename
: "",
1249 pwmd_strerror(error
));
1250 return send_error(ctx
, error
);
1253 error
= assuan_inquire_ext(ctx
, "STORE", 0, store_command_finalize
, ctx
);
1256 return send_error(ctx
, error
);
1258 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1259 client
->inquire_status
= INQUIRE_BUSY
;
1263 static int get_command(assuan_context_t ctx
, char *line
)
1265 struct client_s
*client
= assuan_get_pointer(ctx
);
1270 error
= file_modified(client
);
1273 log_write("%s: %s", client
->filename
? client
->filename
: "",
1274 pwmd_strerror(error
));
1275 return send_error(ctx
, error
);
1278 req
= split_input_line(line
, "\t", -1);
1280 if (!req
|| !*req
) {
1282 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1285 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1289 return send_error(ctx
, error
);
1293 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
, NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1298 return send_error(ctx
, error
);
1300 if (!n
|| !n
->children
)
1301 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1303 n
= find_text_node(n
->children
);
1305 if (!n
|| !n
->content
|| !*n
->content
)
1306 return send_error(ctx
, EPWMD_EMPTY_ELEMENT
);
1308 error
= assuan_send_data(ctx
, n
->content
, xmlStrlen(n
->content
));
1309 return send_error(ctx
, error
);
1312 static xmlNodePtr
realpath_elements_cb(xmlNodePtr node
, gchar
**req
,
1313 gpg_error_t
*error
, void *data
)
1315 gchar
*account
= *(gchar
**)data
;
1320 account
= g_strdup(req
[0]);
1323 *error
= gpg_error_from_errno(ENOMEM
);
1324 *(gchar
**)data
= NULL
;
1328 *(gchar
**)data
= account
;
1332 static int realpath_command(assuan_context_t ctx
, char *line
)
1335 struct client_s
*client
= assuan_get_pointer(ctx
);
1342 gchar
*rp_account
= NULL
;
1344 error
= file_modified(client
);
1347 log_write("%s: %s", client
->filename
? client
->filename
: "",
1348 pwmd_strerror(error
));
1349 return send_error(ctx
, error
);
1352 if (strchr(line
, '\t') != NULL
) {
1353 if ((req
= split_input_line(line
, "\t", 0)) == NULL
)
1354 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1357 if ((req
= split_input_line(line
, " ", 0)) == NULL
)
1358 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1361 n
= find_account(client
->doc
, &req
, &error
, NULL
, 0);
1365 return send_error(ctx
, error
);
1368 rp_account
= g_strdup(req
[0]);
1372 return send_syserror(ctx
, ENOMEM
);
1376 n
= find_elements(client
->doc
, n
->children
, req
+1, &error
,
1377 NULL
, realpath_elements_cb
, NULL
, FALSE
, 0, &rp_account
);
1382 return send_error(ctx
, error
);
1386 p
= xmlGetNodePath(n
);
1387 result
= element_path_to_req(rp_account
, p
);
1394 return send_syserror(ctx
, ENOMEM
);
1397 string
= g_string_new(result
);
1405 for (t
= string
->str
+ i
; *t
; t
++, i
++) {
1406 if ((!i
&& *t
!= '!') || *t
== '\t') {
1407 string
= g_string_insert_c(string
, !i
? i
++ : ++i
, '!');
1412 error
= assuan_send_data(ctx
, string
->str
, string
->len
);
1413 g_string_free(string
, TRUE
);
1414 return send_error(ctx
, error
);
1417 static int list_command(assuan_context_t ctx
, char *line
)
1419 struct client_s
*client
= assuan_get_pointer(ctx
);
1421 struct element_list_s
*elements
= NULL
;
1424 if (disable_list_and_dump
== TRUE
)
1425 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
1427 rc
= file_modified(client
);
1430 log_write("%s: %s", client
->filename
, pwmd_strerror(rc
));
1431 return send_error(ctx
, rc
);
1437 rc
= list_accounts(client
->doc
, &str
);
1440 return send_error(ctx
, rc
);
1442 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1443 g_string_free(str
, TRUE
);
1444 return send_error(ctx
, rc
);
1447 elements
= g_malloc0(sizeof(struct element_list_s
));
1450 rc
= gpg_err_code_from_errno(ENOMEM
);
1454 rc
= create_path_list(client
->doc
, elements
, line
);
1460 gint total
= g_slist_length(elements
->list
);
1465 rc
= EPWMD_EMPTY_ELEMENT
;
1469 str
= g_string_new(NULL
);
1472 rc
= gpg_err_code_from_errno(ENOMEM
);
1476 for (i
= 0; i
< total
; i
++) {
1477 tmp
= g_slist_nth_data(elements
->list
, i
);
1478 g_string_append_printf(str
, "%s%s", tmp
, i
+1 == total
? "" : "\n");
1481 rc
= assuan_send_data(ctx
, str
->str
, str
->len
);
1482 g_string_free(str
, TRUE
);
1485 rc
= EPWMD_EMPTY_ELEMENT
;
1489 gint total
= g_slist_length(elements
->list
);
1492 for (i
= 0; i
< total
; i
++) {
1493 tmp
= g_slist_nth_data(elements
->list
, i
);
1497 g_slist_free(elements
->list
);
1499 if (elements
->prefix
)
1500 g_free(elements
->prefix
);
1505 return send_error(ctx
, rc
);
1508 static gpg_error_t
add_attribute(xmlNodePtr node
, const gchar
*name
,
1513 if ((a
= xmlHasProp(node
, (xmlChar
*)name
)) == NULL
) {
1514 a
= xmlNewProp(node
, (xmlChar
*)name
, (xmlChar
*)value
);
1517 return EPWMD_LIBXML_ERROR
;
1520 xmlNodeSetContent(a
->children
, (xmlChar
*)value
);
1526 * req[0] - element path
1528 static int attribute_list(assuan_context_t ctx
, gchar
**req
)
1530 struct client_s
*client
= assuan_get_pointer(ctx
);
1531 gchar
**attrlist
= NULL
;
1533 gchar
**path
= NULL
;
1539 if (!req
|| !req
[0])
1540 return EPWMD_COMMAND_SYNTAX
;
1542 if ((path
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1544 * The first argument may be only an account.
1546 if ((path
= split_input_line(req
[0], " ", 0)) == NULL
)
1547 return EPWMD_COMMAND_SYNTAX
;
1550 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1558 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1559 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1569 for (a
= n
->properties
; a
; a
= a
->next
) {
1572 if ((pa
= g_realloc(attrlist
, (i
+ 2) * sizeof(gchar
*))) == NULL
) {
1574 g_strfreev(attrlist
);
1577 log_write("%s(%i): %s", __FILE__
, __LINE__
, strerror(errno
));
1578 return gpg_error_from_errno(error
);
1583 attrlist
[i
] = g_strdup_printf("%s\t%s", (gchar
*)a
->name
, (gchar
*)an
->content
);
1586 g_strfreev(attrlist
);
1587 return gpg_error_from_errno(ENOMEM
);
1590 attrlist
[++i
] = NULL
;
1594 return EPWMD_EMPTY_ELEMENT
;
1596 line
= g_strjoinv("\n", attrlist
);
1599 g_strfreev(attrlist
);
1600 return gpg_error_from_errno(ENOMEM
);
1603 error
= assuan_send_data(ctx
, line
, strlen(line
));
1605 g_strfreev(attrlist
);
1610 * req[0] - attribute
1611 * req[1] - element path
1613 static int attribute_delete(struct client_s
*client
, gchar
**req
)
1617 gchar
**path
= NULL
;
1620 if (!req
|| !req
[0] || !req
[1])
1621 return EPWMD_COMMAND_SYNTAX
;
1623 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1625 * The first argument may be only an account.
1627 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1628 return EPWMD_COMMAND_SYNTAX
;
1632 * Don't remove the "name" attribute for the account element. To remove an
1633 * account use DELETE <account>.
1635 if (!path
[1] && xmlStrEqual((xmlChar
*)req
[0], (xmlChar
*)"name")) {
1636 error
= EPWMD_ATTR_SYNTAX
;
1640 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1646 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1647 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1655 if ((a
= xmlHasProp(n
, (xmlChar
*)req
[0])) == NULL
)
1656 return EPWMD_ATTR_NOT_FOUND
;
1658 if (xmlRemoveProp(a
) == -1)
1659 return EPWMD_LIBXML_ERROR
;
1669 * Creates a "target" attribute. When other commands encounter an element with
1670 * this attribute, the element path is modified to the target value. If the
1671 * source element path doesn't exist when using 'ATTR SET target', it is
1672 * created, but the destination element path must exist.
1674 * req[0] - source element path
1675 * req[1] - destination element path
1677 static gpg_error_t
target_attribute(struct client_s
*client
, gchar
**req
)
1679 gchar
**src
, **dst
, *line
= NULL
;
1682 gchar
**src_orig
= NULL
;
1684 if (!req
|| !req
[0] || !req
[1])
1685 return EPWMD_COMMAND_SYNTAX
;
1687 if ((src
= split_input_line(req
[0], "\t", 0)) == NULL
) {
1689 * The first argument may be only an account.
1691 if ((src
= split_input_line(req
[0], " ", 0)) == NULL
)
1692 return EPWMD_COMMAND_SYNTAX
;
1695 if (valid_element_path(src
, FALSE
) == FALSE
) {
1697 return EPWMD_INVALID_ELEMENT
;
1700 if ((dst
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1702 * The first argument may be only an account.
1704 if ((dst
= split_input_line(req
[1], " ", 0)) == NULL
) {
1705 error
= EPWMD_COMMAND_SYNTAX
;
1710 n
= find_account(client
->doc
, &dst
, &error
, NULL
, 0);
1713 * Make sure the destination element path exists.
1719 n
= find_elements(client
->doc
, n
->children
, dst
+1, &error
,
1720 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1727 src_orig
= g_strdupv(src
);
1728 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
1731 if (error
== EPWMD_ELEMENT_NOT_FOUND
) {
1732 error
= new_account(client
->doc
, src
[0]);
1745 n
= create_target_elements_cb(n
, src
+1, &error
, NULL
);
1747 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
1748 NULL
, NULL
, create_target_elements_cb
, FALSE
, 0, NULL
);
1754 * Reset the position of the element tree now that the elements
1755 * have been created.
1760 n
= find_account(client
->doc
, &src
, &error
, NULL
, 0);
1765 n
= find_elements(client
->doc
, n
->children
, src
+1, &error
,
1766 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1772 line
= g_strjoinv("\t", dst
);
1773 error
= add_attribute(n
, "target", line
);
1779 g_strfreev(src_orig
);
1784 * req[0] - account name
1787 static gpg_error_t
name_attribute(struct client_s
*client
, gchar
**req
)
1793 tmp
= g_strdupv(req
);
1796 return gpg_error_from_errno(ENOMEM
);
1798 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
1804 if (g_utf8_collate(req
[0], req
[1]) == 0)
1808 * Will not overwrite an existing account.
1810 tmp
= g_strdupv(req
+1);
1813 return gpg_error_from_errno(ENOMEM
);
1815 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
1818 if (error
&& error
!= EPWMD_ELEMENT_NOT_FOUND
)
1822 return EPWMD_ACCOUNT_EXISTS
;
1825 * Whitespace not allowed in account names.
1827 if (contains_whitespace(req
[1]) == TRUE
)
1828 return EPWMD_ATTR_SYNTAX
;
1830 tmp
= g_strdupv(req
);
1833 return gpg_error_from_errno(ENOMEM
);
1835 n
= find_account(client
->doc
, &tmp
, &error
, NULL
, 0);
1839 return EPWMD_ELEMENT_NOT_FOUND
;
1841 return add_attribute(n
, "name", req
[1]);
1845 * req[0] - attribute
1846 * req[1] - element path
1848 static int attribute_get(assuan_context_t ctx
, gchar
**req
)
1850 struct client_s
*client
= assuan_get_pointer(ctx
);
1856 if (!req
|| !req
[0] || !req
[1])
1857 return EPWMD_COMMAND_SYNTAX
;
1859 if (strchr(req
[1], '\t')) {
1860 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
)
1861 return EPWMD_COMMAND_SYNTAX
;
1864 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1865 return EPWMD_COMMAND_SYNTAX
;
1868 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1874 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1875 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1883 if ((a
= xmlGetProp(n
, (xmlChar
*)req
[0])) == NULL
)
1884 return EPWMD_ATTR_NOT_FOUND
;
1886 error
= assuan_send_data(ctx
, a
, xmlStrlen(a
));
1896 * req[0] - attribute
1897 * req[1] - element path
1900 static int attribute_set(struct client_s
*client
, gchar
**req
)
1902 gchar
**path
= NULL
;
1906 if (!req
|| !req
[0] || !req
[1] || !req
[2])
1907 return EPWMD_COMMAND_SYNTAX
;
1910 * Reserved attribute names.
1912 if (g_utf8_collate(req
[0], "name") == 0) {
1914 * Only reserved for the account element. Not the rest of the
1917 if (strchr(req
[1], '\t') == NULL
)
1918 return name_attribute(client
, req
+ 1);
1920 else if (g_utf8_collate(req
[0], "target") == 0)
1921 return target_attribute(client
, req
+ 1);
1923 if ((path
= split_input_line(req
[1], "\t", 0)) == NULL
) {
1925 * The first argument may be only an account.
1927 if ((path
= split_input_line(req
[1], " ", 0)) == NULL
)
1928 return EPWMD_COMMAND_SYNTAX
;
1931 n
= find_account(client
->doc
, &path
, &error
, NULL
, 0);
1937 n
= find_elements(client
->doc
, n
->children
, path
+1, &error
,
1938 NULL
, NULL
, NULL
, FALSE
, 0, NULL
);
1945 return add_attribute(n
, req
[0], req
[2]);
1954 * req[1] - attribute name or element path if command is LIST
1955 * req[2] - element path
1956 * req[2] - element path or value
1958 static int attr_command(assuan_context_t ctx
, char *line
)
1960 struct client_s
*client
= assuan_get_pointer(ctx
);
1961 gchar
**req
= split_input_line(line
, " ", 4);
1962 gpg_error_t error
= 0;
1964 error
= file_modified(client
);
1967 log_write("%s: %s", client
->filename
? client
->filename
: "",
1968 pwmd_strerror(error
));
1970 return send_error(ctx
, error
);
1973 if (!req
|| !req
[0] || !req
[1]) {
1975 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
1978 if (g_ascii_strcasecmp(req
[0], "SET") == 0)
1979 error
= attribute_set(client
, req
+1);
1980 else if (g_ascii_strcasecmp(req
[0], "GET") == 0)
1981 error
= attribute_get(ctx
, req
+1);
1982 else if (g_ascii_strcasecmp(req
[0], "DELETE") == 0)
1983 error
= attribute_delete(client
, req
+1);
1984 else if (g_ascii_strcasecmp(req
[0], "LIST") == 0)
1985 error
= attribute_list(ctx
, req
+1);
1987 error
= EPWMD_COMMAND_SYNTAX
;
1990 return send_error(ctx
, error
);
1993 static int iscached_command(assuan_context_t ctx
, char *line
)
1995 gchar
**req
= split_input_line(line
, " ", 0);
1998 if (!req
|| !*req
) {
2000 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2003 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2007 if (cache_iscached(md5file
) == FALSE
) {
2009 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2013 return send_error(ctx
, 0);
2016 gpg_error_t
send_cache_status(assuan_context_t ctx
)
2019 struct client_s
*client
= assuan_get_pointer(ctx
);
2020 gchar buf
[ASSUAN_LINELENGTH
];
2022 CACHE_LOCK(client
->ctx
);
2023 tmp
= print_fmt(buf
, sizeof(buf
), "%i %i",
2025 (cache_size
/ sizeof(file_cache_t
)) - cache_file_count());
2028 return assuan_write_status(ctx
, "CACHE", buf
);
2031 static int clearcache_command(assuan_context_t ctx
, char *line
)
2033 struct client_s
*client
= assuan_get_pointer(ctx
);
2034 gchar
**req
= split_input_line(line
, " ", 0);
2039 if (!req
|| !*req
) {
2041 cache_clear(client
->md5file
, 2);
2043 return send_error(ctx
, 0);
2046 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[0], strlen(req
[0]));
2049 if (cache_clear(md5file
, 1) == FALSE
) {
2051 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2055 return send_error(ctx
, 0);
2058 static int cachetimeout_command(assuan_context_t ctx
, char *line
)
2062 gchar
**req
= split_input_line(line
, " ", 0);
2064 struct client_s
*client
= assuan_get_pointer(ctx
);
2066 if (!req
|| !*req
|| !req
[1]) {
2068 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2072 timeout
= strtol(req
[0], &p
, 10);
2074 if (errno
!= 0 || *p
!= 0) {
2076 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2079 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, req
[1], strlen(req
[1]));
2081 CACHE_LOCK(client
->ctx
);
2083 if (cache_set_timeout(md5file
, timeout
) == FALSE
) {
2085 return send_error(ctx
, EPWMD_CACHE_NOT_FOUND
);
2089 return send_error(ctx
, 0);
2092 static int dump_command(assuan_context_t ctx
, char *line
)
2096 struct client_s
*client
= assuan_get_pointer(ctx
);
2099 if (disable_list_and_dump
== TRUE
)
2100 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2102 error
= file_modified(client
);
2105 log_write("%s: %s", client
->filename
? client
->filename
: "",
2106 pwmd_strerror(error
));
2107 return send_error(ctx
, error
);
2110 xmlDocDumpFormatMemory(client
->doc
, &xml
, &len
, 1);
2111 error
= assuan_send_data(ctx
, xml
, len
);
2113 return send_error(ctx
, error
);
2116 static int getconfig_command(assuan_context_t ctx
, gchar
*line
)
2118 struct client_s
*client
= assuan_get_pointer(ctx
);
2119 gpg_error_t error
= 0;
2122 if (strcmp(line
, "key") == 0 || strcmp(line
, "key_file") == 0)
2123 return send_error(ctx
, GPG_ERR_NOT_IMPLEMENTED
);
2125 p
= get_key_file_string(client
->filename
? client
->filename
: "global", line
);
2128 return send_error(ctx
, EPWMD_COMMAND_SYNTAX
);
2130 tmp
= expand_homedir(p
);
2133 error
= assuan_send_data(ctx
, p
, strlen(p
));
2135 return send_error(ctx
, error
);
2138 void cleanup_assuan(assuan_context_t ctx
)
2140 struct client_s
*cl
= assuan_get_pointer(ctx
);
2146 static void reset_notify(assuan_context_t ctx
)
2148 struct client_s
*cl
= assuan_get_pointer(ctx
);
2154 static gpg_error_t
parse_client_option(assuan_context_t ctx
, const gchar
*line
)
2156 gchar name
[32] = {0}, value
[256] = {0};
2158 if (sscanf(line
, " %31[a-zA-Z] = %255c", name
, value
) != 2)
2159 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_SYNTAX
);
2161 if (g_strcasecmp(name
, (gchar
*)"NAME") == 0) {
2162 pth_attr_t attr
= pth_attr_of(pth_self());
2164 log_write("OPTION CLIENT %s", line
);
2165 pth_attr_set(attr
, PTH_ATTR_NAME
, value
);
2166 pth_attr_destroy(attr
);
2169 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2174 static int option_handler(assuan_context_t ctx
, const gchar
*name
,
2177 #ifdef WITH_PINENTRY
2178 struct client_s
*client
= assuan_get_pointer(ctx
);
2181 if (!value
|| !*value
)
2182 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2184 if (g_strcasecmp(name
, (gchar
*)"client") == 0)
2185 return parse_client_option(ctx
, value
);
2187 #ifdef WITH_PINENTRY
2188 if (g_strcasecmp(name
, (gchar
*)"ttyname") == 0) {
2189 g_free(client
->pinentry
->ttyname
);
2190 client
->pinentry
->ttyname
= g_strdup(value
);
2192 else if (g_strcasecmp(name
, (gchar
*)"ttytype") == 0) {
2193 g_free(client
->pinentry
->ttytype
);
2194 client
->pinentry
->ttytype
= g_strdup(value
);
2196 else if (g_strcasecmp(name
, (gchar
*)"display") == 0) {
2197 g_free(client
->pinentry
->display
);
2198 client
->pinentry
->display
= g_strdup(value
);
2200 else if (g_strcasecmp(name
, (gchar
*)"path") == 0) {
2201 g_free(client
->pinentry
->path
);
2202 client
->pinentry
->path
= g_strdup(value
);
2204 else if (g_strcasecmp(name
, (gchar
*)"title") == 0) {
2205 g_free(client
->pinentry
->title
);
2206 client
->pinentry
->title
= g_strdup(value
);
2208 else if (g_strcasecmp(name
, (gchar
*)"prompt") == 0) {
2209 g_free(client
->pinentry
->prompt
);
2210 client
->pinentry
->prompt
= g_strdup(value
);
2212 else if (g_strcasecmp(name
, (gchar
*)"desc") == 0) {
2213 g_free(client
->pinentry
->desc
);
2214 client
->pinentry
->desc
= g_strdup(value
);
2217 /* Need to wait for pinentry to support a --timeout option so it can
2218 * terminate itself. Cannot do it here because assuan_pipe_connect() calls
2219 * execv() which replaces the pid of the fork()ed thread from
2220 * pinentry_fork(). So pinentry will become a real process after the
2221 * thread terminates and won't be able to be kill()ed from pwmd.
2223 else if (g_strcasecmp(name
, (gchar
*)"timeout") == 0) {
2225 gint n
= strtol(value
, &p
, 10);
2228 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2230 client
->pinentry
->timeout
= n
;
2233 else if (g_strcasecmp(name
, (gchar
*)"pinentry") == 0) {
2235 gint n
= strtol(value
, &p
, 10);
2237 if (*p
|| n
< 0 || n
> 1)
2238 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_INV_VALUE
);
2240 g_key_file_set_boolean(keyfileh
, client
->filename
? client
->filename
: "global",
2241 "enable_pinentry", n
== 0 ? FALSE
: TRUE
);
2244 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_UNKNOWN_OPTION
);
2246 return gpg_err_make(PWMD_ERR_SOURCE
, GPG_ERR_NOT_IMPLEMENTED
);
2249 log_write("OPTION %s=%s", name
, value
);
2253 gpg_error_t
register_commands(assuan_context_t ctx
)
2257 gint (*handler
)(assuan_context_t
, gchar
*line
);
2259 { "OPEN", open_command
},
2260 { "SAVE", save_command
},
2261 { "LIST", list_command
},
2262 { "REALPATH", realpath_command
},
2263 { "STORE", store_command
},
2264 { "DELETE", delete_command
},
2265 { "GET", get_command
},
2266 { "ATTR", attr_command
},
2267 { "ISCACHED", iscached_command
},
2268 { "CLEARCACHE", clearcache_command
},
2269 { "CACHETIMEOUT", cachetimeout_command
},
2270 { "GETCONFIG", getconfig_command
},
2271 { "DUMP", dump_command
},
2278 for (i
=0; table
[i
].name
; i
++) {
2279 rc
= assuan_register_command (ctx
, table
[i
].name
, table
[i
].handler
);
2285 rc
= assuan_register_bye_notify(ctx
, cleanup_assuan
);
2290 rc
= assuan_register_option_handler(ctx
, option_handler
);
2295 return assuan_register_reset_notify(ctx
, reset_notify
);
2298 gpg_error_t
try_xml_decrypt(assuan_context_t ctx
, gint fd
, struct stat st
,
2304 guchar tkey
[gcrykeysize
];
2305 struct client_s
*client
= ctx
? assuan_get_pointer(ctx
) : NULL
;
2306 gcry_cipher_hd_t gh
;
2307 guint iter
= 0, n_iter
= 0;
2309 void *outbuf
= NULL
;
2313 file_header_t file_header
;
2314 gchar str
[ASSUAN_LINELENGTH
];
2317 error
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0);
2325 lseek(fd
, 0, SEEK_SET
);
2326 insize
= st
.st_size
- sizeof(file_header_t
);
2327 iv
= gcry_malloc(gcryblocksize
);
2331 gcry_cipher_close(gh
);
2333 return gpg_error_from_errno(ENOMEM
);
2336 len
= pth_read(fd
, &file_header
, sizeof(file_header_t
));
2338 if (len
!= sizeof(file_header_t
)) {
2342 gcry_cipher_close(gh
);
2346 return gpg_error_from_errno(errno
);
2349 /* No encryption iterations. This is a plain (gzipped) file. */
2350 if (file_header
.iter
== -1) {
2352 * cache_file_count() needs both .used == TRUE and a valid key in
2353 * order for it to count as a used cache entry. Fixes CACHE status
2356 memset(key
, '!', gcrykeysize
);
2357 insize
= st
.st_size
- sizeof(file_header_t
);
2360 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
2361 inbuf
= gcry_malloc(insize
);
2365 gcry_cipher_close(gh
);
2368 return gpg_error_from_errno(ENOMEM
);
2371 len
= pth_read(fd
, inbuf
, insize
);
2373 if (len
!= insize
) {
2377 gcry_cipher_close(gh
);
2381 return gpg_error_from_errno(errno
);
2384 if (file_header
.iter
== -1)
2387 memcpy(tkey
, key
, sizeof(tkey
));
2390 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2392 gcry_cipher_close(gh
);
2393 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2396 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2403 if ((error
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
2405 gcry_cipher_close(gh
);
2406 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2409 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2415 gcry_cipher_close(gh
);
2420 iter_progress
= get_key_file_integer("global", "iteration_progress");
2422 if (ctx
&& iter_progress
> 0 && file_header
.iter
>= iter_progress
) {
2423 error
= assuan_write_status(client
->ctx
, "DECRYPT", "0");
2432 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2440 if ((error
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
2442 gcry_cipher_close(gh
);
2443 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2446 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2453 while (iter
< file_header
.iter
) {
2454 if (ctx
&& iter_progress
> 0 && iter
>= iter_progress
) {
2455 if (!(iter
% iter_progress
)) {
2456 error
= assuan_write_status(ctx
, "DECRYPT",
2457 print_fmt(str
, sizeof(str
), "%i", ++n_iter
* iter_progress
));
2467 if ((error
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
2469 gcry_cipher_close(gh
);
2470 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2473 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2480 error
= decrypt_xml(gh
, inbuf
, insize
, NULL
, 0);
2484 gcry_cipher_close(gh
);
2485 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2488 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(error
));
2499 if (ctx
&& iter_progress
&& file_header
.iter
>= iter_progress
) {
2500 error
= assuan_write_status(ctx
, "DECRYPT",
2501 print_fmt(str
, sizeof(str
), "%i", file_header
.iter
));
2513 if (do_decompress(ctx
, inbuf
, insize
, &outbuf
, &outsize
, &zerror
) == FALSE
) {
2515 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2517 if (zerror
== Z_MEM_ERROR
) {
2519 return gpg_error_from_errno(ENOMEM
);
2521 else if (zerror
!= Z_DATA_ERROR
) {
2525 gcry_cipher_close(gh
);
2527 return EPWMD_BADKEY
;
2536 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
2540 gcry_cipher_close(gh
);
2542 return EPWMD_BADKEY
;
2546 client
->xml
= inbuf
;
2547 client
->len
= insize
;
2550 gcry_cipher_close(gh
);
2558 * This is called after every Assuan command.
2560 void command_finalize(assuan_context_t ctx
, gint error
)
2562 struct client_s
*client
= assuan_get_pointer(ctx
);
2564 unlock_file_mutex(client
);