Fixed a segfault with the SAVE command when used with pinentry. Broke in
[pwmd.git] / src / commands.c
blob300412367d19f49c3e96305c1883629afb987d33
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 #include "mem.h"
37 #include "xml.h"
38 #include "common.h"
40 #ifdef WITH_PINENTRY
41 #include "pinentry.h"
42 #endif
44 #include "pwmd_error.h"
45 #include "cache.h"
46 #include "misc.h"
47 #ifdef WITH_GNUTLS
48 #include "tls.h"
49 #endif
50 #include "commands.h"
51 #include "lock.h"
53 static void *z_alloc(void *data, unsigned items, unsigned size)
55 return gcry_calloc(items, size);
58 static void z_free(void *data, void *p)
60 gcry_free(p);
63 static gpg_error_t file_modified(struct client_s *client)
65 struct stat st;
66 gpg_error_t rc;
68 if (client->state != STATE_OPEN)
69 return EPWMD_NO_FILE;
71 rc = lock_file_mutex(client);
73 if (rc)
74 return rc;
76 if (lstat(client->filename, &st) == 0 && client->mtime) {
77 if (client->mtime != st.st_mtime)
78 return EPWMD_FILE_MODIFIED;
81 pthread_testcancel();
82 return 0;
85 static gpg_error_t parse_xml(assuan_context_t ctx)
87 struct client_s *client = assuan_get_pointer(ctx);
89 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
91 if (!client->doc)
92 return EPWMD_LIBXML_ERROR;
94 return 0;
97 void unlock_file_mutex(struct client_s *client)
99 struct file_mutex_s *m;
101 #ifdef WITH_PINENTRY
102 if (client->has_lock == FALSE || client->pinentry->status != PINENTRY_NONE)
103 #else
104 if (client->has_lock == FALSE)
105 #endif
106 return;
108 CACHE_LOCK(client->ctx);
110 if (cache_get_mutex(client->md5file, &m) == FALSE) {
111 CACHE_UNLOCK;
112 return;
115 CACHE_UNLOCK;
116 MUTEX_UNLOCK(&m->mutex);
117 client->has_lock = client->is_lock_cmd = FALSE;
120 gpg_error_t lock_file_mutex(struct client_s *client)
122 struct file_mutex_s *m;
124 if (client->has_lock == TRUE)
125 return 0;
127 CACHE_LOCK(client->ctx);
129 if (cache_get_mutex(client->md5file, &m) == FALSE) {
130 CACHE_UNLOCK;
131 return 0;
134 CACHE_UNLOCK;
136 if (pthread_mutex_trylock(&m->mutex) == EBUSY) {
137 if (client->ctx) {
139 * If a client disconnects unexpectedly while waiting for a
140 * lock, this lets the thread terminate because send_status()
141 * will return an error.
143 while (pthread_mutex_trylock(&m->mutex) == EBUSY) {
144 gpg_error_t rc = send_status(client->ctx, STATUS_LOCKED, NULL);
146 if (rc)
147 return rc;
149 sleep(1);
152 else {
153 MUTEX_LOCK(&m->mutex);
156 else {
157 MUTEX_LOCK_DEBUG;
160 client->has_lock = TRUE;
161 return 0;
164 void free_client(struct client_s *client)
166 if (client->doc)
167 xmlFreeDoc(client->doc);
169 if (client->xml)
170 gcry_free(client->xml);
172 if (client->filename)
173 g_free(client->filename);
175 if (client->crypto)
176 cleanup_crypto(&client->crypto);
178 if (client->xml_error)
179 xmlResetError(client->xml_error);
182 void cleanup_client(struct client_s *client)
184 assuan_context_t ctx = client->ctx;
185 struct client_thread_s *thd = client->thd;
186 gboolean has_lock = client->has_lock;
187 #ifdef WITH_PINENTRY
188 struct pinentry_s *pin = client->pinentry;
189 #endif
191 unlock_file_mutex(client);
192 CACHE_LOCK(client->ctx);
193 cache_decr_refcount(client->md5file);
196 * This may be a new file so don't use a cache slot. save_command() will
197 * set this to FALSE on success.
199 if (client->new == TRUE)
200 cache_clear(client->md5file, 1);
202 free_client(client);
203 memset(client, 0, sizeof(struct client_s));
204 client->state = STATE_CONNECTED;
205 client->ctx = ctx;
206 client->thd = thd;
207 client->freed = TRUE;
208 #ifdef WITH_PINENTRY
209 client->pinentry = pin;
210 #endif
211 client->has_lock = has_lock;
212 CACHE_UNLOCK;
215 gboolean do_decompress(assuan_context_t ctx, gpointer in, gulong insize,
216 gpointer *out, gulong *outsize, gint *rc)
218 z_stream z;
219 gpointer pout;
220 gz_header h;
221 gchar buf[17];
223 z.zalloc = z_alloc;
224 z.zfree = z_free;
225 z.next_in = in;
226 z.avail_in = (uInt)insize;
227 z.avail_out = zlib_bufsize;
228 z.next_out = pout = gcry_malloc(zlib_bufsize);
230 if (!pout) {
231 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
232 *rc = Z_MEM_ERROR;
233 return FALSE;
236 *rc = inflateInit2(&z, 47);
238 if (*rc != Z_OK) {
239 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
240 gcry_free(pout);
241 return FALSE;
244 memset(&h, 0, sizeof(gz_header));
245 h.comment = (guchar *)buf;
246 h.comm_max = sizeof(buf);
247 *rc = inflateGetHeader(&z, &h);
249 if (*rc != Z_OK) {
250 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
251 gcry_free(pout);
252 inflateEnd(&z);
253 return FALSE;
256 *rc = inflate(&z, Z_BLOCK);
258 if (*rc != Z_OK) {
259 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
260 gcry_free(pout);
261 inflateEnd(&z);
262 return FALSE;
265 if (h.comment)
266 insize = (gulong)strtol((gchar *)h.comment, NULL, 10);
268 do {
269 gpointer p;
271 *rc = inflate(&z, Z_FINISH);
273 switch (*rc) {
274 case Z_OK:
275 break;
276 case Z_BUF_ERROR:
277 if (!z.avail_out) {
278 p = gcry_realloc(pout, z.total_out + zlib_bufsize);
280 if (!p) {
281 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
282 *rc = Z_MEM_ERROR;
283 goto fail;
286 pout = p;
287 z.next_out = pout + z.total_out;
288 z.avail_out = zlib_bufsize;
289 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li",
290 z.total_out, insize);
291 if (*rc)
292 goto fail;
294 break;
295 case Z_STREAM_END:
296 break;
297 default:
298 goto fail;
299 break;
301 } while (*rc != Z_STREAM_END);
303 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li", z.total_out,
304 insize);
306 if (*rc)
307 goto fail;
309 *out = pout;
310 *outsize = z.total_out;
311 inflateEnd(&z);
312 *rc = 0;
313 return TRUE;
315 fail:
316 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
317 gcry_free(pout);
318 inflateEnd(&z);
319 return FALSE;
322 static void read_file_header_handler(void *arg)
324 close((int)arg);
327 file_header_internal_t *read_file_header(const gchar *filename, gboolean v1,
328 gpg_error_t *rc)
330 gint fd;
331 gsize len;
332 file_header_internal_t *fh = g_malloc0(sizeof(file_header_internal_t));
333 gsize fh_size;
335 *rc = 0;
337 if (!fh) {
338 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
339 *rc = gpg_error_from_errno(ENOMEM);
340 return NULL;
343 fh_size = v1 ? sizeof(fh->fh1) : sizeof(fh->fh2);
345 if (lstat(filename, &fh->st) == -1) {
346 *rc = gpg_error_from_syserror();
347 g_free(fh);
348 return NULL;
351 if (!S_ISREG(fh->st.st_mode)) {
352 *rc = GPG_ERR_ENOANO;
353 g_free(fh);
354 return NULL;
357 fd = open(filename, O_RDONLY);
359 if (fd == -1) {
360 *rc = gpg_error_from_errno(errno);
361 g_free(fh);
362 return NULL;
365 if (v1)
366 len = read(fd, &fh->fh1, fh_size);
367 else
368 len = read(fd, &fh->fh2, fh_size);
370 if (len != fh_size) {
371 gint n = errno;
372 pthread_cleanup_push(read_file_header_handler, (void *)fd);
373 g_free(fh);
374 pthread_cleanup_pop(1);
375 *rc = gpg_error_from_errno(n);
376 return NULL;
379 fh->v1 = v1;
380 fh->fd = fd;
381 return fh;
384 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar *key,
385 gboolean cached)
387 struct client_s *client = assuan_get_pointer(ctx);
388 gpg_error_t rc;
389 gint timeout;
391 /* New file. */
392 if (!client->crypto->fh) {
393 if (key[0])
394 goto update_cache;
396 goto done;
399 rc = try_xml_decrypt(ctx, key, client->crypto, NULL, NULL);
401 if (rc) {
402 cleanup_client(client);
403 return send_error(ctx, rc);
406 update_cache:
407 CACHE_LOCK(client->ctx);
409 if (cached == FALSE) {
410 if (cache_update_key(client->md5file, key) == FALSE) {
411 cleanup_client(client);
412 CACHE_UNLOCK;
413 return send_syserror(ctx, ENOMEM);
416 timeout = get_key_file_integer(client->filename, "cache_timeout");
417 cache_reset_timeout(client->md5file, timeout);
419 else
420 cache_set_timeout(client->md5file, -2);
422 CACHE_UNLOCK;
424 done:
425 rc = parse_xml(ctx);
427 if (client->xml) {
428 gcry_free(client->xml);
429 client->xml = NULL;
432 if (!rc) {
433 if (client->new == FALSE)
434 send_status_all(STATUS_CACHE);
436 client->state = STATE_OPEN;
439 if (!rc && client->new == FALSE &&
440 client->crypto->fh->fh2.iter != (guint)get_key_file_integer(client->filename, "iterations")) {
441 g_key_file_set_integer(keyfileh, client->filename, "iterations",
442 client->crypto->fh->fh2.iter);
443 send_status_all(STATUS_CONFIG);
446 if (!rc)
447 log_write("OPEN '%s'", client->filename);
449 cleanup_crypto(&client->crypto);
450 return send_error(ctx, rc);
453 #ifdef WITH_GNUTLS
454 static gboolean validate_access(struct client_s *cl, const gchar *filename)
456 gchar *access = get_key_file_string(filename, "tcp_access");
457 gchar **list, **p;
459 if (!access)
460 return TRUE;
462 list = g_strsplit(access, ",", -1);
463 g_free(access);
465 if (!list) {
466 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
467 return FALSE;
470 for (p = list; *p; p++) {
471 gboolean not = FALSE;
472 gchar *fp = *p;
474 if (*fp == '!') {
475 not = TRUE;
476 fp++;
478 if (!*fp)
479 break;
482 if (strcasecmp(cl->thd->tls->fp, fp) == 0) {
483 if (not == TRUE)
484 continue;
486 g_strfreev(list);
487 return TRUE;
491 /* Not allowed. */
492 g_strfreev(list);
493 return FALSE;
495 #endif
497 static void req_cleanup(void *arg)
499 if (!arg)
500 return;
502 g_strfreev((gchar **)arg);
505 static int open_command(assuan_context_t ctx, char *line)
507 gboolean cached = FALSE;
508 gpg_error_t rc;
509 struct client_s *client = assuan_get_pointer(ctx);
510 gchar **req;
511 gchar *filename = NULL;
513 if ((req = split_input_line(line, " ", 2)) != NULL)
514 filename = req[0];
516 pthread_cleanup_push(req_cleanup, req);
518 if (!filename || !*filename) {
519 g_strfreev(req);
520 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
523 if (valid_filename(filename) == FALSE) {
524 g_strfreev(req);
525 return send_error(ctx, EPWMD_INVALID_FILENAME);
528 if (client->state == STATE_OPEN)
529 cleanup_client(client);
531 #ifdef WITH_GNUTLS
532 if (client->thd->remote == TRUE) {
533 if (validate_access(client, filename) == FALSE) {
534 log_write(N_("client validation failed for file '%s'"), filename);
535 g_strfreev(req);
536 return send_error(ctx, EPWMD_FILE_ACCESS);
539 #endif
541 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
542 CACHE_LOCK(client->ctx);
544 if (cache_has_file(client->md5file) == FALSE) {
545 if (cache_add_file(client->md5file, NULL) == FALSE) {
546 g_strfreev(req);
547 CACHE_UNLOCK;
548 return send_syserror(ctx, ENOMEM);
552 cache_incr_refcount(client->md5file);
553 CACHE_UNLOCK;
554 rc = lock_file_mutex(client);
556 if (rc) {
557 g_strfreev(req);
558 return send_error(ctx, rc);
561 client->freed = FALSE;
562 client->crypto = init_client_crypto();
564 if (!client->crypto) {
565 g_strfreev(req);
566 cleanup_client(client);
567 return send_syserror(ctx, ENOMEM);
570 client->crypto->key = gcry_malloc(gcrykeysize);
572 if (!client->crypto->key) {
573 g_strfreev(req);
574 log_write("%s(%i): %s", __FUNCTION__, __LINE__,
575 gpg_error_from_errno(ENOMEM));
576 cleanup_client(client);
577 return send_syserror(ctx, ENOMEM);
580 memset(client->crypto->key, 0, gcrykeysize);
581 client->crypto->fh = read_file_header(filename, FALSE, &rc);
582 pthread_testcancel();
584 if (!client->crypto->fh) {
585 if (gpg_err_code_to_errno(rc) != ENOENT) {
586 log_write("%s: %s", filename, pwmd_strerror(rc));
587 g_strfreev(req);
588 cleanup_client(client);
589 return send_error(ctx, rc);
593 * New files don't need a key.
595 if ((client->xml = new_document()) == NULL) {
596 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
597 g_strfreev(req);
598 cleanup_client(client);
599 return send_syserror(ctx, ENOMEM);
602 client->len = xmlStrlen(client->xml);
603 client->new = TRUE;
604 client->filename = g_strdup(filename);
606 if (!client->filename) {
607 g_strfreev(req);
608 cleanup_client(client);
609 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
610 return send_syserror(ctx, ENOMEM);
613 if (req[1] && *req[1])
614 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
615 strlen(req[1]));
617 g_strfreev(req);
618 #ifdef WITH_PINENTRY
619 client->pinentry->filename = g_strdup(client->filename);
621 if (!client->pinentry->filename) {
622 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
623 cleanup_client(client);
624 return send_syserror(ctx, ENOMEM);
626 #endif
627 return open_command_finalize(ctx, client->crypto->key, cached);
629 else
630 client->mtime = client->crypto->fh->st.st_mtime;
632 client->filename = g_strdup(filename);
634 if (!client->filename) {
635 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
636 g_strfreev(req);
637 cleanup_client(client);
638 return send_syserror(ctx, ENOMEM);
641 #ifdef WITH_PINENTRY
642 client->pinentry->filename = g_strdup(client->filename);
644 if (!client->pinentry->filename) {
645 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
646 g_strfreev(req);
647 cleanup_client(client);
648 return send_syserror(ctx, ENOMEM);
650 #endif
652 if (client->crypto->fh->fh2.iter <= 0)
653 goto done;
655 #ifdef WITH_GNUTLS
656 if (client->thd->remote == FALSE ||
657 get_key_file_boolean(client->filename, "tcp_require_key") == FALSE)
659 #endif
660 CACHE_LOCK(client->ctx);
661 cached = cache_get_key(client->md5file, client->crypto->key);
662 CACHE_UNLOCK;
663 #ifdef WITH_GNUTLS
665 else
666 cached = FALSE;
667 #endif
669 if (cached == FALSE) {
670 gchar *tmp = get_key_file_string(filename, "key_file");
672 if (tmp) {
673 g_free(tmp);
674 cleanup_client(client);
675 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
679 * No key specified and no matching filename found in the cache. Use
680 * pinentry to retrieve the key. Cannot return assuan_process_done()
681 * here otherwise the command will be interrupted. The event loop in
682 * client_thread() will poll the file descriptor waiting for it to
683 * become ready to read a pinentry_key_s which will contain the
684 * entered key or an error code. It will then call
685 * open_command_finalize() to to finish the command.
687 if (!req[1] || !*req[1]) {
688 #ifdef WITH_PINENTRY
689 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
691 /* From set_pinentry_defaults(). */
692 if (client->pinentry->enable == FALSE ||
693 (client->pinentry->enable == -1 && b == FALSE)) {
694 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
695 goto done;
698 g_strfreev(req);
699 rc = lock_pin_mutex(client);
701 if (rc) {
702 unlock_pin_mutex(client->pinentry);
703 cleanup_client(client);
704 return send_error(ctx, rc);
707 client->pinentry->which = PINENTRY_OPEN;
708 rc = pinentry_fork(ctx);
710 if (rc) {
711 unlock_pin_mutex(client->pinentry);
712 cleanup_client(client);
713 return send_error(ctx, rc);
716 // Called from pinentry iterate.
717 client->pinentry->cb = open_command_finalize;
718 client->pinentry->status = PINENTRY_INIT;
719 return 0;
720 #else
721 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
722 goto done;
723 #endif
726 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
727 strlen(req[1]));
730 done:
731 if (1) {}
732 pthread_cleanup_pop(1);
733 return open_command_finalize(ctx, client->crypto->key, cached);
736 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
737 gulong size, gpointer *out, gulong *outsize, gint *rc)
739 z_stream z;
740 gpointer pout, pin;
741 gz_header h;
742 gchar buf[17];
743 gint cmd = Z_NO_FLUSH;
745 z.zalloc = z_alloc;
746 z.zfree = z_free;
747 z.next_in = pin = data;
748 z.avail_in = size < zlib_bufsize ? (uInt)size : (uInt)zlib_bufsize;
749 z.avail_out = (uInt)zlib_bufsize;
750 z.next_out = pout = gcry_malloc(zlib_bufsize);
752 if (!pout) {
753 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
754 *rc = Z_MEM_ERROR;
755 return FALSE;
758 *rc = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
760 if (*rc != Z_OK) {
761 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
762 gcry_free(pout);
763 return FALSE;
766 /* Rather than store the size of the uncompressed data in the file header,
767 * store it in the comment field of the gzip header. Don't give anyone too
768 * much information. Not sure why really, but it seems the right way. :)
770 memset(&h, 0, sizeof(gz_header));
771 g_snprintf(buf, sizeof(buf), "%li", size);
772 h.comment = (guchar *)buf;
773 *rc = deflateSetHeader(&z, &h);
775 if (*rc != Z_OK) {
776 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
777 gcry_free(pout);
778 deflateEnd(&z);
779 return FALSE;
782 do {
783 gpointer p;
785 *rc = deflate(&z, cmd);
787 switch (*rc) {
788 case Z_OK:
789 break;
790 case Z_BUF_ERROR:
791 if (!z.avail_out) {
792 p = gcry_realloc(pout, z.total_out + zlib_bufsize);
794 if (!p) {
795 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
796 *rc = Z_MEM_ERROR;
797 goto fail;
800 pout = p;
801 z.next_out = pout + z.total_out;
802 z.avail_out = zlib_bufsize;
805 if (!z.avail_in && z.total_in < size) {
806 if (z.total_in + zlib_bufsize > size)
807 z.avail_in = size - z.total_in;
808 else
809 z.avail_in = zlib_bufsize;
811 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li",
812 z.total_in, size);
814 if (*rc)
815 goto fail;
818 if (z.total_in >= size)
819 cmd = Z_FINISH;
821 break;
822 case Z_STREAM_END:
823 break;
824 default:
825 goto fail;
827 } while (*rc != Z_STREAM_END);
829 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li", z.total_in, size);
831 if (*rc)
832 goto fail;
834 *out = pout;
835 *outsize = z.total_out;
836 deflateEnd(&z);
837 *rc = 0;
838 return TRUE;
840 fail:
841 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
842 gcry_free(pout);
843 deflateEnd(&z);
844 return FALSE;
847 #define CRYPTO_BLOCKSIZE (gcryblocksize * 1024)
849 static gpg_error_t iterate_crypto_once(struct client_s *client,
850 struct client_crypto_s *crypto, status_msg_t which)
852 gpg_error_t rc = 0;
853 gsize len = CRYPTO_BLOCKSIZE;
854 gpointer p = gcry_malloc(len);
855 gsize total = 0;
856 gpointer inbuf;
858 if (!p)
859 return gpg_err_code_from_errno(ENOMEM);
861 pthread_cleanup_push(gcry_free, p);
863 if (crypto->insize < CRYPTO_BLOCKSIZE)
864 len = crypto->insize;
866 for (;;) {
867 inbuf = crypto->inbuf + total;
868 guchar *tmp;
870 if (len + total > crypto->insize)
871 len = gcryblocksize;
873 if (which == STATUS_ENCRYPT)
874 rc = gcry_cipher_encrypt(crypto->gh, p, len, inbuf, len);
875 else
876 rc = gcry_cipher_decrypt(crypto->gh, p, len, inbuf, len);
878 if (rc)
879 goto done;
881 tmp = crypto->inbuf+total;
882 memmove(tmp, p, len);
883 total += len;
885 if (total >= crypto->insize)
886 break;
888 pthread_testcancel();
891 done:
892 if (1) {}
893 pthread_cleanup_pop(1);
894 return rc;
897 /* The crypto struct must be setup for iterations and .key. */
898 gpg_error_t do_xml_encrypt(struct client_s *client,
899 struct client_crypto_s *crypto, const gchar *filename)
901 gsize len = crypto->insize;
902 gpointer inbuf;
903 gchar *p;
904 gpg_error_t rc;
905 guint iter_progress = 0, n_iter = 0, xiter = 0;
906 gchar tmp[FILENAME_MAX];
907 struct stat st;
908 mode_t mode = 0;
910 if (!crypto->fh->fh2.iter) {
912 * cache_file_count() needs both .used == TRUE and a valid key in
913 * order for it to count as a used cache entry. Fixes CACHE status
914 * messages.
916 memset(crypto->key, '!', gcrykeysize);
917 goto write_file;
921 * Resize the existing xml buffer to the block size required by gcrypt
922 * rather than duplicating it and wasting memory.
924 if (crypto->insize / gcryblocksize) {
925 len = (crypto->insize / gcryblocksize) * gcryblocksize;
927 if (crypto->insize % gcryblocksize)
928 len += gcryblocksize;
931 inbuf = gcry_realloc(crypto->inbuf, len);
933 if (!inbuf) {
934 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
935 return gpg_error_from_errno(ENOMEM);
938 crypto->inbuf = inbuf;
939 crypto->insize = len;
940 gcry_create_nonce(crypto->fh->fh2.iv, sizeof(crypto->fh->fh2.iv));
941 crypto->tkey = gcry_malloc(gcrykeysize);
943 if (!crypto->tkey) {
944 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
945 return gpg_error_from_errno(ENOMEM);
948 memcpy(crypto->tkey, crypto->key, gcrykeysize);
949 guchar *tkey = crypto->tkey;
950 tkey[0] ^= 1;
952 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
953 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
954 return rc;
957 iter_progress = get_key_file_integer(client ? client->filename : "global",
958 "iteration_progress");
960 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
961 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
962 "%u %u", 0, crypto->fh->fh2.iter);
964 if (rc)
965 return rc;
968 while (xiter < crypto->fh->fh2.iter-1) {
969 if (iter_progress > 0 && xiter >= iter_progress) {
970 if (!(xiter % iter_progress)) {
971 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
972 "%u %u", ++n_iter * iter_progress, crypto->fh->fh2.iter);
974 if (rc)
975 return rc;
979 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
980 sizeof(crypto->fh->fh2.iv)))) {
981 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
982 return rc;
985 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
987 if (rc) {
988 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
989 return rc;
992 xiter++;
995 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
996 sizeof(crypto->fh->fh2.iv)))) {
997 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
998 return rc;
1001 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, gcrykeysize))) {
1002 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1003 return rc;
1006 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1008 if (rc)
1009 return rc;
1011 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
1012 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1013 "%u %u", crypto->fh->fh2.iter, crypto->fh->fh2.iter);
1015 if (rc)
1016 return rc;
1019 write_file:
1020 if (filename) {
1021 if (!client && !strcmp(filename, "-")) {
1022 crypto->fh->fd = STDOUT_FILENO;
1023 goto do_write_file;
1026 if (lstat(filename, &st) == 0) {
1027 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
1030 * FIXME What if the file has an ACL?
1032 if (!(mode & S_IWUSR))
1033 return gpg_error_from_errno(EACCES);
1035 else {
1036 if (errno != ENOENT)
1037 return gpg_error_from_errno(errno);
1040 g_snprintf(tmp, sizeof(tmp), "%s.XXXXXX", filename);
1041 crypto->fh->fd = mkstemp(tmp);
1043 if (crypto->fh->fd == -1) {
1044 rc = errno;
1045 p = strrchr(tmp, '/');
1046 p++;
1047 log_write("%s: %s", p, strerror(rc));
1048 return gpg_error_from_errno(rc);
1051 else
1053 * xml_import() or convert_file() from command line.
1055 crypto->fh->fd = STDOUT_FILENO;
1057 do_write_file:
1058 crypto->fh->fh2.version = VERSION_HEX;
1059 len = write(crypto->fh->fd, &crypto->fh->fh2, sizeof(crypto->fh->fh2));
1061 if (len != sizeof(crypto->fh->fh2)) {
1062 len = errno;
1064 if (filename && strcmp(filename, "-"))
1065 unlink(tmp);
1067 return gpg_error_from_errno(len);
1070 len = write(crypto->fh->fd, crypto->inbuf, crypto->insize);
1072 if (len != crypto->insize) {
1073 len = errno;
1075 if (filename && strcmp(filename, "-"))
1076 unlink(tmp);
1078 return gpg_error_from_errno(len);
1081 if (fsync(crypto->fh->fd) == -1) {
1082 len = errno;
1084 if (filename && strcmp(filename, "-"))
1085 unlink(tmp);
1087 return gpg_error_from_errno(len);
1090 if (filename && strcmp(filename, "-")) {
1091 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
1092 gchar tmp2[FILENAME_MAX];
1094 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
1096 if (rename(filename, tmp2) == -1) {
1097 unlink(tmp);
1098 len = errno;
1099 return gpg_error_from_errno(len);
1103 if (rename(tmp, filename) == -1) {
1104 len = errno;
1105 unlink(tmp);
1106 return gpg_error_from_errno(len);
1109 if (mode)
1110 chmod(filename, mode);
1113 if (client && lstat(filename, &st) == 0)
1114 client->mtime = st.st_mtime;
1116 return 0;
1119 static gpg_error_t save_command_finalize(assuan_context_t ctx, guchar *key,
1120 gboolean cached)
1122 struct client_s *client = assuan_get_pointer(ctx);
1123 gpointer xmlbuf;
1124 gulong len, outsize = 0;
1125 guint iter;
1126 gint timeout;
1127 gpointer outbuf;
1128 gint zrc;
1129 gpg_error_t rc;
1131 if (client->crypto->key && client->crypto->key != key)
1132 gcry_free(client->crypto->key);
1134 client->crypto->key = key;
1135 xmlDocDumpFormatMemory(client->doc, (xmlChar **)&xmlbuf, (gint *)&len, 0);
1136 iter = (guint)get_key_file_integer(client->filename, "compression_level");
1138 if (iter < 0)
1139 iter = 0;
1141 if (do_compress(ctx, (gint)iter, xmlbuf, len, &outbuf, &outsize, &zrc)
1142 == FALSE) {
1143 xmlFree(xmlbuf);
1144 cleanup_crypto(&client->crypto);
1146 if (zrc == Z_MEM_ERROR)
1147 return send_syserror(ctx, ENOMEM);
1148 else
1149 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1151 else {
1152 gcry_free(xmlbuf);
1153 xmlbuf = outbuf;
1154 len = outsize;
1157 client->crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1159 if (!client->crypto->fh) {
1160 cleanup_crypto(&client->crypto);
1161 gcry_free(outbuf);
1162 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1163 return send_syserror(ctx, ENOMEM);
1166 iter = get_key_file_integer(client->filename, "iterations");
1167 client->crypto->fh->fh2.iter = iter < 0 ? 0 : iter;
1168 client->crypto->inbuf = xmlbuf;
1169 client->crypto->insize = len;
1170 rc = do_xml_encrypt(client, client->crypto, client->filename);
1172 if (rc) {
1173 cleanup_crypto(&client->crypto);
1174 return send_error(ctx, rc);
1177 timeout = get_key_file_integer(client->filename, "cache_timeout");
1178 CACHE_LOCK(client->ctx);
1180 if (cached) {
1181 cache_reset_timeout(client->md5file, timeout);
1182 CACHE_UNLOCK;
1184 if (client->new == TRUE)
1185 send_status_all(STATUS_CACHE);
1187 client->new = FALSE;
1188 cleanup_crypto(&client->crypto);
1189 return send_error(ctx, 0);
1192 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1193 CACHE_UNLOCK;
1194 cleanup_crypto(&client->crypto);
1195 return send_syserror(ctx, ENOMEM);
1198 client->new = FALSE;
1199 cache_reset_timeout(client->md5file, timeout);
1200 CACHE_UNLOCK;
1201 send_status_all(STATUS_CACHE);
1202 cleanup_crypto(&client->crypto);
1203 return send_error(ctx, 0);
1206 static int save_command(assuan_context_t ctx, char *line)
1208 gboolean cached = FALSE;
1209 struct stat st;
1210 struct client_s *client = assuan_get_pointer(ctx);
1211 gpg_error_t rc;
1213 rc = lock_file_mutex(client);
1215 if (rc)
1216 return send_error(ctx, rc);
1218 rc = file_modified(client);
1220 if (rc)
1221 return send_error(ctx, rc);
1223 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1224 return send_syserror(ctx, errno);
1226 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1227 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1228 return send_error(ctx, GPG_ERR_ENOANO);
1231 CACHE_LOCK(ctx);
1232 cached = cache_iscached(client->md5file);
1233 CACHE_UNLOCK;
1236 * If a cache entry doesn't exist for this file and the file has a
1237 * "key_file" or "key" parameter, then it's an error. The reason is that
1238 * cache expiration would be useless.
1240 if (cached == FALSE) {
1241 gchar *tmp = get_key_file_string(client->filename, "key_file");
1243 if (tmp) {
1244 g_free(tmp);
1245 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1249 cached = FALSE;
1250 client->crypto = init_client_crypto();
1252 if (!client->crypto) {
1253 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1254 return send_syserror(ctx, ENOMEM);
1257 client->crypto->key = gcry_malloc(gcrykeysize);
1259 if (!client->crypto->key) {
1260 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1261 cleanup_crypto(&client->crypto);
1262 return send_syserror(ctx, ENOMEM);
1265 memset(client->crypto->key, '!', gcrykeysize);
1267 if (get_key_file_integer(client->filename, "iterations") <= 0)
1268 goto done;
1270 if (!line || !*line) {
1271 client->crypto->tkey = gcry_malloc(gcrykeysize);
1273 if (!client->crypto->tkey) {
1274 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1275 cleanup_crypto(&client->crypto);
1276 return send_syserror(ctx, ENOMEM);
1279 memset(client->crypto->tkey, '!', gcrykeysize);
1280 CACHE_LOCK(ctx);
1282 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1283 memcmp(client->crypto->key, client->crypto->tkey,
1284 gcrykeysize) == 0) {
1285 CACHE_UNLOCK;
1287 #ifdef WITH_PINENTRY
1288 if (client->pinentry->enable == FALSE ||
1289 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1290 /* Empty keys are allowed. */
1291 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1292 goto done;
1295 lock_pin_mutex(client);
1296 client->pinentry->which = PINENTRY_SAVE;
1297 rc = pinentry_fork(ctx);
1299 if (rc) {
1300 unlock_pin_mutex(client->pinentry);
1301 return send_error(ctx, rc);
1304 client->pinentry->cb = save_command_finalize;
1305 client->pinentry->status = PINENTRY_INIT;
1306 return 0;
1307 #else
1308 /* Empty keys are allowed. */
1309 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1310 goto done;
1311 #endif
1313 else {
1314 CACHE_UNLOCK;
1315 cached = TRUE;
1318 else {
1319 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, line,
1320 strlen(line));
1321 memset(line, 0, strlen(line));
1324 done:
1325 return save_command_finalize(ctx, client->crypto->key, cached);
1328 static int delete_command(assuan_context_t ctx, char *line)
1330 struct client_s *client = assuan_get_pointer(ctx);
1331 gchar **req;
1332 gpg_error_t rc;
1333 xmlNodePtr n;
1335 rc = file_modified(client);
1337 if (rc)
1338 return send_error(ctx, rc);
1340 if (strchr(line, '\t'))
1341 req = split_input_line(line, "\t", -1);
1342 else
1343 req = split_input_line(line, " ", -1);
1345 if (!req || !*req)
1346 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1348 n = find_account(client->doc, &req, &rc, NULL, 0);
1350 if (!n) {
1351 g_strfreev(req);
1352 return send_error(ctx, rc);
1356 * No sub-node defined. Remove the entire node (account).
1358 if (!req[1]) {
1359 if (n) {
1360 xmlUnlinkNode(n);
1361 xmlFreeNode(n);
1364 g_strfreev(req);
1365 return send_error(ctx, 0);
1368 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1369 g_strfreev(req);
1371 if (!n)
1372 return send_error(ctx, rc);
1374 if (n) {
1375 xmlUnlinkNode(n);
1376 xmlFreeNode(n);
1379 return send_error(ctx, 0);
1383 * Don't return with assuan_process_done() here. This has been called from
1384 * assuan_process_next() and the command should be finished in
1385 * client_thread().
1387 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1388 gsize len)
1390 assuan_context_t ctx = data;
1391 struct client_s *client = assuan_get_pointer(ctx);
1392 gchar **req;
1393 xmlNodePtr n;
1394 gpg_error_t rc = file_modified(client);
1396 if (assuan_rc || rc) {
1397 if (line)
1398 xfree(line);
1399 return assuan_rc ? assuan_rc : rc;
1402 req = split_input_line((gchar *)line, "\t", 0);
1403 xfree(line);
1405 if (!req || !*req)
1406 return EPWMD_COMMAND_SYNTAX;
1408 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1409 g_strfreev(req);
1410 return EPWMD_INVALID_ELEMENT;
1413 if (valid_element_path(req+1, TRUE) == FALSE) {
1414 g_strfreev(req);
1415 return EPWMD_INVALID_ELEMENT;
1418 again:
1419 n = find_account(client->doc, &req, &rc, NULL, 0);
1421 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1422 rc = new_account(client->doc, *req);
1424 if (rc) {
1425 g_strfreev(req);
1426 return rc;
1429 goto again;
1432 if (!n) {
1433 g_strfreev(req);
1434 return rc;
1437 if (req[1]) {
1438 if (!n->children)
1439 create_elements_cb(n, req+1, &rc, NULL);
1440 else
1441 find_elements(client->doc, n->children, req+1, &rc,
1442 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1445 g_strfreev(req);
1446 client->inquire_status = INQUIRE_DONE;
1447 return rc;
1450 static int store_command(assuan_context_t ctx, char *line)
1452 struct client_s *client = assuan_get_pointer(ctx);
1453 gpg_error_t rc = file_modified(client);
1455 if (rc)
1456 return send_error(ctx, rc);
1458 pthread_testcancel();
1459 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1460 pthread_testcancel();
1462 if (rc)
1463 return send_error(ctx, rc);
1465 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1466 client->inquire_status = INQUIRE_BUSY;
1467 return 0;
1470 static int get_command(assuan_context_t ctx, char *line)
1472 struct client_s *client = assuan_get_pointer(ctx);
1473 gchar **req;
1474 gpg_error_t rc;
1475 xmlNodePtr n;
1477 rc = file_modified(client);
1479 if (rc)
1480 return send_error(ctx, rc);
1482 req = split_input_line(line, "\t", -1);
1484 if (!req || !*req) {
1485 g_strfreev(req);
1486 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1489 n = find_account(client->doc, &req, &rc, NULL, 0);
1491 if (!n) {
1492 g_strfreev(req);
1493 return send_error(ctx, rc);
1496 if (req[1])
1497 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1499 g_strfreev(req);
1501 if (rc)
1502 return send_error(ctx, rc);
1504 if (!n || !n->children)
1505 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1507 n = find_text_node(n->children);
1509 if (!n || !n->content || !*n->content)
1510 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1512 pthread_testcancel();
1513 rc = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1514 pthread_testcancel();
1515 return send_error(ctx, rc);
1518 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1519 gpg_error_t *rc, gchar **req_orig, void *data)
1521 gchar *path = *(gchar **)data;
1522 gchar *tmp = NULL, *result;
1524 if (path) {
1525 g_free(path);
1526 *(gchar **)data = NULL;
1529 path = g_strjoinv("\t", target);
1531 if (!path) {
1532 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1533 *rc = gpg_error_from_errno(ENOMEM);
1534 return NULL;
1537 if (req_orig) {
1538 tmp = g_strjoinv("\t", req_orig);
1540 if (!tmp) {
1541 g_free(path);
1542 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1543 *rc = gpg_error_from_errno(ENOMEM);
1544 return NULL;
1548 if (tmp && *tmp)
1549 result = g_strdup_printf("%s\t%s", path, tmp);
1550 else
1551 result = g_strdup(path);
1553 if (!result) {
1554 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1555 *rc = gpg_error_from_errno(ENOMEM);
1556 g_free(path);
1557 g_free(tmp);
1558 return NULL;
1561 g_free(path);
1562 g_free(tmp);
1563 *(gchar **)data = result;
1564 return node;
1567 static void list_command_cleanup1(void *arg);
1568 static int realpath_command(assuan_context_t ctx, char *line)
1570 gpg_error_t rc;
1571 struct client_s *client = assuan_get_pointer(ctx);
1572 gchar **req;
1573 gchar *t;
1574 gint i;
1575 xmlNodePtr n;
1576 GString *string;
1577 gchar *rp = NULL;
1579 rc = file_modified(client);
1581 if (rc)
1582 return send_error(ctx, rc);
1584 if (strchr(line, '\t') != NULL) {
1585 if ((req = split_input_line(line, "\t", 0)) == NULL)
1586 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1588 else {
1589 if ((req = split_input_line(line, " ", 0)) == NULL)
1590 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1593 n = find_account(client->doc, &req, &rc, NULL, 0);
1595 if (!n) {
1596 g_strfreev(req);
1597 return send_error(ctx, rc);
1600 rp = g_strjoinv("\t", req);
1602 if (!rp) {
1603 g_strfreev(req);
1604 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1605 return send_syserror(ctx, ENOMEM);
1608 if (req[1]) {
1609 n = find_elements(client->doc, n->children, req+1, &rc,
1610 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1612 if (!n) {
1613 g_free(rp);
1614 g_strfreev(req);
1615 return send_error(ctx, rc);
1619 string = g_string_new(rp);
1620 g_free(rp);
1621 g_strfreev(req);
1623 if (!string) {
1624 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1625 return send_syserror(ctx, ENOMEM);
1628 again:
1629 for (i = 0, t = string->str + i; *t; t++, i++) {
1630 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1631 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1632 goto again;
1636 pthread_cleanup_push(list_command_cleanup1, string);
1637 pthread_testcancel();
1638 rc = assuan_send_data(ctx, string->str, string->len);
1639 pthread_testcancel();
1640 pthread_cleanup_pop(1);
1641 return send_error(ctx, rc);
1644 static void list_command_cleanup1(void *arg)
1646 g_string_free((GString *)arg, TRUE);
1649 static void list_command_cleanup2(void *arg)
1651 struct element_list_s *elements = arg;
1653 if (elements) {
1654 gint total = g_slist_length(elements->list);
1655 gint i;
1657 for (i = 0; i < total; i++) {
1658 gchar *tmp = g_slist_nth_data(elements->list, i);
1659 g_free(tmp);
1662 g_slist_free(elements->list);
1664 if (elements->prefix)
1665 g_free(elements->prefix);
1667 g_free(elements);
1671 static int list_command(assuan_context_t ctx, char *line)
1673 struct client_s *client = assuan_get_pointer(ctx);
1674 gpg_error_t rc;
1675 struct element_list_s *elements = NULL;
1676 gchar *tmp;
1678 if (disable_list_and_dump == TRUE)
1679 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1681 rc = file_modified(client);
1683 if (rc)
1684 return send_error(ctx, rc);
1686 if (!*line) {
1687 GString *str;
1689 rc = list_accounts(client->doc, &str);
1690 pthread_cleanup_push(list_command_cleanup1, str);
1692 if (rc)
1693 return send_error(ctx, rc);
1695 pthread_testcancel();
1696 rc = assuan_send_data(ctx, str->str, str->len);
1697 pthread_testcancel();
1698 pthread_cleanup_pop(1);
1699 return send_error(ctx, rc);
1702 elements = g_malloc0(sizeof(struct element_list_s));
1704 if (!elements) {
1705 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1706 rc = gpg_err_code_from_errno(ENOMEM);
1707 goto fail;
1710 pthread_cleanup_push(list_command_cleanup2, elements);
1711 pthread_testcancel();
1712 rc = create_path_list(client->doc, elements, line);
1713 pthread_testcancel();
1715 if (rc)
1716 goto fail;
1718 if (elements) {
1719 gint total = g_slist_length(elements->list);
1720 gint i;
1721 GString *str;
1723 if (!total) {
1724 rc = EPWMD_EMPTY_ELEMENT;
1725 goto fail;
1728 str = g_string_new(NULL);
1730 if (!str) {
1731 rc = gpg_err_code_from_errno(ENOMEM);
1732 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1733 goto fail;
1736 pthread_cleanup_push(list_command_cleanup1, str);
1738 for (i = 0; i < total; i++) {
1739 tmp = g_slist_nth_data(elements->list, i);
1740 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1743 pthread_testcancel();
1744 rc = assuan_send_data(ctx, str->str, str->len);
1745 pthread_testcancel();
1746 pthread_cleanup_pop(1);
1748 else
1749 rc = EPWMD_EMPTY_ELEMENT;
1751 fail:
1752 if (1) {}
1753 pthread_cleanup_pop(1);
1754 return send_error(ctx, rc);
1757 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1758 const gchar *value)
1760 xmlAttrPtr a;
1762 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1763 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1765 if (!a)
1766 return EPWMD_LIBXML_ERROR;
1768 else
1769 xmlNodeSetContent(a->children, (xmlChar *)value);
1771 return 0;
1775 * req[0] - element path
1777 static int attribute_list(assuan_context_t ctx, gchar **req)
1779 struct client_s *client = assuan_get_pointer(ctx);
1780 gchar **attrlist = NULL;
1781 gint i = 0;
1782 gchar **path = NULL;
1783 xmlAttrPtr a;
1784 xmlNodePtr n, an;
1785 gchar *line;
1786 gpg_error_t rc;
1788 if (!req || !req[0])
1789 return EPWMD_COMMAND_SYNTAX;
1791 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1793 * The first argument may be only an account.
1795 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1796 return EPWMD_COMMAND_SYNTAX;
1799 n = find_account(client->doc, &path, &rc, NULL, 0);
1801 if (!n) {
1802 g_strfreev(path);
1803 return rc;
1806 if (path[1]) {
1807 n = find_elements(client->doc, n->children, path+1, &rc,
1808 NULL, NULL, NULL, FALSE, 0, NULL);
1810 if (!n) {
1811 g_strfreev(path);
1812 return rc;
1816 g_strfreev(path);
1818 for (a = n->properties; a; a = a->next) {
1819 gchar **pa;
1821 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1822 if (attrlist)
1823 g_strfreev(attrlist);
1825 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1826 return gpg_error_from_errno(ENOMEM);
1829 attrlist = pa;
1830 an = a->children;
1831 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
1833 if (!attrlist[i]) {
1834 g_strfreev(attrlist);
1835 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1836 return gpg_error_from_errno(ENOMEM);
1839 attrlist[++i] = NULL;
1842 if (!attrlist)
1843 return EPWMD_EMPTY_ELEMENT;
1845 line = g_strjoinv("\n", attrlist);
1847 if (!line) {
1848 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1849 g_strfreev(attrlist);
1850 return gpg_error_from_errno(ENOMEM);
1853 pthread_cleanup_push(g_free, line);
1854 pthread_cleanup_push(req_cleanup, attrlist);
1855 pthread_testcancel();
1856 rc = assuan_send_data(ctx, line, strlen(line));
1857 pthread_testcancel();
1858 pthread_cleanup_pop(1);
1859 pthread_cleanup_pop(1);
1860 return rc;
1864 * req[0] - attribute
1865 * req[1] - element path
1867 static int attribute_delete(struct client_s *client, gchar **req)
1869 xmlAttrPtr a;
1870 xmlNodePtr n;
1871 gchar **path = NULL;
1872 gpg_error_t rc;
1874 if (!req || !req[0] || !req[1])
1875 return EPWMD_COMMAND_SYNTAX;
1877 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1879 * The first argument may be only an account.
1881 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1882 return EPWMD_COMMAND_SYNTAX;
1886 * Don't remove the "name" attribute for the account element. To remove an
1887 * account use DELETE <account>.
1889 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1890 rc = EPWMD_ATTR_SYNTAX;
1891 goto fail;
1894 n = find_account(client->doc, &path, &rc, NULL, 0);
1896 if (!n)
1897 goto fail;
1899 if (path[1]) {
1900 n = find_elements(client->doc, n->children, path+1, &rc,
1901 NULL, NULL, NULL, FALSE, 0, NULL);
1903 if (!n)
1904 goto fail;
1907 g_strfreev(path);
1909 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1910 return EPWMD_ATTR_NOT_FOUND;
1912 if (xmlRemoveProp(a) == -1)
1913 return EPWMD_LIBXML_ERROR;
1915 return 0;
1917 fail:
1918 g_strfreev(path);
1919 return rc;
1922 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
1923 gpg_error_t *rc)
1925 gchar **src = *path;
1926 gchar **src_orig = g_strdupv(src);
1927 xmlNodePtr n = NULL;
1929 *rc = 0;
1931 if (!src_orig) {
1932 *rc = gpg_error_from_errno(ENOMEM);
1933 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1934 goto fail;
1937 again:
1938 n = find_account(client->doc, &src, rc, NULL, 0);
1940 if (!n) {
1941 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
1942 *rc = new_account(client->doc, src[0]);
1944 if (*rc)
1945 goto fail;
1947 goto again;
1949 else
1950 goto fail;
1953 if (src[1]) {
1954 if (!n->children)
1955 n = create_target_elements_cb(n, src+1, rc, NULL);
1956 else
1957 n = find_elements(client->doc, n->children, src+1, rc,
1958 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
1960 if (!n)
1961 goto fail;
1964 * Reset the position of the element tree now that the elements
1965 * have been created.
1967 g_strfreev(src);
1968 src = src_orig;
1969 src_orig = NULL;
1970 n = find_account(client->doc, &src, rc, NULL, 0);
1972 if (!n)
1973 goto fail;
1975 n = find_elements(client->doc, n->children, src+1, rc,
1976 NULL, NULL, NULL, FALSE, 0, NULL);
1978 if (!n)
1979 goto fail;
1982 fail:
1983 if (src_orig)
1984 g_strfreev(src_orig);
1986 *path = src;
1987 return n;
1991 * Creates a "target" attribute. When other commands encounter an element with
1992 * this attribute, the element path is modified to the target value. If the
1993 * source element path doesn't exist when using 'ATTR SET target', it is
1994 * created, but the destination element path must exist.
1996 * req[0] - source element path
1997 * req[1] - destination element path
1999 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2001 gchar **src, **dst, *line = NULL;
2002 gpg_error_t rc;
2003 xmlNodePtr n;
2005 if (!req || !req[0] || !req[1])
2006 return EPWMD_COMMAND_SYNTAX;
2008 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2010 * The first argument may be only an account.
2012 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2013 return EPWMD_COMMAND_SYNTAX;
2016 if (valid_element_path(src, FALSE) == FALSE) {
2017 g_strfreev(src);
2018 return EPWMD_INVALID_ELEMENT;
2021 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2023 * The first argument may be only an account.
2025 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2026 rc = EPWMD_COMMAND_SYNTAX;
2027 goto fail;
2031 n = find_account(client->doc, &dst, &rc, NULL, 0);
2034 * Make sure the destination element path exists.
2036 if (!n)
2037 goto fail;
2039 if (dst[1]) {
2040 n = find_elements(client->doc, n->children, dst+1, &rc,
2041 NULL, NULL, NULL, FALSE, 0, NULL);
2043 if (!n)
2044 goto fail;
2047 n = create_element_path(client, &src, &rc);
2049 if (rc)
2050 goto fail;
2052 line = g_strjoinv("\t", dst);
2054 if (!line) {
2055 rc = gpg_error_from_errno(ENOMEM);
2056 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2057 goto fail;
2060 rc = add_attribute(n, "target", line);
2062 fail:
2063 g_free(line);
2064 g_strfreev(src);
2065 g_strfreev(dst);
2066 return rc;
2070 * req[0] - account name
2071 * req[1] - new name
2073 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2075 gpg_error_t rc;
2076 gchar **tmp;
2077 xmlNodePtr n;
2079 tmp = g_strdupv(req);
2081 if (!tmp) {
2082 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2083 return gpg_error_from_errno(ENOMEM);
2086 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2087 g_strfreev(tmp);
2089 if (!n)
2090 return rc;
2092 if (g_utf8_collate(req[0], req[1]) == 0)
2093 return 0;
2096 * Will not overwrite an existing account.
2098 tmp = g_strdupv(req+1);
2100 if (!tmp) {
2101 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2102 return gpg_error_from_errno(ENOMEM);
2105 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2106 g_strfreev(tmp);
2108 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
2109 return rc;
2111 if (n)
2112 return EPWMD_ACCOUNT_EXISTS;
2115 * Whitespace not allowed in account names.
2117 if (contains_whitespace(req[1]) == TRUE)
2118 return EPWMD_ATTR_SYNTAX;
2120 tmp = g_strdupv(req);
2122 if (!tmp) {
2123 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2124 return gpg_error_from_errno(ENOMEM);
2127 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2128 g_strfreev(tmp);
2130 if (!n)
2131 return EPWMD_ELEMENT_NOT_FOUND;
2133 return add_attribute(n, "name", req[1]);
2137 * req[0] - attribute
2138 * req[1] - element path
2140 static int attribute_get(assuan_context_t ctx, gchar **req)
2142 struct client_s *client = assuan_get_pointer(ctx);
2143 xmlNodePtr n;
2144 xmlChar *a;
2145 gchar **path= NULL;
2146 gpg_error_t rc;
2148 if (!req || !req[0] || !req[1])
2149 return EPWMD_COMMAND_SYNTAX;
2151 if (strchr(req[1], '\t')) {
2152 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2153 return EPWMD_COMMAND_SYNTAX;
2155 else {
2156 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2157 return EPWMD_COMMAND_SYNTAX;
2160 n = find_account(client->doc, &path, &rc, NULL, 0);
2162 if (!n)
2163 goto fail;
2165 if (path[1]) {
2166 n = find_elements(client->doc, n->children, path+1, &rc,
2167 NULL, NULL, NULL, FALSE, 0, NULL);
2169 if (!n)
2170 goto fail;
2173 g_strfreev(path);
2175 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2176 return EPWMD_ATTR_NOT_FOUND;
2178 pthread_cleanup_push(xmlFree, a);
2179 pthread_testcancel();
2180 rc = assuan_send_data(ctx, a, xmlStrlen(a));
2181 pthread_testcancel();
2182 pthread_cleanup_pop(1);
2183 return rc;
2185 fail:
2186 g_strfreev(path);
2187 return rc;
2191 * req[0] - attribute
2192 * req[1] - element path
2193 * req[2] - value
2195 static int attribute_set(struct client_s *client, gchar **req)
2197 gchar **path = NULL;
2198 gpg_error_t rc;
2199 xmlNodePtr n;
2201 if (!req || !req[0] || !req[1] || !req[2])
2202 return EPWMD_COMMAND_SYNTAX;
2205 * Reserved attribute names.
2207 if (g_utf8_collate(req[0], "name") == 0) {
2209 * Only reserved for the account element. Not the rest of the
2210 * document.
2212 if (strchr(req[1], '\t') == NULL)
2213 return name_attribute(client, req + 1);
2215 else if (g_utf8_collate(req[0], "target") == 0)
2216 return target_attribute(client, req + 1);
2218 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2220 * The first argument may be only an account.
2222 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2223 return EPWMD_COMMAND_SYNTAX;
2226 n = find_account(client->doc, &path, &rc, NULL, 0);
2228 if (!n)
2229 goto fail;
2231 if (path[1]) {
2232 n = find_elements(client->doc, n->children, path+1, &rc,
2233 NULL, NULL, NULL, FALSE, 0, NULL);
2235 if (!n)
2236 goto fail;
2239 g_strfreev(path);
2240 return add_attribute(n, req[0], req[2]);
2242 fail:
2243 g_strfreev(path);
2244 return rc;
2248 * req[0] - command
2249 * req[1] - attribute name or element path if command is LIST
2250 * req[2] - element path
2251 * req[2] - element path or value
2253 static int attr_command(assuan_context_t ctx, char *line)
2255 struct client_s *client = assuan_get_pointer(ctx);
2256 gchar **req;
2257 gpg_error_t rc = 0;
2259 rc = file_modified(client);
2261 if (rc)
2262 return send_error(ctx, rc);
2264 req = split_input_line(line, " ", 4);
2266 if (!req || !req[0] || !req[1]) {
2267 g_strfreev(req);
2268 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2271 pthread_cleanup_push(req_cleanup, req);
2273 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2274 rc = attribute_set(client, req+1);
2275 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2276 rc = attribute_get(ctx, req+1);
2277 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2278 rc = attribute_delete(client, req+1);
2279 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2280 rc = attribute_list(ctx, req+1);
2281 else
2282 rc = EPWMD_COMMAND_SYNTAX;
2284 pthread_cleanup_pop(1);
2285 return send_error(ctx, rc);
2288 static int iscached_command(assuan_context_t ctx, char *line)
2290 gchar **req = split_input_line(line, " ", 0);
2291 guchar md5file[16];
2293 if (!req || !*req) {
2294 g_strfreev(req);
2295 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2298 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2299 g_strfreev(req);
2300 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2301 CACHE_LOCK(ctx);
2303 if (cache_iscached(md5file) == FALSE) {
2304 CACHE_UNLOCK;
2305 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2308 CACHE_UNLOCK;
2309 pthread_cleanup_pop(0);
2310 return send_error(ctx, 0);
2313 static int clearcache_command(assuan_context_t ctx, char *line)
2315 struct client_s *client = assuan_get_pointer(ctx);
2316 gchar **req = split_input_line(line, " ", 0);
2317 guchar md5file[16];
2319 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2320 CACHE_LOCK(ctx);
2322 if (!req || !*req) {
2323 g_strfreev(req);
2324 cache_clear(client->md5file, 2);
2325 CACHE_UNLOCK;
2326 return send_error(ctx, 0);
2329 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2330 g_strfreev(req);
2332 if (cache_clear(md5file, 1) == FALSE) {
2333 CACHE_UNLOCK;
2334 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2337 CACHE_UNLOCK;
2338 pthread_cleanup_pop(0);
2339 return send_error(ctx, 0);
2342 static int cachetimeout_command(assuan_context_t ctx, char *line)
2344 guchar md5file[16];
2345 glong timeout;
2346 gchar **req = split_input_line(line, " ", 0);
2347 gchar *p;
2348 struct client_s *client = assuan_get_pointer(ctx);
2350 pthread_cleanup_push(req_cleanup, req);
2352 if (!req || !*req || !req[1]) {
2353 g_strfreev(req);
2354 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2357 errno = 0;
2358 timeout = strtol(req[0], &p, 10);
2360 if (errno != 0 || *p != 0) {
2361 g_strfreev(req);
2362 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2365 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2366 pthread_cleanup_pop(1);
2367 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2368 CACHE_LOCK(client->ctx);
2370 if (cache_set_timeout(md5file, timeout) == FALSE) {
2371 CACHE_UNLOCK;
2372 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2375 CACHE_UNLOCK;
2376 pthread_cleanup_pop(0);
2377 return send_error(ctx, 0);
2380 static int dump_command(assuan_context_t ctx, char *line)
2382 xmlChar *xml;
2383 gssize len;
2384 struct client_s *client = assuan_get_pointer(ctx);
2385 gpg_error_t rc;
2387 if (disable_list_and_dump == TRUE)
2388 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2390 rc = file_modified(client);
2392 if (rc)
2393 return send_error(ctx, rc);
2395 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2397 if (!xml) {
2398 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2399 return send_syserror(ctx, ENOMEM);
2402 pthread_cleanup_push(xmlFree, xml);
2403 pthread_testcancel();
2404 rc = assuan_send_data(ctx, xml, len);
2405 pthread_testcancel();
2406 pthread_cleanup_pop(1);
2407 return send_error(ctx, rc);
2410 static int getconfig_command(assuan_context_t ctx, gchar *line)
2412 struct client_s *client = assuan_get_pointer(ctx);
2413 gpg_error_t rc = 0;
2414 gchar filename[255]={0}, param[747]={0};
2415 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2417 if (strchr(line, ' ')) {
2418 sscanf(line, " %254[^ ] %746c", filename, param);
2419 fp = filename;
2420 paramp = param;
2423 if (fp && !valid_filename(fp))
2424 return send_error(ctx, EPWMD_INVALID_FILENAME);
2426 paramp = g_ascii_strdown(paramp, -1);
2428 if (!paramp) {
2429 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2430 return send_syserror(ctx, ENOMEM);
2433 p = get_key_file_string(fp ? fp : "global", paramp);
2434 g_free(paramp);
2436 if (!p)
2437 return send_error(ctx, GPG_ERR_NO_VALUE);
2439 tmp = expand_homedir(p);
2440 g_free(p);
2442 if (!tmp) {
2443 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2444 return send_syserror(ctx, ENOMEM);
2447 p = tmp;
2448 pthread_cleanup_push(g_free, p);
2449 pthread_testcancel();
2450 rc = assuan_send_data(ctx, p, strlen(p));
2451 pthread_testcancel();
2452 pthread_cleanup_pop(1);
2453 return send_error(ctx, rc);
2456 struct xpath_s {
2457 xmlXPathContextPtr xp;
2458 xmlXPathObjectPtr result;
2459 xmlBufferPtr buf;
2460 gchar **req;
2463 static void xpath_command_cleanup(void *arg)
2465 struct xpath_s *xpath = arg;
2467 req_cleanup(xpath->req);
2469 if (xpath->buf)
2470 xmlBufferFree(xpath->buf);
2472 if (xpath->result)
2473 xmlXPathFreeObject(xpath->result);
2475 if (xpath->xp)
2476 xmlXPathFreeContext(xpath->xp);
2479 static int xpath_command(assuan_context_t ctx, gchar *line)
2481 struct client_s *client = assuan_get_pointer(ctx);
2482 gpg_error_t rc;
2483 struct xpath_s xpath;
2485 if (disable_list_and_dump == TRUE)
2486 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2488 rc = file_modified(client);
2490 if (rc)
2491 return send_error(ctx, rc);
2493 if (!line || !*line)
2494 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2496 memset(&xpath, 0, sizeof(struct xpath_s));
2497 pthread_cleanup_push(xpath_command_cleanup, &xpath);
2499 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
2500 if (strv_printf(&xpath.req, "%s", line) == FALSE)
2501 return send_syserror(ctx, ENOMEM);
2504 xpath.xp = xmlXPathNewContext(client->doc);
2506 if (!xpath.xp) {
2507 xpath_command_cleanup(&xpath);
2508 return send_error(ctx, EPWMD_LIBXML_ERROR);
2511 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
2513 if (!xpath.result) {
2514 xpath_command_cleanup(&xpath);
2515 return send_error(ctx, EPWMD_LIBXML_ERROR);
2518 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
2519 rc = EPWMD_EMPTY_ELEMENT;
2520 goto fail;
2523 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
2524 (xmlChar *)xpath.req[1], &xpath.buf);
2526 if (rc)
2527 goto fail;
2528 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
2529 rc = EPWMD_EMPTY_ELEMENT;
2530 goto fail;
2532 else if (xpath.req[1])
2533 goto fail;
2535 pthread_testcancel();
2536 rc = assuan_send_data(ctx, xmlBufferContent(xpath.buf),
2537 xmlBufferLength(xpath.buf));
2538 pthread_testcancel();
2540 fail:
2541 if (1) {}
2542 pthread_cleanup_pop(1);
2543 return send_error(ctx, rc);
2546 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2547 gsize len)
2549 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2550 gpg_error_t rc = file_modified(client);
2551 gchar **req, **path = NULL, **path_orig = NULL, *content;
2552 xmlDocPtr doc;
2553 xmlNodePtr n, root, copy;
2555 if (assuan_rc || rc) {
2556 if (line)
2557 xfree(line);
2558 return assuan_rc ? assuan_rc : rc;
2561 req = split_input_line((gchar *)line, " ", 2);
2562 xfree(line);
2564 if (!req || !*req)
2565 return EPWMD_COMMAND_SYNTAX;
2567 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2568 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2569 return EPWMD_COMMAND_SYNTAX;
2572 content = req[1];
2574 if (!content || !*content) {
2575 rc = EPWMD_COMMAND_SYNTAX;
2576 goto fail;
2579 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2580 rc = EPWMD_INVALID_ELEMENT;
2581 goto fail;
2584 if (valid_element_path(path+1, FALSE) == FALSE) {
2585 rc = EPWMD_INVALID_ELEMENT;
2586 goto fail;
2589 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2591 if (!doc) {
2592 rc = EPWMD_LIBXML_ERROR;
2593 goto fail;
2596 root = xmlDocGetRootElement(doc);
2597 path_orig = g_strdupv(path);
2599 if (!path_orig) {
2600 xmlFreeDoc(doc);
2601 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2602 rc = gpg_error_from_errno(ENOMEM);
2603 goto fail;
2606 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2607 g_strfreev(path_orig);
2608 xmlFreeDoc(doc);
2609 rc = gpg_error_from_errno(ENOMEM);
2610 goto fail;
2613 n = find_account(client->doc, &path, &rc, NULL, 0);
2615 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2616 g_strfreev(path_orig);
2617 xmlFreeDoc(doc);
2618 goto fail;
2620 else if (!rc) {
2621 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2623 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2624 g_strfreev(path_orig);
2625 xmlFreeDoc(doc);
2626 goto fail;
2628 else if (!rc) {
2629 xmlNodePtr parent = n->parent;
2631 xmlUnlinkNode(n);
2632 xmlFreeNode(n);
2633 n = parent;
2637 g_strfreev(path);
2638 path = path_orig;
2640 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2641 n = create_element_path(client, &path, &rc);
2643 if (rc) {
2644 xmlFreeDoc(doc);
2645 goto fail;
2649 copy = xmlCopyNode(root, 1);
2650 n = xmlAddChild(n, copy);
2651 xmlFreeDoc(doc);
2653 if (!n)
2654 rc = EPWMD_LIBXML_ERROR;
2656 fail:
2657 g_strfreev(path);
2658 g_strfreev(req);
2659 client->inquire_status = INQUIRE_DONE;
2660 return rc;
2663 static int import_command(assuan_context_t ctx, gchar *line)
2665 gpg_error_t rc;
2666 struct client_s *client = assuan_get_pointer(ctx);
2668 rc = file_modified(client);
2670 if (rc)
2671 return send_error(ctx, rc);
2673 pthread_testcancel();
2674 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2675 pthread_testcancel();
2677 if (rc)
2678 return send_error(ctx, rc);
2680 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2681 client->inquire_status = INQUIRE_BUSY;
2682 return 0;
2685 static int lock_command(assuan_context_t ctx, gchar *line)
2687 gpg_error_t rc;
2688 struct client_s *client = assuan_get_pointer(ctx);
2690 rc = file_modified(client);
2692 if (rc)
2693 return send_error(ctx, rc);
2695 rc = lock_file_mutex(client);
2697 if (!rc)
2698 client->is_lock_cmd = TRUE;
2700 return send_error(ctx, rc);
2703 static int unlock_command(assuan_context_t ctx, gchar *line)
2705 struct client_s *client = assuan_get_pointer(ctx);
2706 gpg_error_t rc = file_modified(client);
2708 if (rc)
2709 return send_error(ctx, rc);
2711 unlock_file_mutex(client);
2712 return send_error(ctx, 0);
2715 static int getpid_command(assuan_context_t ctx, gchar *line)
2717 gpg_error_t rc;
2718 gchar buf[32];
2719 pid_t pid = getpid();
2721 print_fmt(buf, sizeof(buf), "%i", pid);
2722 pthread_testcancel();
2723 rc = assuan_send_data(ctx, buf, strlen(buf));
2724 pthread_testcancel();
2725 return send_error(ctx, rc);
2728 static int version_command(assuan_context_t ctx, gchar *line)
2730 gpg_error_t rc;
2731 gchar buf[32];
2733 print_fmt(buf, sizeof(buf), "%s", PACKAGE_VERSION);
2734 pthread_testcancel();
2735 rc = assuan_send_data(ctx, buf, strlen(buf));
2736 pthread_testcancel();
2737 return send_error(ctx, rc);
2740 static void bye_notify(assuan_context_t ctx)
2742 struct client_s *cl = assuan_get_pointer(ctx);
2743 #ifdef WITH_GNUTLS
2744 gint rc;
2746 if (!cl->thd->remote)
2747 return;
2749 do {
2750 rc = gnutls_bye(cl->thd->tls->ses, GNUTLS_SHUT_RDWR);
2751 } while (rc == GNUTLS_E_AGAIN);
2752 #endif
2754 /* This will let assuan_process_next() return. */
2755 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
2758 static void reset_notify(assuan_context_t ctx)
2760 struct client_s *cl = assuan_get_pointer(ctx);
2762 if (cl)
2763 cleanup_client(cl);
2766 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2768 gchar name[32] = {0}, value[256] = {0};
2770 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2771 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2773 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2774 struct client_s *cl = assuan_get_pointer(ctx);
2776 if (cl->thd->name)
2777 g_free(cl->thd->name);
2779 cl->thd->name = g_strdup(value);
2780 log_write("OPTION CLIENT %s", line);
2782 else
2783 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2785 return 0;
2788 static int option_handler(assuan_context_t ctx, const gchar *name,
2789 const gchar *value)
2791 struct client_s *client = assuan_get_pointer(ctx);
2793 if (!value || !*value)
2794 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2796 if (g_strcasecmp(name, (gchar *)"client") == 0)
2797 return parse_client_option(ctx, value);
2799 if (g_strcasecmp(name, (gchar *)"iterations") == 0) {
2800 long n;
2801 gchar *p = NULL;
2803 errno = 0;
2804 n = strtol(value, &p, 10);
2806 if (errno || (p && *p) || n < 0)
2807 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2809 g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", (guint)n);
2810 send_status_all(STATUS_CONFIG);
2812 #ifdef WITH_PINENTRY
2813 else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2814 g_free(client->pinentry->ttyname);
2815 client->pinentry->ttyname = g_strdup(value);
2817 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2818 g_free(client->pinentry->ttytype);
2819 client->pinentry->ttytype = g_strdup(value);
2821 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2822 g_free(client->pinentry->display);
2823 client->pinentry->display = g_strdup(value);
2825 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2826 g_free(client->pinentry->path);
2827 client->pinentry->path = g_strdup(value);
2829 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2830 g_free(client->pinentry->title);
2831 client->pinentry->title = g_strdup(value);
2833 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2834 g_free(client->pinentry->prompt);
2835 client->pinentry->prompt = g_strdup(value);
2837 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2838 g_free(client->pinentry->desc);
2839 client->pinentry->desc = g_strdup(value);
2842 * Look at client_thread() to see how this works.
2844 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2845 gchar *p = NULL;
2846 gint n = strtol(value, &p, 10);
2848 if (*p || n < 0)
2849 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2851 client->pinentry->timeout = n;
2853 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2854 gchar *p = NULL;
2855 gint n = strtol(value, &p, 10);
2857 if (*p || n < 0 || n > 1)
2858 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2860 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2862 #endif
2863 else
2864 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2866 log_write("OPTION %s=%s", name, value);
2867 return 0;
2870 gpg_error_t register_commands(assuan_context_t ctx)
2872 static struct {
2873 const gchar *name;
2874 gint (*handler)(assuan_context_t, gchar *line);
2875 } table[] = {
2876 { "OPEN", open_command },
2877 { "SAVE", save_command },
2878 { "LIST", list_command },
2879 { "REALPATH", realpath_command },
2880 { "STORE", store_command },
2881 { "DELETE", delete_command },
2882 { "GET", get_command },
2883 { "ATTR", attr_command },
2884 { "ISCACHED", iscached_command },
2885 { "CLEARCACHE", clearcache_command },
2886 { "CACHETIMEOUT", cachetimeout_command },
2887 { "GETCONFIG", getconfig_command },
2888 { "DUMP", dump_command },
2889 { "XPATH", xpath_command },
2890 { "IMPORT", import_command },
2891 { "LOCK", lock_command },
2892 { "UNLOCK", unlock_command },
2893 { "GETPID", getpid_command },
2894 { "VERSION", version_command },
2895 { "INPUT", NULL },
2896 { "OUTPUT", NULL },
2897 { NULL, NULL }
2899 gint i, rc;
2901 for (i=0; table[i].name; i++) {
2902 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2904 if (rc)
2905 return rc;
2908 rc = assuan_register_bye_notify(ctx, bye_notify);
2910 if (rc)
2911 return rc;
2913 rc = assuan_register_option_handler(ctx, option_handler);
2915 if (rc)
2916 return rc;
2918 rc = assuan_register_reset_notify(ctx, reset_notify);
2920 if (rc)
2921 return rc;
2923 return assuan_register_post_cmd_notify(ctx, command_finalize);
2926 gpg_error_t try_xml_decrypt(assuan_context_t ctx, guchar *key,
2927 struct client_crypto_s *crypto, gpointer *dst, gsize *dst_len)
2929 gsize insize, len;
2930 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2931 guint iter = 0, n_iter = 0, iter_progress = 0;
2932 gint zrc = 0;
2933 gulong outsize = 0;
2934 gpg_error_t rc;
2935 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->fh1) : sizeof(crypto->fh->fh2);
2936 glong fh_iter = crypto->fh->v1 ? crypto->fh->fh1.iter : crypto->fh->fh2.iter;
2938 lseek(crypto->fh->fd, fh_size, SEEK_SET);
2939 insize = crypto->fh->st.st_size - fh_size;
2940 crypto->iv = gcry_malloc(gcryblocksize);
2942 if (!crypto->iv) {
2943 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2944 return gpg_error_from_errno(ENOMEM);
2947 /* No encryption iterations. This is a plain (gzipped) file. */
2948 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0)) {
2950 * cache_file_count() needs both .used == TRUE and a valid key in
2951 * order for it to count as a used cache entry. Fixes CACHE status
2952 * messages.
2954 memset(key, '!', gcrykeysize);
2957 if (crypto->fh->v1)
2958 memcpy(crypto->iv, crypto->fh->fh1.iv, gcryblocksize);
2959 else
2960 memcpy(crypto->iv, crypto->fh->fh2.iv, gcryblocksize);
2962 crypto->inbuf = gcry_malloc(insize);
2964 if (!crypto->inbuf) {
2965 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2966 return gpg_error_from_errno(ENOMEM);
2969 crypto->insize = insize;
2970 len = read(crypto->fh->fd, crypto->inbuf, crypto->insize);
2972 if (len != crypto->insize)
2973 return GPG_ERR_INV_LENGTH;
2975 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0))
2976 goto decompress;
2978 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
2979 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2980 return rc;
2983 if ((rc = gcry_cipher_setkey(crypto->gh, key, gcrykeysize))) {
2984 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2985 return rc;
2988 iter_progress = (guint)get_key_file_integer(client && client->filename ?
2989 client->filename : "global", "iteration_progress");
2991 if (iter_progress > 0 && fh_iter >= iter_progress) {
2992 rc = send_status(ctx, STATUS_DECRYPT, "%u %u", 0, fh_iter);
2994 if (rc)
2995 return rc;
2998 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3000 if (rc)
3001 return rc;
3003 crypto->tkey = gcry_malloc(gcrykeysize);
3005 if (!crypto->tkey) {
3006 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
3007 return gpg_error_from_errno(ENOMEM);
3010 memcpy(crypto->tkey, key, gcrykeysize);
3011 guchar *tkey = crypto->tkey;
3012 tkey[0] ^= 1;
3014 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
3015 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3016 return rc;
3019 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
3020 if (iter_progress > 0 && iter >= iter_progress) {
3021 if (!(iter % iter_progress)) {
3022 rc = send_status(ctx, STATUS_DECRYPT, "%u %u",
3023 ++n_iter * iter_progress, fh_iter);
3025 if (rc)
3026 return rc;
3030 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3031 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3032 return rc;
3035 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3037 if (rc) {
3038 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3039 return rc;
3042 iter++;
3045 if (iter_progress && fh_iter >= iter_progress) {
3046 rc = send_status(ctx, STATUS_DECRYPT, "%u %u", fh_iter, fh_iter);
3048 if (rc)
3049 return rc;
3052 decompress:
3053 if (do_decompress(ctx, crypto->inbuf, crypto->insize,
3054 (gpointer *)&crypto->outbuf, &outsize, &zrc) == FALSE) {
3055 if (zrc == Z_MEM_ERROR)
3056 return gpg_error_from_errno(ENOMEM);
3057 else
3058 return EPWMD_BADKEY; // Not a valid gzip header. Must be a bad key.
3061 if (g_strncasecmp(crypto->outbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
3062 gcry_free(crypto->outbuf);
3063 crypto->outbuf = NULL;
3064 return EPWMD_BADKEY;
3067 if (ctx) {
3068 client->xml = crypto->outbuf;
3069 client->len = outsize;
3070 crypto->outbuf = NULL;
3072 else if (dst) {
3073 *dst = crypto->outbuf;
3074 *dst_len = outsize;
3075 crypto->outbuf = NULL;
3078 /* The calling function should free the crypto struct. */
3079 return 0;
3083 * This is called after every Assuan command.
3085 void command_finalize(assuan_context_t ctx, gint rc)
3087 struct client_s *client = assuan_get_pointer(ctx);
3089 if (!client->is_lock_cmd)
3090 unlock_file_mutex(client);