Added get_key_file_double(). Use this in places where needed rather than
[pwmd.git] / src / commands.c
blob7ef99575a86156acd34960beb8427390195cba2d
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2009 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 02110-1301 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <gcrypt.h>
30 #include <zlib.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #ifdef WITH_LIBACL
37 #include <sys/acl.h>
38 #endif
40 #include "mem.h"
41 #include "xml.h"
42 #include "common.h"
44 #ifdef WITH_PINENTRY
45 #include "pinentry.h"
46 #endif
48 #include "pwmd_error.h"
49 #include "cache.h"
50 #include "misc.h"
51 #include "commands.h"
52 #include "lock.h"
54 struct gz_s {
55 z_stream z;
56 gpointer out;
57 gboolean done;
58 status_msg_t which;
61 static void *z_alloc(void *data, unsigned items, unsigned size)
63 return gcry_calloc(items, size);
66 static void z_free(void *data, void *p)
68 gcry_free(p);
71 static gpg_error_t file_modified(struct client_s *client)
73 struct stat st;
74 gpg_error_t rc;
76 if (client->state != STATE_OPEN)
77 return EPWMD_NO_FILE;
79 rc = lock_file_mutex(client);
81 if (rc)
82 return rc;
84 if (lstat(client->filename, &st) == 0 && client->mtime) {
85 if (client->mtime != st.st_mtime)
86 return EPWMD_FILE_MODIFIED;
89 pth_cancel_point();
90 return 0;
93 static gpg_error_t parse_xml(assuan_context_t ctx)
95 struct client_s *client = assuan_get_pointer(ctx);
97 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
99 if (!client->doc)
100 return EPWMD_LIBXML_ERROR;
102 return 0;
105 void unlock_file_mutex(struct client_s *client)
107 pth_mutex_t *m;
109 #ifdef WITH_PINENTRY
110 if (client->has_lock == FALSE || client->pinentry->status != PINENTRY_NONE)
111 #else
112 if (client->has_lock == FALSE)
113 #endif
114 return;
116 CACHE_LOCK(client->ctx);
118 if (cache_get_mutex(client->md5file, &m) == FALSE) {
119 CACHE_UNLOCK;
120 return;
123 CACHE_UNLOCK;
124 MUTEX_UNLOCK(m);
125 client->has_lock = client->is_lock_cmd = FALSE;
128 gpg_error_t lock_file_mutex(struct client_s *client)
130 pth_mutex_t *m;
131 gpg_error_t rc = 0;
133 if (client->has_lock == TRUE)
134 return 0;
136 CACHE_LOCK(client->ctx);
138 if (cache_get_mutex(client->md5file, &m) == FALSE) {
139 CACHE_UNLOCK;
140 return 0;
143 CACHE_UNLOCK;
144 MUTEX_TRYLOCK(client->ctx, m, rc);
146 if (!rc)
147 client->has_lock = TRUE;
149 return rc;
152 void free_client(struct client_s *client)
154 if (client->doc)
155 xmlFreeDoc(client->doc);
157 if (client->xml)
158 gcry_free(client->xml);
160 if (client->filename)
161 g_free(client->filename);
163 if (client->crypto)
164 cleanup_crypto(&client->crypto);
166 if (client->xml_error)
167 xmlResetError(client->xml_error);
170 void cleanup_client(struct client_s *client)
172 assuan_context_t ctx = client->ctx;
173 struct client_thread_s *thd = client->thd;
174 gboolean has_lock = client->has_lock;
175 #ifdef WITH_PINENTRY
176 struct pinentry_s *pin = client->pinentry;
177 #endif
179 unlock_file_mutex(client);
180 CACHE_LOCK(client->ctx);
181 cache_decr_refcount(client->md5file);
184 * This may be a new file so don't use a cache slot. save_command() will
185 * set this to FALSE on success.
187 if (client->new == TRUE)
188 cache_clear(client->md5file, 1);
190 CACHE_UNLOCK;
191 free_client(client);
192 memset(client, 0, sizeof(struct client_s));
193 client->state = STATE_CONNECTED;
194 client->ctx = ctx;
195 client->thd = thd;
196 client->freed = TRUE;
197 #ifdef WITH_PINENTRY
198 client->pinentry = pin;
199 #endif
200 client->has_lock = has_lock;
203 static void gz_cleanup(void *arg)
205 struct gz_s **gz = (struct gz_s **)arg;
207 if (!gz)
208 return;
210 if (!(*gz)->done && (*gz)->out)
211 gcry_free((*gz)->out);
213 if ((*gz)->which == STATUS_COMPRESS) {
214 if ((*gz)->z.zalloc)
215 deflateEnd(&(*gz)->z);
217 else {
218 if ((*gz)->z.zalloc)
219 inflateEnd(&(*gz)->z);
222 g_free(*gz);
223 *gz = NULL;
226 gboolean do_decompress(assuan_context_t ctx, gpointer in, gulong insize,
227 gpointer *out, gulong *outsize, gint *rc)
229 struct gz_s *gz;
230 gz_header h;
231 gchar buf[17];
233 gz = g_malloc0(sizeof(struct gz_s));
235 if (!gz) {
236 *rc = gpg_error_from_errno(ENOMEM);
237 return FALSE;
240 pth_cleanup_push(gz_cleanup, &gz);
241 gz->which = STATUS_DECOMPRESS;
242 gz->z.zalloc = z_alloc;
243 gz->z.zfree = z_free;
244 gz->z.next_in = in;
245 gz->z.avail_in = (uInt)insize;
246 gz->z.avail_out = zlib_bufsize;
247 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
249 if (!gz->out) {
250 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
251 *rc = Z_MEM_ERROR;
252 pth_cleanup_pop(1);
253 return FALSE;
256 *rc = inflateInit2(&gz->z, 47);
258 if (*rc != Z_OK) {
259 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
260 pth_cleanup_pop(1);
261 return FALSE;
264 memset(&h, 0, sizeof(gz_header));
265 h.comment = (guchar *)buf;
266 h.comm_max = sizeof(buf);
267 *rc = inflateGetHeader(&gz->z, &h);
269 if (*rc != Z_OK) {
270 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
271 pth_cleanup_pop(1);
272 return FALSE;
275 *rc = inflate(&gz->z, Z_BLOCK);
277 if (*rc != Z_OK) {
278 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
279 pth_cleanup_pop(1);
280 return FALSE;
283 if (h.comment)
284 insize = (gulong)strtol((gchar *)h.comment, NULL, 10);
286 do {
287 gpointer p;
289 *rc = inflate(&gz->z, Z_FINISH);
291 switch (*rc) {
292 case Z_OK:
293 break;
294 case Z_BUF_ERROR:
295 if (!gz->z.avail_out) {
296 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
298 if (!p) {
299 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
300 *rc = Z_MEM_ERROR;
301 goto fail;
304 gz->out = p;
305 gz->z.next_out = (guchar *)gz->out + gz->z.total_out;
306 gz->z.avail_out = zlib_bufsize;
307 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li",
308 gz->z.total_out, insize);
310 if (*rc)
311 goto fail;
313 break;
314 case Z_STREAM_END:
315 break;
316 default:
317 goto fail;
318 break;
320 } while (*rc != Z_STREAM_END);
322 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li", gz->z.total_out,
323 insize);
325 if (*rc)
326 goto fail;
328 *out = gz->out;
329 *outsize = gz->z.total_out;
330 gz->done = TRUE;
331 pth_cleanup_pop(1);
332 *rc = 0;
333 return TRUE;
335 fail:
336 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
337 pth_cleanup_pop(1);
338 return FALSE;
341 file_header_internal_t *read_file_header(const gchar *filename, gboolean v1,
342 gpg_error_t *rc)
344 gint fd;
345 gsize len;
346 file_header_internal_t *fh = g_malloc0(sizeof(file_header_internal_t));
347 gsize fh_size;
348 gpointer p;
350 *rc = 0;
352 if (!fh) {
353 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
354 *rc = gpg_error_from_errno(ENOMEM);
355 return NULL;
358 pth_cleanup_push(g_free, fh);
359 fh_size = v1 ? sizeof(fh->ver.fh1) : sizeof(fh->ver.fh2);
361 if (lstat(filename, &fh->st) == -1) {
362 *rc = gpg_error_from_syserror();
363 pth_cleanup_pop(1);
364 return NULL;
367 if (!S_ISREG(fh->st.st_mode)) {
368 *rc = GPG_ERR_ENOANO;
369 pth_cleanup_pop(1);
370 return NULL;
373 fd = open(filename, O_RDONLY);
375 if (fd == -1) {
376 *rc = gpg_error_from_errno(errno);
377 pth_cleanup_pop(1);
378 return NULL;
381 pth_cleanup_push(cleanup_fd_cb, &fd);
382 p = v1 ? (void *)&fh->ver.fh1 : (void *)&fh->ver.fh2;
383 len = pth_read(fd, p, fh_size);
385 if (len != fh_size) {
386 gint n = errno;
387 pth_cleanup_pop(1);
388 pth_cleanup_pop(1);
389 *rc = gpg_error_from_errno(n);
390 return NULL;
393 fh->v1 = v1;
394 fh->fd = fd;
395 pth_cleanup_pop(0);
396 pth_cleanup_pop(0);
397 return fh;
400 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar *key,
401 gboolean cached)
403 struct client_s *client = assuan_get_pointer(ctx);
404 gpg_error_t rc;
405 gint timeout;
407 /* New file. */
408 if (!client->crypto->fh) {
409 if (key[0])
410 goto update_cache;
412 goto done;
415 rc = init_client_crypto2(client->filename, client->crypto);
417 if (rc) {
418 cleanup_client(client);
419 return send_error(ctx, rc);
422 rc = try_xml_decrypt(ctx, key, client->crypto, NULL, NULL);
424 if (rc) {
425 cleanup_client(client);
426 return send_error(ctx, rc);
429 update_cache:
430 CACHE_LOCK(client->ctx);
432 if (cached == FALSE) {
433 if (cache_update_key(client->md5file, key) == FALSE) {
434 cleanup_client(client);
435 CACHE_UNLOCK;
436 return send_syserror(ctx, ENOMEM);
439 timeout = get_key_file_integer(client->filename, "cache_timeout");
440 cache_reset_timeout(client->md5file, timeout);
442 else
443 cache_set_timeout(client->md5file, -2);
445 CACHE_UNLOCK;
447 done:
448 rc = parse_xml(ctx);
450 if (client->xml) {
451 gcry_free(client->xml);
452 client->xml = NULL;
455 if (!rc) {
456 if (client->new == FALSE)
457 send_status_all(STATUS_CACHE);
459 client->state = STATE_OPEN;
462 if (!rc && client->new == FALSE &&
463 client->crypto->fh->ver.fh2.iter != (guint64)get_key_file_double(client->filename, "iterations")) {
464 MUTEX_LOCK(&rcfile_mutex);
465 g_key_file_set_double(keyfileh, client->filename, "iterations",
466 client->crypto->fh->ver.fh2.iter);
467 MUTEX_UNLOCK(&rcfile_mutex);
468 send_status_all(STATUS_CONFIG);
471 cleanup_crypto(&client->crypto);
472 return send_error(ctx, rc);
475 static void req_cleanup(void *arg)
477 if (!arg)
478 return;
480 g_strfreev((gchar **)arg);
483 static int open_command(assuan_context_t ctx, char *line)
485 gboolean cached = FALSE;
486 gpg_error_t rc;
487 struct client_s *client = assuan_get_pointer(ctx);
488 gchar **req;
489 gchar *filename = NULL;
490 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
492 if ((req = split_input_line(line, " ", 2)) != NULL)
493 filename = req[0];
495 pth_cleanup_push(req_cleanup, req);
497 if (!filename || !*filename) {
498 pth_cleanup_pop(1);
499 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
502 log_write2("ARGS=\"%s\" %s", filename, req[1] ? "<passphrase>" : "");
504 if (valid_filename(filename) == FALSE) {
505 pth_cleanup_pop(1);
506 return send_error(ctx, EPWMD_INVALID_FILENAME);
509 if (client->state == STATE_OPEN)
510 cleanup_client(client);
512 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
513 CACHE_LOCK(client->ctx);
515 if (cache_has_file(client->md5file) == FALSE) {
516 if (cache_add_file(client->md5file, NULL) == FALSE) {
517 pth_cleanup_pop(1);
518 CACHE_UNLOCK;
519 return send_syserror(ctx, ENOMEM);
523 cache_incr_refcount(client->md5file);
524 CACHE_UNLOCK;
525 rc = lock_file_mutex(client);
527 if (rc) {
528 pth_cleanup_pop(1);
529 return send_error(ctx, rc);
532 client->freed = FALSE;
533 client->crypto = init_client_crypto();
535 if (!client->crypto) {
536 pth_cleanup_pop(1);
537 cleanup_client(client);
538 return send_syserror(ctx, ENOMEM);
541 client->crypto->key = gcry_malloc(hashlen);
543 if (!client->crypto->key) {
544 pth_cleanup_pop(1);
545 log_write("%s(%i): %s", __FUNCTION__, __LINE__,
546 gpg_error_from_errno(ENOMEM));
547 cleanup_client(client);
548 return send_syserror(ctx, ENOMEM);
551 memset(client->crypto->key, 0, hashlen);
552 client->crypto->fh = read_file_header(filename, FALSE, &rc);
554 if (!client->crypto->fh) {
555 if (gpg_err_code_to_errno(rc) != ENOENT) {
556 log_write("%s: %s", filename, pwmd_strerror(rc));
557 pth_cleanup_pop(1);
558 cleanup_client(client);
559 return send_error(ctx, rc);
563 * New files don't need a key.
565 if ((client->xml = new_document()) == NULL) {
566 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
567 pth_cleanup_pop(1);
568 cleanup_client(client);
569 return send_syserror(ctx, ENOMEM);
572 client->len = xmlStrlen(client->xml);
573 client->new = TRUE;
574 client->filename = g_strdup(filename);
576 if (!client->filename) {
577 pth_cleanup_pop(1);
578 cleanup_client(client);
579 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
580 return send_syserror(ctx, ENOMEM);
583 if (req[1] && *req[1])
584 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
585 strlen(req[1]));
587 pth_cleanup_pop(1);
588 #ifdef WITH_PINENTRY
589 client->pinentry->filename = g_strdup(client->filename);
591 if (!client->pinentry->filename) {
592 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
593 cleanup_client(client);
594 return send_syserror(ctx, ENOMEM);
596 #endif
597 return open_command_finalize(ctx, client->crypto->key, cached);
599 else
600 client->mtime = client->crypto->fh->st.st_mtime;
602 client->filename = g_strdup(filename);
604 if (!client->filename) {
605 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
606 pth_cleanup_pop(1);
607 cleanup_client(client);
608 return send_syserror(ctx, ENOMEM);
611 #ifdef WITH_PINENTRY
612 client->pinentry->filename = g_strdup(client->filename);
614 if (!client->pinentry->filename) {
615 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
616 pth_cleanup_pop(1);
617 cleanup_client(client);
618 return send_syserror(ctx, ENOMEM);
620 #endif
622 if (client->crypto->fh->ver.fh2.iter <= 0ULL)
623 goto done;
625 CACHE_LOCK(client->ctx);
626 cached = cache_get_key(client->md5file, client->crypto->key);
627 CACHE_UNLOCK;
629 if (cached == FALSE) {
630 gchar *tmp = get_key_file_string(filename, "key_file");
632 if (tmp) {
633 g_free(tmp);
634 pth_cleanup_pop(1);
635 cleanup_client(client);
636 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
640 * No key specified and no matching filename found in the cache. Use
641 * pinentry to retrieve the key. Cannot return assuan_process_done()
642 * here otherwise the command will be interrupted. The event loop in
643 * client_thread() will poll the file descriptor waiting for it to
644 * become ready to read a pinentry_key_s which will contain the
645 * entered key or an error code. It will then call
646 * open_command_finalize() to to finish the command.
648 if (!req[1] || !*req[1]) {
649 #ifdef WITH_PINENTRY
650 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
652 /* From set_pinentry_defaults(). */
653 if (client->pinentry->enable == FALSE ||
654 (client->pinentry->enable == -1 && b == FALSE)) {
655 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
656 goto done;
659 pth_cleanup_pop(1);
660 rc = lock_pin_mutex(client);
662 if (rc) {
663 unlock_pin_mutex(client->pinentry);
664 cleanup_client(client);
665 return send_error(ctx, rc);
668 client->pinentry->which = PINENTRY_OPEN;
669 rc = pinentry_fork(ctx);
671 if (rc) {
672 unlock_pin_mutex(client->pinentry);
673 cleanup_client(client);
674 return send_error(ctx, rc);
677 // Called from pinentry iterate.
678 client->pinentry->cb = open_command_finalize;
679 client->pinentry->status = PINENTRY_INIT;
680 return 0;
681 #else
682 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
683 goto done;
684 #endif
687 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
688 strlen(req[1]));
690 else if (req && req[1] && *req[1]) {
691 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
692 strlen(req[1]) ? strlen(req[1]) : 1);
695 done:
696 pth_cleanup_pop(1);
697 return open_command_finalize(ctx, client->crypto->key, cached);
700 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
701 gulong size, gpointer *out, gulong *outsize, gint *rc)
703 struct gz_s *gz;
704 gz_header h;
705 gchar buf[17];
706 gint cmd = Z_NO_FLUSH;
708 gz = g_malloc0(sizeof(struct gz_s));
710 if (!gz) {
711 *rc = gpg_error_from_errno(ENOMEM);
712 return FALSE;
715 pth_cleanup_push(gz_cleanup, &gz);
716 gz->which = STATUS_COMPRESS;
717 gz->z.zalloc = z_alloc;
718 gz->z.zfree = z_free;
719 gz->z.next_in = data;
720 gz->z.avail_in = size < zlib_bufsize ? (uInt)size : (uInt)zlib_bufsize;
721 gz->z.avail_out = (uInt)zlib_bufsize;
722 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
724 if (!gz->out) {
725 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
726 *rc = Z_MEM_ERROR;
727 pth_cleanup_pop(1);
728 return FALSE;
731 *rc = deflateInit2(&gz->z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
733 if (*rc != Z_OK) {
734 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
735 pth_cleanup_pop(1);
736 return FALSE;
739 /* Rather than store the size of the uncompressed data in the file header,
740 * store it in the comment field of the gzip header. Don't give anyone too
741 * much information. Not sure why really, but it seems the right way. :)
743 memset(&h, 0, sizeof(gz_header));
744 g_snprintf(buf, sizeof(buf), "%li", size);
745 h.comment = (guchar *)buf;
746 *rc = deflateSetHeader(&gz->z, &h);
748 if (*rc != Z_OK) {
749 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
750 pth_cleanup_pop(1);
751 return FALSE;
754 do {
755 gpointer p;
757 *rc = deflate(&gz->z, cmd);
759 switch (*rc) {
760 case Z_OK:
761 break;
762 case Z_BUF_ERROR:
763 if (!gz->z.avail_out) {
764 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
766 if (!p) {
767 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
768 *rc = Z_MEM_ERROR;
769 goto fail;
772 gz->out = p;
773 gz->z.next_out = (guchar *)gz->out + gz->z.total_out;
774 gz->z.avail_out = zlib_bufsize;
777 if (!gz->z.avail_in && gz->z.total_in < size) {
778 if (gz->z.total_in + zlib_bufsize > size)
779 gz->z.avail_in = size - gz->z.total_in;
780 else
781 gz->z.avail_in = zlib_bufsize;
783 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li",
784 gz->z.total_in, size);
786 if (*rc)
787 goto fail;
790 if (gz->z.total_in >= size)
791 cmd = Z_FINISH;
793 break;
794 case Z_STREAM_END:
795 break;
796 default:
797 goto fail;
799 } while (*rc != Z_STREAM_END);
801 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li", gz->z.total_in, size);
803 if (*rc)
804 goto fail;
806 *out = gz->out;
807 *outsize = gz->z.total_out;
808 *rc = 0;
809 gz->done = TRUE;
810 pth_cleanup_pop(1);
811 return TRUE;
813 fail:
814 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
815 pth_cleanup_pop(1);
816 return FALSE;
819 #define CRYPTO_BLOCKSIZE(c) (c->blocksize * 1024)
821 static gpg_error_t iterate_crypto_once(struct client_s *client,
822 struct client_crypto_s *crypto, status_msg_t which)
824 gpg_error_t rc = 0;
825 goffset len = CRYPTO_BLOCKSIZE(crypto);
826 gpointer p = gcry_malloc(len);
827 goffset total = 0;
828 gpointer inbuf;
830 if (!p)
831 return gpg_err_code_from_errno(ENOMEM);
833 if (crypto->insize < CRYPTO_BLOCKSIZE(crypto))
834 len = crypto->insize;
836 pth_cleanup_push(gcry_free, p);
838 for (;;) {
839 inbuf = (guchar *)crypto->inbuf + total;
840 guchar *tmp;
842 if (len + total > crypto->insize)
843 len = crypto->blocksize;
845 if (which == STATUS_ENCRYPT)
846 rc = gcry_cipher_encrypt(crypto->gh, p, len, inbuf, len);
847 else
848 rc = gcry_cipher_decrypt(crypto->gh, p, len, inbuf, len);
850 if (rc)
851 goto done;
853 tmp = (guchar *)crypto->inbuf + total;
854 memmove(tmp, p, len);
855 total += len;
857 if (total >= crypto->insize)
858 break;
860 pth_cancel_point();
863 done:
864 pth_cleanup_pop(1);
865 return rc;
868 /* The crypto struct must be setup for iterations and .key. */
869 gpg_error_t do_xml_encrypt(struct client_s *client,
870 struct client_crypto_s *crypto, const gchar *filename)
872 goffset len = crypto->insize;
873 gpointer inbuf;
874 gchar *p;
875 gpg_error_t rc;
876 guint64 iter_progress = 0ULL, n_iter = 0ULL, xiter = 0ULL;
877 gchar tmp[FILENAME_MAX];
878 struct stat st;
879 mode_t mode = 0;
880 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
882 if (!crypto->fh->ver.fh2.iter) {
884 * cache_file_count() needs both .used == TRUE and a valid key in
885 * order for it to count as a used cache entry. Fixes CACHE status
886 * messages.
888 memset(crypto->key, '!', hashlen);
889 goto write_file;
893 * Resize the existing xml buffer to the block size required by gcrypt
894 * rather than duplicating it and wasting memory.
896 len = (crypto->insize / crypto->blocksize) * crypto->blocksize;
898 if (crypto->insize % crypto->blocksize)
899 len += crypto->blocksize;
901 inbuf = gcry_realloc(crypto->inbuf, len);
903 if (!inbuf) {
904 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
905 return gpg_error_from_errno(ENOMEM);
908 crypto->inbuf = inbuf;
909 crypto->insize = len;
910 gcry_create_nonce(crypto->fh->ver.fh2.iv, crypto->blocksize);
911 crypto->tkey = gcry_malloc(hashlen);
913 if (!crypto->tkey) {
914 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
915 return gpg_error_from_errno(ENOMEM);
918 memcpy(crypto->tkey, crypto->key, hashlen);
919 guchar *tkey = crypto->tkey;
920 tkey[0] ^= 1;
922 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, crypto->keysize))) {
923 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
924 return rc;
927 iter_progress = (guint64)get_key_file_double(
928 client ? client->filename : "global", "iteration_progress");
930 if (iter_progress && crypto->fh->ver.fh2.iter >= iter_progress) {
931 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
932 "0 %llu", crypto->fh->ver.fh2.iter);
934 if (rc)
935 return rc;
938 while (xiter < crypto->fh->ver.fh2.iter-1) {
939 if (iter_progress > 0ULL && xiter >= iter_progress) {
940 if (!(xiter % iter_progress)) {
941 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
942 "%llu %llu", ++n_iter * iter_progress,
943 crypto->fh->ver.fh2.iter);
945 if (rc)
946 return rc;
950 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->ver.fh2.iv,
951 crypto->blocksize))) {
952 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
953 return rc;
956 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
958 if (rc) {
959 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
960 return rc;
963 xiter++;
966 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->ver.fh2.iv,
967 crypto->blocksize))) {
968 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
969 return rc;
972 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, crypto->keysize))) {
973 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
974 return rc;
977 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
979 if (rc)
980 return rc;
982 if (iter_progress && crypto->fh->ver.fh2.iter >= iter_progress) {
983 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
984 "%llu %llu", crypto->fh->ver.fh2.iter, crypto->fh->ver.fh2.iter);
986 if (rc)
987 return rc;
990 write_file:
991 tmp[0] = 0;
993 if (filename) {
994 if (!client && !g_ascii_strcasecmp(filename, "-")) {
995 crypto->fh->fd = STDOUT_FILENO;
996 goto do_write_file;
999 if (lstat(filename, &st) == 0) {
1000 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
1002 if (!(mode & S_IWUSR))
1003 return gpg_error_from_errno(EACCES);
1005 else if (errno != ENOENT)
1006 return gpg_error_from_errno(errno);
1008 g_snprintf(tmp, sizeof(tmp), "%s.XXXXXX", filename);
1009 crypto->fh->fd = mkstemp(tmp);
1011 if (crypto->fh->fd == -1) {
1012 rc = errno;
1013 p = strrchr(tmp, '/');
1014 p++;
1015 log_write("%s: %s", p, strerror(rc));
1016 return gpg_error_from_errno(rc);
1019 pth_cleanup_push(cleanup_unlink_cb, tmp);
1021 else
1023 * xml_import() or convert_file() from command line.
1025 crypto->fh->fd = STDOUT_FILENO;
1027 do_write_file:
1028 crypto->fh->ver.fh2.magic[0] = '\177';
1029 crypto->fh->ver.fh2.magic[1] = 'P';
1030 crypto->fh->ver.fh2.magic[2] = 'W';
1031 crypto->fh->ver.fh2.magic[3] = 'M';
1032 crypto->fh->ver.fh2.magic[4] = 'D';
1033 crypto->fh->ver.fh2.version = VERSION_HEX;
1034 len = pth_write(crypto->fh->fd, &crypto->fh->ver.fh2, sizeof(crypto->fh->ver.fh2));
1036 if (len != sizeof(crypto->fh->ver.fh2)) {
1037 len = errno;
1039 if (tmp[0])
1040 pth_cleanup_pop(1);
1042 return gpg_error_from_errno(len);
1045 len = pth_write(crypto->fh->fd, crypto->inbuf, crypto->insize);
1047 if (len != crypto->insize) {
1048 len = errno;
1050 if (tmp[0])
1051 pth_cleanup_pop(1);
1053 return gpg_error_from_errno(len);
1056 if (fsync(crypto->fh->fd) == -1) {
1057 len = errno;
1059 if (tmp[0])
1060 pth_cleanup_pop(1);
1062 return gpg_error_from_errno(len);
1065 if (tmp[0]) {
1066 #ifdef WITH_LIBACL
1067 acl_t acl;
1068 #endif
1069 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
1070 gchar tmp2[FILENAME_MAX];
1072 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
1073 #ifdef WITH_LIBACL
1074 acl = acl_get_file(filename, ACL_TYPE_ACCESS);
1076 if (!acl)
1077 log_write("ACL: %s: %s", filename, strerror(errno));
1078 #endif
1080 if (rename(filename, tmp2) == -1) {
1081 len = errno;
1082 pth_cleanup_pop(1);
1083 #ifdef WITH_LIBACL
1084 if (acl)
1085 acl_free(acl);
1086 #endif
1087 return gpg_error_from_errno(len);
1090 #ifdef WITH_LIBACL
1091 else {
1092 acl = acl_get_file(".", ACL_TYPE_DEFAULT);
1094 if (!acl)
1095 log_write("ACL: %s: %s", filename, strerror(errno));
1097 #endif
1099 if (rename(tmp, filename) == -1) {
1100 len = errno;
1101 pth_cleanup_pop(1);
1102 #ifdef WITH_LIBACL
1103 if (acl)
1104 acl_free(acl);
1105 #endif
1106 return gpg_error_from_errno(len);
1109 pth_cleanup_pop(0);
1111 if (mode)
1112 chmod(filename, mode);
1114 #ifdef WITH_LIBACL
1115 if (acl && acl_set_file(filename, ACL_TYPE_ACCESS, acl))
1116 log_write("ACL: %s: %s", filename, strerror(errno));
1118 if (acl)
1119 acl_free(acl);
1120 #endif
1123 if (client && lstat(filename, &st) == 0)
1124 client->mtime = st.st_mtime;
1126 return 0;
1129 gpg_error_t update_save_flags(const gchar *filename,
1130 struct client_crypto_s *crypto)
1132 gpg_error_t rc;
1133 guint64 iter;
1135 /* New file? */
1136 if (!crypto->fh) {
1137 crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1139 if (!crypto->fh)
1140 return GPG_ERR_ENOMEM;
1143 rc = init_client_crypto2(filename, crypto);
1145 if (rc)
1146 return rc;
1148 iter = (guint64)get_key_file_double(filename, "iterations");
1149 crypto->fh->ver.fh2.iter = iter < 0ULL ? 0ULL : iter;
1150 return 0;
1153 static gpg_error_t save_command_finalize(assuan_context_t ctx, guchar *key,
1154 gboolean cached)
1156 struct client_s *client = assuan_get_pointer(ctx);
1157 gpointer xmlbuf;
1158 gulong len, outsize = 0;
1159 guint iter;
1160 gint timeout;
1161 gpointer outbuf;
1162 gint zrc;
1163 gpg_error_t rc;
1165 if (client->crypto->key && client->crypto->key != key)
1166 gcry_free(client->crypto->key);
1168 client->crypto->key = key;
1169 rc = update_timestamp(client->doc);
1171 if (rc)
1172 return send_error(ctx, rc);
1174 xmlDocDumpFormatMemory(client->doc, (xmlChar **)&xmlbuf, (gint *)&len, 0);
1175 pth_cleanup_push(xmlFree, xmlbuf);
1176 iter = (guint)get_key_file_integer(client->filename, "compression_level");
1178 if (iter < 0)
1179 iter = 0;
1181 if (do_compress(ctx, (gint)iter, xmlbuf, len, &outbuf, &outsize, &zrc)
1182 == FALSE) {
1183 pth_cleanup_pop(1);
1184 cleanup_crypto(&client->crypto);
1186 if (zrc == Z_MEM_ERROR)
1187 return send_syserror(ctx, ENOMEM);
1188 else
1189 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1191 else {
1192 pth_cleanup_pop(1);
1193 xmlbuf = outbuf;
1194 len = outsize;
1197 client->crypto->inbuf = xmlbuf;
1198 client->crypto->insize = len;
1199 rc = update_save_flags(client->filename, client->crypto);
1201 if (rc) {
1202 cleanup_crypto(&client->crypto);
1203 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(rc));
1204 return send_error(ctx, rc);
1207 rc = do_xml_encrypt(client, client->crypto, client->filename);
1209 if (rc) {
1210 cleanup_crypto(&client->crypto);
1211 return send_error(ctx, rc);
1214 timeout = get_key_file_integer(client->filename, "cache_timeout");
1215 CACHE_LOCK(client->ctx);
1217 if (cached) {
1218 cache_reset_timeout(client->md5file, timeout);
1219 CACHE_UNLOCK;
1221 if (client->new == TRUE)
1222 send_status_all(STATUS_CACHE);
1224 client->new = FALSE;
1225 cleanup_crypto(&client->crypto);
1226 return send_error(ctx, 0);
1229 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1230 CACHE_UNLOCK;
1231 cleanup_crypto(&client->crypto);
1232 return send_syserror(ctx, ENOMEM);
1235 client->new = FALSE;
1236 cache_reset_timeout(client->md5file, timeout);
1237 CACHE_UNLOCK;
1238 send_status_all(STATUS_CACHE);
1239 cleanup_crypto(&client->crypto);
1240 return send_error(ctx, 0);
1243 static int save_command(assuan_context_t ctx, char *line)
1245 gboolean cached = FALSE;
1246 struct stat st;
1247 struct client_s *client = assuan_get_pointer(ctx);
1248 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
1250 if (line && *line)
1251 log_write2("ARGS=%s", "<passphrase>");
1253 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1254 return send_syserror(ctx, errno);
1256 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1257 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1258 return send_error(ctx, GPG_ERR_ENOANO);
1261 CACHE_LOCK(ctx);
1262 cached = cache_iscached(client->md5file);
1263 CACHE_UNLOCK;
1266 * If a cache entry doesn't exist for this file and the file has a
1267 * "key_file" or "key" parameter, then it's an error. The reason is that
1268 * cache expiration would be useless.
1270 if (cached == FALSE) {
1271 gchar *tmp = get_key_file_string(client->filename, "key_file");
1273 if (tmp) {
1274 g_free(tmp);
1275 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1279 cached = FALSE;
1281 /* New file? */
1282 if (!client->crypto) {
1283 client->crypto = init_client_crypto();
1285 if (!client->crypto) {
1286 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1287 return send_syserror(ctx, ENOMEM);
1291 client->crypto->key = gcry_malloc(hashlen);
1293 if (!client->crypto->key) {
1294 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1295 cleanup_crypto(&client->crypto);
1296 return send_syserror(ctx, ENOMEM);
1299 memset(client->crypto->key, '!', hashlen);
1301 if (get_key_file_double(client->filename, "iterations") <= 0ULL)
1302 goto done;
1304 if (!line || !*line) {
1305 client->crypto->tkey = gcry_malloc(hashlen);
1307 if (!client->crypto->tkey) {
1308 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1309 cleanup_crypto(&client->crypto);
1310 return send_syserror(ctx, ENOMEM);
1313 memset(client->crypto->tkey, '!', hashlen);
1314 CACHE_LOCK(ctx);
1316 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1317 !memcmp(client->crypto->key, client->crypto->tkey, hashlen)) {
1318 CACHE_UNLOCK;
1320 #ifdef WITH_PINENTRY
1321 gpg_error_t rc;
1323 if (client->pinentry->enable == FALSE ||
1324 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1325 /* Empty keys are allowed. */
1326 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1327 goto done;
1330 lock_pin_mutex(client);
1331 client->pinentry->which = PINENTRY_SAVE;
1332 rc = pinentry_fork(ctx);
1334 if (rc) {
1335 unlock_pin_mutex(client->pinentry);
1336 return send_error(ctx, rc);
1339 client->pinentry->cb = save_command_finalize;
1340 client->pinentry->status = PINENTRY_INIT;
1341 return 0;
1342 #else
1343 /* Empty keys are allowed. */
1344 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1345 goto done;
1346 #endif
1348 else {
1349 CACHE_UNLOCK;
1350 cached = TRUE;
1353 else
1354 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, line,
1355 strlen(line));
1357 done:
1358 return save_command_finalize(ctx, client->crypto->key, cached);
1361 static int delete_command(assuan_context_t ctx, char *line)
1363 struct client_s *client = assuan_get_pointer(ctx);
1364 gchar **req;
1365 gpg_error_t rc;
1366 xmlNodePtr n;
1368 log_write2("ARGS=\"%s\"", line);
1370 if (strchr(line, '\t'))
1371 req = split_input_line(line, "\t", -1);
1372 else
1373 req = split_input_line(line, " ", -1);
1375 if (!req || !*req)
1376 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1378 n = find_account(client->doc, &req, &rc, NULL, 0);
1380 if (!n) {
1381 g_strfreev(req);
1382 return send_error(ctx, rc);
1386 * No sub-node defined. Remove the entire node (account).
1388 if (!req[1]) {
1389 if (n) {
1390 xmlUnlinkNode(n);
1391 xmlFreeNode(n);
1394 g_strfreev(req);
1395 return send_error(ctx, 0);
1398 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1399 g_strfreev(req);
1401 if (!n)
1402 return send_error(ctx, rc);
1404 if (n) {
1405 xmlUnlinkNode(n);
1406 xmlFreeNode(n);
1409 return send_error(ctx, 0);
1413 * Don't return with assuan_process_done() here. This has been called from
1414 * assuan_process_next() and the command should be finished in
1415 * client_thread().
1417 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1418 gsize len)
1420 assuan_context_t ctx = data;
1421 struct client_s *client = assuan_get_pointer(ctx);
1422 gchar **req;
1423 xmlNodePtr n;
1424 gpg_error_t rc = file_modified(client);
1426 if (assuan_rc || rc) {
1427 if (line)
1428 xfree(line);
1429 return assuan_rc ? assuan_rc : rc;
1432 req = split_input_line((gchar *)line, "\t", 0);
1433 xfree(line);
1435 if (!req || !*req)
1436 return EPWMD_COMMAND_SYNTAX;
1438 again:
1439 n = find_account(client->doc, &req, &rc, NULL, 0);
1441 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1442 rc = new_account(client->doc, *req);
1444 if (rc) {
1445 g_strfreev(req);
1446 return rc;
1449 goto again;
1452 if (!n) {
1453 g_strfreev(req);
1454 return rc;
1457 if (req[1]) {
1458 if (!n->children)
1459 create_elements_cb(n, req+1, &rc, NULL);
1460 else
1461 find_elements(client->doc, n->children, req+1, &rc,
1462 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1465 g_strfreev(req);
1466 client->inquire_status = INQUIRE_DONE;
1467 return rc;
1470 static int store_command(assuan_context_t ctx, char *line)
1472 struct client_s *client = assuan_get_pointer(ctx);
1473 gpg_error_t rc;
1475 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1477 if (rc)
1478 return send_error(ctx, rc);
1480 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1481 client->inquire_status = INQUIRE_BUSY;
1482 return 0;
1485 static void *send_data_cb(void *arg)
1487 struct assuan_cmd_s *data = arg;
1488 gint old;
1489 gpg_error_t rc;
1491 pth_cancel_state(PTH_CANCEL_ENABLE|PTH_CANCEL_ASYNCHRONOUS, &old);
1492 rc = assuan_send_data(data->ctx, data->line, data->line_len);
1493 pth_cancel_state(old, NULL);
1494 pth_exit((void *)rc);
1495 return NULL;
1498 /* For every assuan command that needs to be sent to the client, a timeout is
1499 * needed to determine if the client lost the connection. The timeout is the
1500 * same as the "keepalive" configuration parameter or a default if unset.
1502 gpg_error_t do_assuan_command(assuan_context_t ctx,
1503 void *(*cb)(void *data), void *data)
1505 pth_attr_t attr = pth_attr_new();
1506 pth_t tid;
1507 gint n;
1508 gint to = get_key_file_integer("global", "keepalive");
1509 pth_event_t ev, tev;
1510 pth_status_t st;
1511 gpg_error_t rc;
1513 pth_attr_init(attr);
1514 pth_attr_set(attr, PTH_ATTR_JOINABLE, TRUE);
1515 tid = pth_spawn(attr, cb, data);
1516 n = errno;
1517 pth_attr_destroy(attr);
1519 if (!tid) {
1520 log_write("%s(%i): pth_spawn(): %s", __FILE__, __LINE__,
1521 _gpg_strerror(gpg_error_from_errno(n)));
1522 return gpg_error_from_errno(n);
1525 pth_cleanup_push(cleanup_cancel_cb, tid);
1526 to = to <= 0 ? DEFAULT_KEEPALIVE_TO : to;
1527 ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD, tid);
1528 tev = pth_event(PTH_EVENT_TIME, pth_timeout(to, 0));
1529 ev = pth_event_concat(ev, tev, NULL);
1530 pth_cleanup_push(cleanup_ev_cb, ev);
1531 pth_yield(tid);
1532 pth_wait(ev);
1533 st = pth_event_status(ev);
1535 if (st == PTH_STATUS_FAILED) {
1536 pth_cancel(tid);
1537 rc = GPG_ERR_ASS_WRITE_ERROR;
1539 else if (st == PTH_STATUS_OCCURRED)
1540 pth_join(tid, (void **)&rc);
1541 else {
1542 st = pth_event_status(tev);
1544 if (st == PTH_STATUS_OCCURRED) {
1545 pth_cancel(tid);
1546 rc = GPG_ERR_ASS_WRITE_ERROR;
1550 pth_cleanup_pop(1);
1551 pth_cleanup_pop(0);
1552 return rc;
1555 static gpg_error_t xfer_data(assuan_context_t ctx, const gchar *line,
1556 gint total)
1558 int to_send;
1559 int sent = 0;
1560 gpg_error_t rc;
1561 struct assuan_cmd_s data;
1562 int progress = get_key_file_integer("global", "xfer_progress");
1563 int flush = 0;
1565 progress = progress>0 ? (progress/ASSUAN_LINELENGTH)*ASSUAN_LINELENGTH : 0;
1566 to_send = total < ASSUAN_LINELENGTH ? total : ASSUAN_LINELENGTH;
1567 data.ctx = ctx;
1568 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1570 if (rc)
1571 return rc;
1573 again:
1574 do {
1575 if (sent + to_send > total)
1576 to_send = total - sent;
1578 data.line = flush ? NULL : (gchar *)line+sent;
1579 data.line_len = flush ? 0 : to_send;
1580 rc = do_assuan_command(ctx, send_data_cb, &data);
1582 if (!rc) {
1583 sent += flush ? 0 : to_send;
1585 if ((progress && !(sent % progress) && sent != total) ||
1586 (sent == total && flush))
1587 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1589 if (!flush && !rc && sent == total) {
1590 flush = 1;
1591 goto again;
1594 } while (!rc && sent < total);
1596 return rc;
1599 static int get_command(assuan_context_t ctx, char *line)
1601 struct client_s *client = assuan_get_pointer(ctx);
1602 gchar **req;
1603 gpg_error_t rc;
1604 xmlNodePtr n;
1606 log_write2("ARGS=\"%s\"", line);
1607 req = split_input_line(line, "\t", -1);
1609 if (!req || !*req) {
1610 g_strfreev(req);
1611 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1614 n = find_account(client->doc, &req, &rc, NULL, 0);
1616 if (!n) {
1617 g_strfreev(req);
1618 return send_error(ctx, rc);
1621 if (req[1])
1622 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1624 g_strfreev(req);
1626 if (rc)
1627 return send_error(ctx, rc);
1629 if (!n || !n->children)
1630 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1632 n = find_text_node(n->children);
1634 if (!n || !n->content || !*n->content)
1635 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1637 rc = xfer_data(ctx, (gchar *)n->content, xmlStrlen(n->content));
1638 return send_error(ctx, rc);
1641 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1642 gpg_error_t *rc, gchar **req_orig, void *data)
1644 gchar *path = *(gchar **)data;
1645 gchar *tmp = NULL, *result;
1647 if (path) {
1648 g_free(path);
1649 *(gchar **)data = NULL;
1652 path = g_strjoinv("\t", target);
1654 if (!path) {
1655 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1656 *rc = gpg_error_from_errno(ENOMEM);
1657 return NULL;
1660 if (req_orig) {
1661 tmp = g_strjoinv("\t", req_orig);
1663 if (!tmp) {
1664 g_free(path);
1665 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1666 *rc = gpg_error_from_errno(ENOMEM);
1667 return NULL;
1671 if (tmp && *tmp)
1672 result = g_strdup_printf("%s\t%s", path, tmp);
1673 else
1674 result = g_strdup(path);
1676 if (!result) {
1677 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1678 *rc = gpg_error_from_errno(ENOMEM);
1679 g_free(path);
1680 g_free(tmp);
1681 return NULL;
1684 g_free(path);
1685 g_free(tmp);
1686 *(gchar **)data = result;
1687 return node;
1690 static void list_command_cleanup1(void *arg);
1691 static int realpath_command(assuan_context_t ctx, char *line)
1693 gpg_error_t rc;
1694 struct client_s *client = assuan_get_pointer(ctx);
1695 gchar **req;
1696 gchar *t;
1697 gint i;
1698 xmlNodePtr n;
1699 GString *string;
1700 gchar *rp = NULL;
1702 log_write2("ARGS=\"%s\"", line);
1704 if (strchr(line, '\t') != NULL) {
1705 if ((req = split_input_line(line, "\t", 0)) == NULL)
1706 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1708 else {
1709 if ((req = split_input_line(line, " ", 0)) == NULL)
1710 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1713 n = find_account(client->doc, &req, &rc, NULL, 0);
1715 if (!n) {
1716 g_strfreev(req);
1717 return send_error(ctx, rc);
1720 rp = g_strjoinv("\t", req);
1722 if (!rp) {
1723 g_strfreev(req);
1724 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1725 return send_syserror(ctx, ENOMEM);
1728 if (req[1]) {
1729 n = find_elements(client->doc, n->children, req+1, &rc,
1730 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1732 if (!n) {
1733 g_free(rp);
1734 g_strfreev(req);
1735 return send_error(ctx, rc);
1739 string = g_string_new(rp);
1740 g_free(rp);
1741 g_strfreev(req);
1743 if (!string) {
1744 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1745 return send_syserror(ctx, ENOMEM);
1748 again:
1749 for (i = 0, t = string->str + i; *t; t++, i++) {
1750 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1751 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1752 goto again;
1756 pth_cleanup_push(list_command_cleanup1, string);
1757 rc = xfer_data(ctx, string->str, string->len);
1758 pth_cleanup_pop(1);
1759 return send_error(ctx, rc);
1762 static void list_command_cleanup1(void *arg)
1764 g_string_free((GString *)arg, TRUE);
1767 static void list_command_cleanup2(void *arg)
1769 struct element_list_s *elements = arg;
1771 if (elements) {
1772 gint total = g_slist_length(elements->list);
1773 gint i;
1775 for (i = 0; i < total; i++) {
1776 gchar *tmp = g_slist_nth_data(elements->list, i);
1777 g_free(tmp);
1780 g_slist_free(elements->list);
1782 if (elements->prefix)
1783 g_free(elements->prefix);
1785 g_free(elements);
1789 static int list_command(assuan_context_t ctx, char *line)
1791 struct client_s *client = assuan_get_pointer(ctx);
1792 gpg_error_t rc;
1793 struct element_list_s *elements = NULL;
1794 gchar *tmp;
1796 if (disable_list_and_dump == TRUE)
1797 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1799 log_write2("ARGS=\"%s\"", line);
1801 if (!*line) {
1802 GString *str;
1804 rc = list_accounts(client->doc, &str);
1806 if (rc)
1807 return send_error(ctx, rc);
1809 pth_cleanup_push(list_command_cleanup1, str);
1810 rc = xfer_data(ctx, str->str, str->len);
1811 pth_cleanup_pop(1);
1812 return send_error(ctx, rc);
1815 elements = g_malloc0(sizeof(struct element_list_s));
1817 if (!elements) {
1818 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1819 rc = gpg_err_code_from_errno(ENOMEM);
1820 goto fail;
1823 pth_cleanup_push(list_command_cleanup2, elements);
1824 rc = create_path_list(client->doc, elements, line);
1826 if (rc)
1827 goto fail;
1829 if (elements) {
1830 gint total = g_slist_length(elements->list);
1831 gint i;
1832 GString *str;
1834 if (!total) {
1835 rc = EPWMD_EMPTY_ELEMENT;
1836 goto fail;
1839 str = g_string_new(NULL);
1841 if (!str) {
1842 rc = gpg_err_code_from_errno(ENOMEM);
1843 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1844 goto fail;
1847 for (i = 0; i < total; i++) {
1848 tmp = g_slist_nth_data(elements->list, i);
1849 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1852 pth_cleanup_push(list_command_cleanup1, str);
1853 rc = xfer_data(ctx, str->str, str->len);
1854 pth_cleanup_pop(1);
1856 else
1857 rc = EPWMD_EMPTY_ELEMENT;
1859 fail:
1860 pth_cleanup_pop(1);
1861 return send_error(ctx, rc);
1865 * req[0] - element path
1867 static int attribute_list(assuan_context_t ctx, gchar **req)
1869 struct client_s *client = assuan_get_pointer(ctx);
1870 gchar **attrlist = NULL;
1871 gint i = 0;
1872 gchar **path = NULL;
1873 xmlAttrPtr a;
1874 xmlNodePtr n, an;
1875 gchar *line;
1876 gpg_error_t rc;
1878 if (!req || !req[0])
1879 return EPWMD_COMMAND_SYNTAX;
1881 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1883 * The first argument may be only an account.
1885 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1886 return EPWMD_COMMAND_SYNTAX;
1889 n = find_account(client->doc, &path, &rc, NULL, 0);
1891 if (!n) {
1892 g_strfreev(path);
1893 return rc;
1896 if (path[1]) {
1897 n = find_elements(client->doc, n->children, path+1, &rc,
1898 NULL, NULL, NULL, FALSE, 0, NULL);
1900 if (!n) {
1901 g_strfreev(path);
1902 return rc;
1906 g_strfreev(path);
1908 for (a = n->properties; a; a = a->next) {
1909 gchar **pa;
1911 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1912 if (attrlist)
1913 g_strfreev(attrlist);
1915 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1916 return gpg_error_from_errno(ENOMEM);
1919 attrlist = pa;
1920 an = a->children;
1921 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
1923 if (!attrlist[i]) {
1924 g_strfreev(attrlist);
1925 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1926 return gpg_error_from_errno(ENOMEM);
1929 attrlist[++i] = NULL;
1932 if (!attrlist)
1933 return EPWMD_EMPTY_ELEMENT;
1935 line = g_strjoinv("\n", attrlist);
1937 if (!line) {
1938 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1939 g_strfreev(attrlist);
1940 return gpg_error_from_errno(ENOMEM);
1943 pth_cleanup_push(g_free, line);
1944 pth_cleanup_push(req_cleanup, attrlist);
1945 rc = xfer_data(ctx, line, strlen(line));
1946 pth_cleanup_pop(1);
1947 pth_cleanup_pop(1);
1948 return rc;
1952 * req[0] - attribute
1953 * req[1] - element path
1955 static int attribute_delete(struct client_s *client, gchar **req)
1957 xmlAttrPtr a;
1958 xmlNodePtr n;
1959 gchar **path = NULL;
1960 gpg_error_t rc;
1962 if (!req || !req[0] || !req[1])
1963 return EPWMD_COMMAND_SYNTAX;
1965 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1967 * The first argument may be only an account.
1969 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1970 return EPWMD_COMMAND_SYNTAX;
1974 * Don't remove the "name" attribute for the account element. To remove an
1975 * account use DELETE <account>.
1977 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1978 rc = EPWMD_ATTR_SYNTAX;
1979 goto fail;
1982 n = find_account(client->doc, &path, &rc, NULL, 0);
1984 if (!n)
1985 goto fail;
1987 if (path[1]) {
1988 n = find_elements(client->doc, n->children, path+1, &rc,
1989 NULL, NULL, NULL, FALSE, 0, NULL);
1991 if (!n)
1992 goto fail;
1995 g_strfreev(path);
1997 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1998 return EPWMD_ATTR_NOT_FOUND;
2000 if (xmlRemoveProp(a) == -1)
2001 return EPWMD_LIBXML_ERROR;
2003 return 0;
2005 fail:
2006 g_strfreev(path);
2007 return rc;
2010 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
2011 gpg_error_t *rc)
2013 gchar **src = *path;
2014 gchar **src_orig = g_strdupv(src);
2015 xmlNodePtr n = NULL;
2017 *rc = 0;
2019 if (!src_orig) {
2020 *rc = gpg_error_from_errno(ENOMEM);
2021 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2022 goto fail;
2025 again:
2026 n = find_account(client->doc, &src, rc, NULL, 0);
2028 if (!n) {
2029 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
2030 *rc = new_account(client->doc, src[0]);
2032 if (*rc)
2033 goto fail;
2035 goto again;
2037 else
2038 goto fail;
2041 if (src[1]) {
2042 if (!n->children)
2043 n = create_target_elements_cb(n, src+1, rc, NULL);
2044 else
2045 n = find_elements(client->doc, n->children, src+1, rc,
2046 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
2048 if (!n)
2049 goto fail;
2052 * Reset the position of the element tree now that the elements
2053 * have been created.
2055 g_strfreev(src);
2056 src = src_orig;
2057 src_orig = NULL;
2058 n = find_account(client->doc, &src, rc, NULL, 0);
2060 if (!n)
2061 goto fail;
2063 n = find_elements(client->doc, n->children, src+1, rc,
2064 NULL, NULL, NULL, FALSE, 0, NULL);
2066 if (!n)
2067 goto fail;
2070 fail:
2071 if (src_orig)
2072 g_strfreev(src_orig);
2074 *path = src;
2075 return n;
2079 * Creates a "target" attribute. When other commands encounter an element with
2080 * this attribute, the element path is modified to the target value. If the
2081 * source element path doesn't exist when using 'ATTR SET target', it is
2082 * created, but the destination element path must exist.
2084 * req[0] - source element path
2085 * req[1] - destination element path
2087 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2089 gchar **src, **dst, *line = NULL;
2090 gpg_error_t rc;
2091 xmlNodePtr n;
2093 if (!req || !req[0] || !req[1])
2094 return EPWMD_COMMAND_SYNTAX;
2096 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2098 * The first argument may be only an account.
2100 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2101 return EPWMD_COMMAND_SYNTAX;
2104 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2106 * The first argument may be only an account.
2108 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2109 rc = EPWMD_COMMAND_SYNTAX;
2110 goto fail;
2114 n = find_account(client->doc, &dst, &rc, NULL, 0);
2117 * Make sure the destination element path exists.
2119 if (!n)
2120 goto fail;
2122 if (dst[1]) {
2123 n = find_elements(client->doc, n->children, dst+1, &rc,
2124 NULL, NULL, NULL, FALSE, 0, NULL);
2126 if (!n)
2127 goto fail;
2130 n = create_element_path(client, &src, &rc);
2132 if (rc)
2133 goto fail;
2135 line = g_strjoinv("\t", dst);
2137 if (!line) {
2138 rc = gpg_error_from_errno(ENOMEM);
2139 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2140 goto fail;
2143 rc = add_attribute(n, "target", line);
2145 fail:
2146 g_free(line);
2147 g_strfreev(src);
2148 g_strfreev(dst);
2149 return rc;
2153 * req[0] - account name
2154 * req[1] - new name
2156 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2158 gpg_error_t rc;
2159 gchar **tmp;
2160 xmlNodePtr n;
2162 tmp = g_strdupv(req);
2164 if (!tmp) {
2165 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2166 return gpg_error_from_errno(ENOMEM);
2169 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2170 g_strfreev(tmp);
2172 if (!n)
2173 return rc;
2175 if (g_utf8_collate(req[0], req[1]) == 0)
2176 return 0;
2179 * Will not overwrite an existing account.
2181 tmp = g_strdupv(req+1);
2183 if (!tmp) {
2184 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2185 return gpg_error_from_errno(ENOMEM);
2188 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2189 g_strfreev(tmp);
2191 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
2192 return rc;
2194 if (n)
2195 return EPWMD_ACCOUNT_EXISTS;
2198 * Whitespace not allowed in account names.
2200 if (contains_whitespace(req[1]) == TRUE)
2201 return EPWMD_ATTR_SYNTAX;
2203 tmp = g_strdupv(req);
2205 if (!tmp) {
2206 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2207 return gpg_error_from_errno(ENOMEM);
2210 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2211 g_strfreev(tmp);
2213 if (!n)
2214 return EPWMD_ELEMENT_NOT_FOUND;
2216 return add_attribute(n, "name", req[1]);
2220 * req[0] - attribute
2221 * req[1] - element path
2223 static int attribute_get(assuan_context_t ctx, gchar **req)
2225 struct client_s *client = assuan_get_pointer(ctx);
2226 xmlNodePtr n;
2227 xmlChar *a;
2228 gchar **path= NULL;
2229 gpg_error_t rc;
2231 if (!req || !req[0] || !req[1])
2232 return EPWMD_COMMAND_SYNTAX;
2234 if (strchr(req[1], '\t')) {
2235 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2236 return EPWMD_COMMAND_SYNTAX;
2238 else {
2239 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2240 return EPWMD_COMMAND_SYNTAX;
2243 n = find_account(client->doc, &path, &rc, NULL, 0);
2245 if (!n)
2246 goto fail;
2248 if (path[1]) {
2249 n = find_elements(client->doc, n->children, path+1, &rc,
2250 NULL, NULL, NULL, FALSE, 0, NULL);
2252 if (!n)
2253 goto fail;
2256 g_strfreev(path);
2258 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2259 return EPWMD_ATTR_NOT_FOUND;
2261 pth_cleanup_push(xmlFree, a);
2262 rc = xfer_data(ctx, (gchar *)a, xmlStrlen(a));
2263 pth_cleanup_pop(1);
2264 return rc;
2266 fail:
2267 g_strfreev(path);
2268 return rc;
2272 * req[0] - attribute
2273 * req[1] - element path
2274 * req[2] - value
2276 static int attribute_set(struct client_s *client, gchar **req)
2278 gchar **path = NULL;
2279 gpg_error_t rc;
2280 xmlNodePtr n;
2282 if (!req || !req[0] || !req[1] || !req[2])
2283 return EPWMD_COMMAND_SYNTAX;
2286 * Reserved attribute names.
2288 if (g_utf8_collate(req[0], "name") == 0) {
2290 * Only reserved for the account element. Not the rest of the
2291 * document.
2293 if (strchr(req[1], '\t') == NULL)
2294 return name_attribute(client, req + 1);
2296 else if (g_utf8_collate(req[0], "target") == 0)
2297 return target_attribute(client, req + 1);
2299 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2301 * The first argument may be only an account.
2303 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2304 return EPWMD_COMMAND_SYNTAX;
2307 n = find_account(client->doc, &path, &rc, NULL, 0);
2309 if (!n)
2310 goto fail;
2312 if (path[1]) {
2313 n = find_elements(client->doc, n->children, path+1, &rc,
2314 NULL, NULL, NULL, FALSE, 0, NULL);
2316 if (!n)
2317 goto fail;
2320 g_strfreev(path);
2321 return add_attribute(n, req[0], req[2]);
2323 fail:
2324 g_strfreev(path);
2325 return rc;
2329 * req[0] - command
2330 * req[1] - attribute name or element path if command is LIST
2331 * req[2] - element path
2332 * req[2] - element path or value
2334 static int attr_command(assuan_context_t ctx, char *line)
2336 struct client_s *client = assuan_get_pointer(ctx);
2337 gchar **req;
2338 gpg_error_t rc = 0;
2340 log_write2("ARGS=\"%s\"", line);
2341 req = split_input_line(line, " ", 4);
2343 if (!req || !req[0] || !req[1]) {
2344 g_strfreev(req);
2345 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2348 pth_cleanup_push(req_cleanup, req);
2350 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2351 rc = attribute_set(client, req+1);
2352 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2353 rc = attribute_get(ctx, req+1);
2354 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2355 rc = attribute_delete(client, req+1);
2356 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2357 rc = attribute_list(ctx, req+1);
2358 else
2359 rc = EPWMD_COMMAND_SYNTAX;
2361 pth_cleanup_pop(1);
2362 return send_error(ctx, rc);
2365 static int iscached_command(assuan_context_t ctx, char *line)
2367 gchar **req = split_input_line(line, " ", 0);
2368 guchar md5file[16];
2369 gchar *path, *tmp;
2371 if (!req || !*req) {
2372 g_strfreev(req);
2373 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2376 log_write2("ARGS=\"%s\"", line);
2378 if (!valid_filename(req[0])) {
2379 g_strfreev(req);
2380 return EPWMD_INVALID_FILENAME;
2383 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2384 CACHE_LOCK(ctx);
2386 if (cache_iscached(md5file)) {
2387 g_strfreev(req);
2388 CACHE_UNLOCK;
2389 return send_error(ctx, 0);
2392 CACHE_UNLOCK;
2393 tmp = get_key_file_string("global", "data_directory");
2395 if (!tmp) {
2396 g_strfreev(req);
2397 return gpg_error_from_errno(ENOMEM);
2400 path = expand_homedir(tmp);
2402 if (!path) {
2403 g_strfreev(req);
2404 g_free(tmp);
2405 return gpg_error_from_errno(ENOMEM);
2408 g_free(tmp);
2409 tmp = path;
2410 path = g_strdup_printf("%s/%s", tmp, req[0]);
2411 g_free(tmp);
2413 if (!path) {
2414 g_strfreev(req);
2415 return gpg_error_from_errno(ENOMEM);
2418 if (access(path, R_OK) == -1) {
2419 gpg_error_t rc = gpg_error_from_syserror();
2421 g_free(path);
2422 g_strfreev(req);
2423 return send_error(ctx, rc);
2426 g_free(path);
2427 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2430 static int clearcache_command(assuan_context_t ctx, char *line)
2432 struct client_s *client = assuan_get_pointer(ctx);
2433 gchar **req = split_input_line(line, " ", 0);
2434 guchar md5file[16];
2436 log_write2("ARGS=\"%s\"", line);
2437 CACHE_LOCK(ctx);
2439 if (!req || !*req) {
2440 g_strfreev(req);
2441 cache_clear(client->md5file, 2);
2442 CACHE_UNLOCK;
2443 return send_error(ctx, 0);
2446 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2447 g_strfreev(req);
2449 if (cache_clear(md5file, 1) == FALSE) {
2450 CACHE_UNLOCK;
2451 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2454 CACHE_UNLOCK;
2455 return send_error(ctx, 0);
2458 static int cachetimeout_command(assuan_context_t ctx, char *line)
2460 guchar md5file[16];
2461 glong timeout;
2462 gchar **req = split_input_line(line, " ", 0);
2463 gchar *p;
2465 if (!req || !*req || !req[1]) {
2466 g_strfreev(req);
2467 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2470 errno = 0;
2471 timeout = strtol(req[1], &p, 10);
2473 if (errno != 0 || *p != 0 || timeout < -1) {
2474 g_strfreev(req);
2475 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2478 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2479 CACHE_LOCK(client->ctx);
2481 if (cache_set_timeout(md5file, timeout) == FALSE) {
2482 CACHE_UNLOCK;
2483 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2486 CACHE_UNLOCK;
2487 return send_error(ctx, 0);
2490 static int dump_command(assuan_context_t ctx, char *line)
2492 xmlChar *xml;
2493 gssize len;
2494 struct client_s *client = assuan_get_pointer(ctx);
2495 gpg_error_t rc;
2497 if (disable_list_and_dump == TRUE)
2498 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2500 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2502 if (!xml) {
2503 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2504 return send_syserror(ctx, ENOMEM);
2507 pth_cleanup_push(xmlFree, xml);
2508 rc = xfer_data(ctx, (gchar *)xml, len);
2509 pth_cleanup_pop(1);
2510 return send_error(ctx, rc);
2513 static int getconfig_command(assuan_context_t ctx, gchar *line)
2515 struct client_s *client = assuan_get_pointer(ctx);
2516 gpg_error_t rc = 0;
2517 gchar filename[255]={0}, param[747]={0};
2518 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2520 log_write2("ARGS=\"%s\"", line);
2522 if (strchr(line, ' ')) {
2523 sscanf(line, " %254[^ ] %746c", filename, param);
2524 paramp = param;
2525 fp = filename;
2528 if (fp && !valid_filename(fp))
2529 return send_error(ctx, EPWMD_INVALID_FILENAME);
2531 paramp = g_ascii_strdown(paramp, -1);
2533 if (!paramp) {
2534 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2535 return send_syserror(ctx, ENOMEM);
2538 if (fp && !g_ascii_strcasecmp(paramp, "iterations")) {
2539 if (!(client->opts & OPT_ITERATIONS) || fp != client->filename) {
2540 file_header_internal_t *fh = read_file_header(fp, FALSE, &rc);
2542 if (!fh && rc != GPG_ERR_ENOENT)
2543 return send_error(ctx, rc);
2545 if (!rc) {
2546 g_free(paramp);
2547 p = g_strdup_printf("%llu", fh->ver.fh2.iter);
2548 close_file_header(fh);
2550 if (!p) {
2551 log_write("%s(%i): %s", __FILE__, __LINE__,
2552 strerror(ENOMEM));
2553 return send_syserror(ctx, ENOMEM);
2556 goto done;
2560 else if (!g_ascii_strcasecmp(paramp, "enable_pinentry")) {
2561 #ifdef WITH_PINENTRY
2562 gboolean n;
2564 if (fp == client->filename && (client->opts & OPT_PINENTRY))
2565 n = client->pinentry->enable;
2566 else
2567 n = get_key_file_boolean(fp, "enable_pinentry");
2569 p = g_strdup_printf("%s", n ? "true" : "false");
2571 if (!p) {
2572 log_write("%s(%i): %s", __FILE__, __LINE__,
2573 strerror(ENOMEM));
2574 return send_syserror(ctx, ENOMEM);
2577 goto done;
2578 #else
2579 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2580 #endif
2582 else if (!g_ascii_strcasecmp(paramp, "pinentry_timeout")) {
2583 #ifdef WITH_PINENTRY
2584 if (fp == client->filename && (client->opts & OPT_PINENTRY_TO))
2585 p = g_strdup_printf("%i", client->pinentry->timeout);
2586 else
2587 p = g_strdup_printf("%i",
2588 get_key_file_integer(fp, "pinentry_timeout"));
2590 if (!p) {
2591 log_write("%s(%i): %s", __FILE__, __LINE__,
2592 strerror(ENOMEM));
2593 return send_syserror(ctx, ENOMEM);
2596 goto done;
2597 #else
2598 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2599 #endif
2602 p = get_key_file_string(fp ? fp : "global", paramp);
2603 g_free(paramp);
2605 if (!p)
2606 return send_error(ctx, GPG_ERR_NO_VALUE);
2608 tmp = expand_homedir(p);
2609 g_free(p);
2611 if (!tmp) {
2612 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2613 return send_syserror(ctx, ENOMEM);
2616 p = tmp;
2617 done:
2618 pth_cleanup_push(g_free, p);
2619 rc = xfer_data(ctx, p, strlen(p));
2620 pth_cleanup_pop(1);
2621 return send_error(ctx, rc);
2624 struct xpath_s {
2625 xmlXPathContextPtr xp;
2626 xmlXPathObjectPtr result;
2627 xmlBufferPtr buf;
2628 gchar **req;
2631 static void xpath_command_cleanup(void *arg)
2633 struct xpath_s *xpath = arg;
2635 req_cleanup(xpath->req);
2637 if (xpath->buf)
2638 xmlBufferFree(xpath->buf);
2640 if (xpath->result)
2641 xmlXPathFreeObject(xpath->result);
2643 if (xpath->xp)
2644 xmlXPathFreeContext(xpath->xp);
2647 static int xpath_command(assuan_context_t ctx, gchar *line)
2649 struct client_s *client = assuan_get_pointer(ctx);
2650 gpg_error_t rc;
2651 struct xpath_s xpath;
2653 log_write2("ARGS=\"%s\"", line);
2655 if (disable_list_and_dump == TRUE)
2656 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2658 if (!line || !*line)
2659 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2661 memset(&xpath, 0, sizeof(struct xpath_s));
2663 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
2664 if (strv_printf(&xpath.req, "%s", line) == FALSE)
2665 return send_syserror(ctx, ENOMEM);
2668 xpath.xp = xmlXPathNewContext(client->doc);
2670 if (!xpath.xp) {
2671 xpath_command_cleanup(&xpath);
2672 return send_error(ctx, EPWMD_LIBXML_ERROR);
2675 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
2677 if (!xpath.result) {
2678 xpath_command_cleanup(&xpath);
2679 return send_error(ctx, EPWMD_LIBXML_ERROR);
2682 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
2683 rc = EPWMD_EMPTY_ELEMENT;
2684 goto fail;
2687 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
2688 (xmlChar *)xpath.req[1], &xpath.buf);
2690 if (rc)
2691 goto fail;
2692 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
2693 rc = EPWMD_EMPTY_ELEMENT;
2694 goto fail;
2696 else if (xpath.req[1])
2697 goto fail;
2699 pth_cleanup_push(xpath_command_cleanup, &xpath);
2700 rc = xfer_data(ctx, (gchar *)xmlBufferContent(xpath.buf),
2701 xmlBufferLength(xpath.buf));
2702 pth_cleanup_pop(0);
2704 fail:
2705 xpath_command_cleanup(&xpath);
2706 return send_error(ctx, rc);
2709 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2710 gsize len)
2712 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2713 gpg_error_t rc = file_modified(client);
2714 gchar **req, **path = NULL, **path_orig = NULL, *content;
2715 xmlDocPtr doc;
2716 xmlNodePtr n, root, copy;
2718 if (assuan_rc || rc) {
2719 if (line)
2720 xfree(line);
2721 return assuan_rc ? assuan_rc : rc;
2724 req = split_input_line((gchar *)line, " ", 2);
2725 xfree(line);
2727 if (!req || !*req)
2728 return EPWMD_COMMAND_SYNTAX;
2730 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2731 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2732 return EPWMD_COMMAND_SYNTAX;
2735 content = req[1];
2737 if (!content || !*content) {
2738 rc = EPWMD_COMMAND_SYNTAX;
2739 goto fail;
2742 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2744 if (!doc) {
2745 rc = EPWMD_LIBXML_ERROR;
2746 goto fail;
2749 root = xmlDocGetRootElement(doc);
2750 path_orig = g_strdupv(path);
2752 if (!path_orig) {
2753 xmlFreeDoc(doc);
2754 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2755 rc = gpg_error_from_errno(ENOMEM);
2756 goto fail;
2759 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2760 g_strfreev(path_orig);
2761 xmlFreeDoc(doc);
2762 rc = gpg_error_from_errno(ENOMEM);
2763 goto fail;
2766 n = find_account(client->doc, &path, &rc, NULL, 0);
2768 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2769 g_strfreev(path_orig);
2770 xmlFreeDoc(doc);
2771 goto fail;
2773 else if (!rc) {
2774 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2776 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2777 g_strfreev(path_orig);
2778 xmlFreeDoc(doc);
2779 goto fail;
2781 else if (!rc) {
2782 xmlNodePtr parent = n->parent;
2784 xmlUnlinkNode(n);
2785 xmlFreeNode(n);
2786 n = parent;
2790 g_strfreev(path);
2791 path = path_orig;
2793 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2794 n = create_element_path(client, &path, &rc);
2796 if (rc) {
2797 xmlFreeDoc(doc);
2798 goto fail;
2802 copy = xmlCopyNode(root, 1);
2803 n = xmlAddChild(n, copy);
2804 xmlFreeDoc(doc);
2806 if (!n)
2807 rc = EPWMD_LIBXML_ERROR;
2809 fail:
2810 g_strfreev(path);
2811 g_strfreev(req);
2812 client->inquire_status = INQUIRE_DONE;
2813 return rc;
2816 static int import_command(assuan_context_t ctx, gchar *line)
2818 gpg_error_t rc;
2819 struct client_s *client = assuan_get_pointer(ctx);
2821 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2823 if (rc)
2824 return send_error(ctx, rc);
2826 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2827 client->inquire_status = INQUIRE_BUSY;
2828 return 0;
2831 static int lock_command(assuan_context_t ctx, gchar *line)
2833 gpg_error_t rc;
2834 struct client_s *client = assuan_get_pointer(ctx);
2836 rc = lock_file_mutex(client);
2838 if (!rc)
2839 client->is_lock_cmd = TRUE;
2841 return send_error(ctx, rc);
2844 static int unlock_command(assuan_context_t ctx, gchar *line)
2846 struct client_s *client = assuan_get_pointer(ctx);
2848 unlock_file_mutex(client);
2849 return send_error(ctx, 0);
2852 static int getpid_command(assuan_context_t ctx, gchar *line)
2854 gpg_error_t rc;
2855 gchar buf[32];
2856 pid_t pid = getpid();
2858 print_fmt(buf, sizeof(buf), "%i", pid);
2859 rc = xfer_data(ctx, buf, strlen(buf));
2860 return send_error(ctx, rc);
2863 static int version_command(assuan_context_t ctx, gchar *line)
2865 gpg_error_t rc;
2866 gchar buf[32];
2868 print_fmt(buf, sizeof(buf), "0x%X", VERSION_HEX);
2869 rc = xfer_data(ctx, buf, strlen(buf));
2870 return send_error(ctx, rc);
2873 #ifdef WITH_PINENTRY
2874 static void set_option_value(gchar **opt, const gchar *value)
2876 if (opt)
2877 g_free(*opt);
2879 *opt = NULL;
2881 if (value)
2882 *opt = g_strdup(value);
2884 #endif
2886 static int set_unset_common(assuan_context_t ctx, const gchar *name,
2887 const gchar *value)
2889 struct client_s *client = assuan_get_pointer(ctx);
2891 if (g_ascii_strcasecmp(name, (gchar *)"log_level") == 0) {
2892 glong l = 0;
2894 if (value) {
2895 l = strtol(value, NULL, 10);
2897 if (l < 0 || l > 2)
2898 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2901 log_write1("log_level=%li", l);
2902 MUTEX_LOCK(&rcfile_mutex);
2903 g_key_file_set_integer(keyfileh, "global", "log_level", (gint)l);
2904 MUTEX_UNLOCK(&rcfile_mutex);
2905 goto done;
2907 else if (g_ascii_strcasecmp(name, (gchar *)"cipher") == 0) {
2908 guint64 flags;
2909 const gchar *p = value;
2911 if (!client->filename)
2912 return EPWMD_NO_FILE;
2914 if (value) {
2915 flags = pwmd_cipher_str_to_cipher(value);
2917 if (!flags)
2918 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2920 else if (!value)
2921 p = get_key_file_string("global", "cipher");
2923 MUTEX_LOCK(&rcfile_mutex);
2924 g_key_file_set_string(keyfileh, client->filename, "cipher", p);
2925 MUTEX_UNLOCK(&rcfile_mutex);
2926 log_write1("cipher=%s", p);
2928 if (!value)
2929 g_free((gchar *)p);
2931 goto done;
2933 else if (g_ascii_strcasecmp(name, (gchar *)"iterations") == 0) {
2934 gdouble n;
2935 gchar *p = NULL;
2937 if (!client->filename)
2938 return EPWMD_NO_FILE;
2940 if (!value) {
2941 MUTEX_LOCK(&rcfile_mutex);
2942 g_key_file_set_double(keyfileh, client->filename, "iterations",
2943 get_key_file_double("global", "iterations"));
2944 MUTEX_UNLOCK(&rcfile_mutex);
2945 log_write1("iterations=%llu",
2946 get_key_file_double(client->filename, "iterations"));
2947 goto done;
2950 errno = 0;
2951 n = strtoll(value, &p, 10);
2953 if (errno || (p && *p) || n < 0ULL)
2954 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2956 MUTEX_LOCK(&rcfile_mutex);
2957 g_key_file_set_double(keyfileh,
2958 client->filename ? client->filename : "global", "iterations", n);
2959 MUTEX_UNLOCK(&rcfile_mutex);
2961 if (client->filename)
2962 client->opts |= OPT_ITERATIONS;
2964 log_write1("iterations=%llu", n);
2965 goto done;
2967 else if (g_ascii_strcasecmp(name, (gchar *)"NAME") == 0) {
2968 pth_attr_t attr = pth_attr_of(pth_self());
2969 gchar buf[41];
2971 if (!value) {
2972 pth_attr_destroy(attr);
2973 goto done;
2976 print_fmt(buf, sizeof(buf), "%s", value);
2977 pth_attr_set(attr, PTH_ATTR_NAME, buf);
2978 pth_attr_destroy(attr);
2979 log_write1("name=%s", buf);
2980 #ifdef WITH_PINENTRY
2981 if (client->pinentry->name)
2982 g_free(client->pinentry->name);
2984 client->pinentry->name = g_strdup(buf);
2986 if (!client->pinentry->name)
2987 return gpg_error_from_errno(ENOMEM);
2988 #endif
2990 goto done;
2992 #ifdef WITH_PINENTRY
2993 else if (g_ascii_strcasecmp(name, (gchar *)"lc_messages") == 0)
2994 set_option_value(&client->pinentry->lcmessages, value);
2995 else if (g_ascii_strcasecmp(name, (gchar *)"lc_ctype") == 0)
2996 set_option_value(&client->pinentry->lcctype, value);
2997 else if (g_ascii_strcasecmp(name, (gchar *)"ttyname") == 0)
2998 set_option_value(&client->pinentry->ttyname, value);
2999 else if (g_ascii_strcasecmp(name, (gchar *)"ttytype") == 0)
3000 set_option_value(&client->pinentry->ttytype, value);
3001 else if (g_ascii_strcasecmp(name, (gchar *)"display") == 0)
3002 set_option_value(&client->pinentry->display, value);
3003 else if (g_ascii_strcasecmp(name, (gchar *)"pinentry_path") == 0)
3004 set_option_value(&client->pinentry->path, value);
3005 else if (g_ascii_strcasecmp(name, (gchar *)"title") == 0)
3006 set_option_value(&client->pinentry->title, value);
3007 else if (g_ascii_strcasecmp(name, (gchar *)"prompt") == 0)
3008 set_option_value(&client->pinentry->prompt, value);
3009 else if (g_ascii_strcasecmp(name, (gchar *)"desc") == 0)
3010 set_option_value(&client->pinentry->desc, value);
3011 else if (g_ascii_strcasecmp(name, "pinentry_timeout") == 0) {
3012 gchar *p = NULL;
3013 gint n;
3015 if (!value) {
3016 client->pinentry->timeout =
3017 get_key_file_integer(client->filename, "pinentry_timeout");
3018 client->opts &= ~(OPT_PINENTRY_TO);
3019 log_write1("pinentry_timeout=%i",
3020 get_key_file_integer(client->filename, "pinentry_timeout"));
3021 goto done;
3024 n = strtol(value, &p, 10);
3026 if (*p || n < 0)
3027 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3029 client->pinentry->timeout = n;
3030 client->opts |= OPT_PINENTRY_TO;
3031 log_write1("pinentry_timeout=%i", n);
3032 goto done;
3034 else if (g_ascii_strcasecmp(name, "enable_pinentry") == 0) {
3035 gchar *p = NULL;
3036 gint n;
3038 if (!value) {
3039 client->pinentry->enable = -1;
3040 client->opts &= ~(OPT_PINENTRY);
3041 goto done;
3044 n = strtol(value, &p, 10);
3046 if (*p || n < 0 || n > 1)
3047 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3049 client->pinentry->enable = n == 0 ? FALSE : TRUE;
3050 client->opts |= OPT_PINENTRY;
3051 log_write1("enable_pinentry=%i", n);
3052 goto done;
3054 #endif
3055 else
3056 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
3058 log_write1("%s=%s", name, value ? value : "");
3060 done:
3061 return 0;
3064 static int unset_command(assuan_context_t ctx, gchar *line)
3066 log_write2("ARGS=\"%s\"", line);
3067 return send_error(ctx, set_unset_common(ctx, line, NULL));
3070 static int set_command(assuan_context_t ctx, gchar *line)
3072 gchar name[64] = {0}, value[256] = {0};
3074 log_write2("ARGS=\"%s\"", line);
3076 if (sscanf(line, " %63[_a-zA-Z] = %255c", name, value) != 2)
3077 return send_error(ctx, gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX));
3079 return send_error(ctx, set_unset_common(ctx, name, value));
3082 static int rename_command(assuan_context_t ctx, gchar *line)
3084 struct client_s *client = assuan_get_pointer(ctx);
3085 gpg_error_t rc;
3086 gchar **req, **src, *dst;
3087 xmlNodePtr n;
3089 log_write2("ARGS=\"%s\"", line);
3090 req = split_input_line(line, " ", -1);
3092 if (!req || !req[0] || !req[1]) {
3093 g_strfreev(req);
3094 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
3097 dst = req[1];
3099 if (!valid_xml_element((xmlChar *)dst)) {
3100 g_strfreev(req);
3101 return EPWMD_INVALID_ELEMENT;
3104 if (strchr(req[0], '\t'))
3105 src = split_input_line(req[0], "\t", -1);
3106 else
3107 src = split_input_line(req[0], " ", -1);
3109 if (!src || !*src) {
3110 rc = EPWMD_COMMAND_SYNTAX;
3111 goto fail;
3114 n = find_account(client->doc, &src, &rc, NULL, 0);
3116 if (src[1] && n)
3117 n = find_elements(client->doc, n->children, src+1, &rc, NULL, NULL,
3118 NULL, FALSE, 0, NULL);
3120 if (!n)
3121 goto fail;
3123 if (src[1])
3124 xmlNodeSetName(n, (xmlChar *)dst);
3125 else
3126 rc = add_attribute(n, "name", dst);
3128 fail:
3129 g_strfreev(req);
3130 g_strfreev(src);
3131 return send_error(ctx, rc);
3134 static int copy_command(assuan_context_t ctx, gchar *line)
3136 struct client_s *client = assuan_get_pointer(ctx);
3137 gpg_error_t rc;
3138 gchar **req, **src = NULL, **dst = NULL;
3139 xmlNodePtr nsrc, ndst, new, n;
3141 log_write2("ARGS=\"%s\"", line);
3142 req = split_input_line(line, " ", -1);
3144 if (!req || !req[0] || !req[1]) {
3145 g_strfreev(req);
3146 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
3149 if (strchr(req[0], '\t'))
3150 src = split_input_line(req[0], "\t", -1);
3151 else
3152 src = split_input_line(req[0], " ", -1);
3154 if (!src || !*src) {
3155 rc = EPWMD_COMMAND_SYNTAX;
3156 goto fail;
3159 if (strchr(req[1], '\t'))
3160 dst = split_input_line(req[1], "\t", -1);
3161 else
3162 dst = split_input_line(req[1], " ", -1);
3164 if (!dst || !*dst) {
3165 rc = EPWMD_COMMAND_SYNTAX;
3166 goto fail;
3169 nsrc = find_account(client->doc, &src, &rc, NULL, 0);
3171 if (nsrc && src[1])
3172 nsrc = find_elements(client->doc, nsrc->children, src+1, &rc, NULL,
3173 NULL, NULL, FALSE, 0, NULL);
3175 if (!nsrc)
3176 goto fail;
3178 ndst = find_account(client->doc, &dst, &rc, NULL, 0);
3180 if (ndst && dst[1])
3181 ndst = find_elements(client->doc, ndst->children, dst+1, &rc, NULL,
3182 NULL, NULL, FALSE, 0, NULL);
3184 if (!ndst && rc != EPWMD_ELEMENT_NOT_FOUND)
3185 goto fail;
3187 new = xmlCopyNode(nsrc->children, 1);
3189 if (!new) {
3190 rc = GPG_ERR_ENOMEM;
3191 goto fail;
3194 if (!ndst) {
3195 ndst = create_element_path(client, &dst, &rc);
3197 if (!ndst)
3198 goto fail;
3201 n = ndst->children;
3202 xmlUnlinkNode(ndst->children);
3203 xmlFreeNodeList(n);
3204 xmlAddChild(ndst, new);
3206 fail:
3207 if (req)
3208 g_strfreev(req);
3210 if (src)
3211 g_strfreev(src);
3213 if (dst)
3214 g_strfreev(dst);
3216 return send_error(ctx, rc);
3219 static void bye_notify(assuan_context_t ctx)
3221 struct client_s *cl = assuan_get_pointer(ctx);
3223 /* This will let assuan_process_next() return. */
3224 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
3227 static void reset_notify(assuan_context_t ctx)
3229 struct client_s *cl = assuan_get_pointer(ctx);
3231 if (cl)
3232 cleanup_client(cl);
3236 * This is called before every Assuan command.
3238 int command_startup(assuan_context_t ctx, const char *name)
3240 struct client_s *cl = assuan_get_pointer(ctx);
3241 gpg_error_t rc;
3243 log_write1("%s", name);
3245 if (!g_ascii_strcasecmp(name, "ISCACHED") ||
3246 !g_ascii_strcasecmp(name, "CLEARCACHE") ||
3247 !g_ascii_strcasecmp(name, "CACHETIMEOUT") ||
3248 !g_ascii_strcasecmp(name, "GETCONFIG") ||
3249 !g_ascii_strcasecmp(name, "GETPID") ||
3250 !g_ascii_strcasecmp(name, "VERSION") ||
3251 !g_ascii_strcasecmp(name, "SET") ||
3252 !g_ascii_strcasecmp(name, "BYE") ||
3253 !g_ascii_strcasecmp(name, "NOP") ||
3254 !g_ascii_strcasecmp(name, "CANCEL") ||
3255 !g_ascii_strcasecmp(name, "RESET") ||
3256 !g_ascii_strcasecmp(name, "END") ||
3257 !g_ascii_strcasecmp(name, "HELP") ||
3258 !g_ascii_strcasecmp(name, "OPTION") ||
3259 !g_ascii_strcasecmp(name, "INPUT") ||
3260 !g_ascii_strcasecmp(name, "OUTPUT") ||
3261 !g_ascii_strcasecmp(name, "UNSET"))
3262 return 0;
3264 cl->last_rc = rc = file_modified(cl);
3266 if (rc) {
3267 if ((rc == EPWMD_NO_FILE || rc == EPWMD_FILE_MODIFIED) &&
3268 !g_ascii_strcasecmp(name, "OPEN"))
3269 rc = 0;
3272 return rc;
3276 * This is called after every Assuan command.
3278 void command_finalize(assuan_context_t ctx, gint rc)
3280 struct client_s *client = assuan_get_pointer(ctx);
3282 if (!client->is_lock_cmd)
3283 unlock_file_mutex(client);
3285 log_write1(N_("command completed (rc=%u)"), client->last_rc);
3288 gpg_error_t register_commands(assuan_context_t ctx)
3290 static struct {
3291 const gchar *name;
3292 gint (*handler)(assuan_context_t, gchar *line);
3293 } table[] = {
3294 { "OPEN", open_command },
3295 { "SAVE", save_command },
3296 { "LIST", list_command },
3297 { "REALPATH", realpath_command },
3298 { "STORE", store_command },
3299 { "DELETE", delete_command },
3300 { "GET", get_command },
3301 { "ATTR", attr_command },
3302 { "ISCACHED", iscached_command },
3303 { "CLEARCACHE", clearcache_command },
3304 { "CACHETIMEOUT", cachetimeout_command },
3305 { "GETCONFIG", getconfig_command },
3306 { "DUMP", dump_command },
3307 { "XPATH", xpath_command },
3308 { "IMPORT", import_command },
3309 { "LOCK", lock_command },
3310 { "UNLOCK", unlock_command },
3311 { "GETPID", getpid_command },
3312 { "VERSION", version_command },
3313 { "SET", set_command },
3314 { "UNSET", unset_command },
3315 { "RENAME", rename_command },
3316 { "COPY", copy_command },
3317 { "INPUT", NULL },
3318 { "OUTPUT", NULL },
3319 { NULL, NULL }
3321 gint i, rc;
3323 for (i=0; table[i].name; i++) {
3324 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
3326 if (rc)
3327 return rc;
3330 rc = assuan_register_bye_notify(ctx, bye_notify);
3332 if (rc)
3333 return rc;
3335 rc = assuan_register_reset_notify(ctx, reset_notify);
3337 if (rc)
3338 return rc;
3340 rc = assuan_register_pre_cmd_notify(ctx, command_startup);
3342 if (rc)
3343 return rc;
3345 return assuan_register_post_cmd_notify(ctx, command_finalize);
3348 gpg_error_t try_xml_decrypt(assuan_context_t ctx, guchar *key,
3349 struct client_crypto_s *crypto, gpointer *dst, goffset *dst_len)
3351 goffset insize, len;
3352 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
3353 guint64 iter = 0ULL, n_iter = 0ULL, iter_progress = 0ULL;
3354 gint zrc = 0;
3355 gulong outsize = 0;
3356 gpg_error_t rc;
3357 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->ver.fh1) : sizeof(crypto->fh->ver.fh2);
3358 guint64 fh_iter = crypto->fh->v1 ? crypto->fh->ver.fh1.iter : crypto->fh->ver.fh2.iter;
3359 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
3361 lseek(crypto->fh->fd, fh_size, SEEK_SET);
3362 insize = crypto->fh->st.st_size - fh_size;
3363 crypto->iv = gcry_malloc(crypto->blocksize);
3365 if (!crypto->iv) {
3366 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3367 return gpg_error_from_errno(ENOMEM);
3370 /* No encryption iterations. This is a plain (gzipped) file. */
3371 if ((crypto->fh->v1 && fh_iter < 0ULL) ||
3372 (!crypto->fh->v1 && fh_iter <= 0ULL)) {
3374 * cache_file_count() needs both .used == TRUE and a valid key in
3375 * order for it to count as a used cache entry. Fixes CACHE status
3376 * messages.
3378 memset(key, '!', hashlen);
3381 if (crypto->fh->v1)
3382 memcpy(crypto->iv, crypto->fh->ver.fh1.iv, crypto->blocksize);
3383 else
3384 memcpy(crypto->iv, crypto->fh->ver.fh2.iv, crypto->blocksize);
3386 crypto->inbuf = gcry_malloc(insize);
3388 if (!crypto->inbuf) {
3389 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3390 return gpg_error_from_errno(ENOMEM);
3393 crypto->insize = insize;
3394 len = pth_read(crypto->fh->fd, crypto->inbuf, crypto->insize);
3396 if (len != crypto->insize)
3397 return GPG_ERR_INV_LENGTH;
3399 if ((crypto->fh->v1 && fh_iter < 0ULL) ||
3400 (!crypto->fh->v1 && fh_iter <= 0ULL))
3401 goto decompress;
3403 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, crypto->blocksize))) {
3404 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3405 return rc;
3408 if ((rc = gcry_cipher_setkey(crypto->gh, key, crypto->keysize))) {
3409 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3410 return rc;
3413 iter_progress = (guint64)get_key_file_double(client && client->filename ?
3414 client->filename : "global", "iteration_progress");
3416 if (iter_progress > 0ULL && fh_iter >= iter_progress) {
3417 rc = send_status(ctx, STATUS_DECRYPT, "0 %llu", fh_iter);
3419 if (rc)
3420 return rc;
3423 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3425 if (rc)
3426 return rc;
3428 crypto->tkey = gcry_malloc(hashlen);
3430 if (!crypto->tkey) {
3431 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
3432 return gpg_error_from_errno(ENOMEM);
3435 memcpy(crypto->tkey, key, hashlen);
3436 guchar *tkey = crypto->tkey;
3437 tkey[0] ^= 1;
3439 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, crypto->keysize))) {
3440 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3441 return rc;
3444 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
3445 if (iter_progress > 0ULL && iter >= iter_progress) {
3446 if (!(iter % iter_progress)) {
3447 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu",
3448 ++n_iter * iter_progress, fh_iter);
3450 if (rc)
3451 return rc;
3455 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, crypto->blocksize))) {
3456 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3457 return rc;
3460 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3462 if (rc) {
3463 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3464 return rc;
3467 iter++;
3470 if (iter_progress && fh_iter >= iter_progress) {
3471 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu", fh_iter, fh_iter);
3473 if (rc)
3474 return rc;
3477 decompress:
3478 if (do_decompress(ctx, crypto->inbuf, crypto->insize,
3479 (gpointer *)&crypto->outbuf, &outsize, &zrc) == FALSE) {
3480 if (zrc == Z_MEM_ERROR)
3481 return gpg_error_from_errno(ENOMEM);
3482 else
3483 return EPWMD_BADKEY; // Not a valid gzip header. Must be a bad key.
3486 if (g_strncasecmp(crypto->outbuf, "<?xml ", 6) != 0) {
3487 gcry_free(crypto->outbuf);
3488 crypto->outbuf = NULL;
3489 return EPWMD_BADKEY;
3492 if (ctx) {
3493 client->xml = crypto->outbuf;
3494 client->len = outsize;
3495 crypto->outbuf = NULL;
3497 else if (dst) {
3498 *dst = crypto->outbuf;
3499 *dst_len = outsize;
3500 crypto->outbuf = NULL;
3503 /* The calling function should free the crypto struct. */
3504 return 0;