More thread cancelation tests and handlers.
[pwmd.git] / src / commands.c
blob24e46311bd937c5a5c028f8164a71329977a0d11
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 if (key != client->crypto->key)
1144 gcry_free(key);
1146 xmlFree(xmlbuf);
1147 cleanup_crypto(&client->crypto);
1149 if (zrc == Z_MEM_ERROR)
1150 return send_syserror(ctx, ENOMEM);
1151 else
1152 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1154 else {
1155 gcry_free(xmlbuf);
1156 xmlbuf = outbuf;
1157 len = outsize;
1160 client->crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1162 if (!client->crypto->fh) {
1163 cleanup_crypto(&client->crypto);
1164 gcry_free(outbuf);
1165 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1166 return send_syserror(ctx, ENOMEM);
1169 iter = get_key_file_integer(client->filename, "iterations");
1170 client->crypto->fh->fh2.iter = iter < 0 ? 0 : iter;
1171 client->crypto->inbuf = xmlbuf;
1172 client->crypto->insize = len;
1173 rc = do_xml_encrypt(client, client->crypto, client->filename);
1175 if (rc) {
1176 cleanup_crypto(&client->crypto);
1177 return send_error(ctx, rc);
1180 timeout = get_key_file_integer(client->filename, "cache_timeout");
1181 CACHE_LOCK(client->ctx);
1183 if (cached) {
1184 cache_reset_timeout(client->md5file, timeout);
1185 CACHE_UNLOCK;
1187 if (client->new == TRUE)
1188 send_status_all(STATUS_CACHE);
1190 client->new = FALSE;
1191 cleanup_crypto(&client->crypto);
1192 return send_error(ctx, 0);
1195 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1196 CACHE_UNLOCK;
1197 cleanup_crypto(&client->crypto);
1198 return send_syserror(ctx, ENOMEM);
1201 client->new = FALSE;
1202 cache_reset_timeout(client->md5file, timeout);
1203 CACHE_UNLOCK;
1204 send_status_all(STATUS_CACHE);
1205 cleanup_crypto(&client->crypto);
1206 return send_error(ctx, 0);
1209 static int save_command(assuan_context_t ctx, char *line)
1211 gboolean cached = FALSE;
1212 struct stat st;
1213 struct client_s *client = assuan_get_pointer(ctx);
1214 gpg_error_t rc;
1216 rc = lock_file_mutex(client);
1218 if (rc)
1219 return send_error(ctx, rc);
1221 rc = file_modified(client);
1223 if (rc)
1224 return send_error(ctx, rc);
1226 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1227 return send_syserror(ctx, errno);
1229 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1230 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1231 return send_error(ctx, GPG_ERR_ENOANO);
1234 CACHE_LOCK(ctx);
1235 cached = cache_iscached(client->md5file);
1236 CACHE_UNLOCK;
1239 * If a cache entry doesn't exist for this file and the file has a
1240 * "key_file" or "key" parameter, then it's an error. The reason is that
1241 * cache expiration would be useless.
1243 if (cached == FALSE) {
1244 gchar *tmp = get_key_file_string(client->filename, "key_file");
1246 if (tmp) {
1247 g_free(tmp);
1248 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1252 cached = FALSE;
1253 client->crypto = init_client_crypto();
1255 if (!client->crypto) {
1256 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1257 return send_syserror(ctx, ENOMEM);
1260 client->crypto->key = gcry_malloc(gcrykeysize);
1262 if (!client->crypto->key) {
1263 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1264 cleanup_crypto(&client->crypto);
1265 return send_syserror(ctx, ENOMEM);
1268 memset(client->crypto->key, '!', gcrykeysize);
1270 if (get_key_file_integer(client->filename, "iterations") <= 0)
1271 goto done;
1273 if (!line || !*line) {
1274 client->crypto->tkey = gcry_malloc(gcrykeysize);
1276 if (!client->crypto->tkey) {
1277 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1278 cleanup_crypto(&client->crypto);
1279 return send_syserror(ctx, ENOMEM);
1282 memset(client->crypto->tkey, '!', gcrykeysize);
1283 CACHE_LOCK(ctx);
1285 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1286 memcmp(client->crypto->key, client->crypto->tkey,
1287 gcrykeysize) == 0) {
1288 CACHE_UNLOCK;
1290 #ifdef WITH_PINENTRY
1291 if (client->pinentry->enable == FALSE ||
1292 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1293 /* Empty keys are allowed. */
1294 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1295 goto done;
1298 lock_pin_mutex(client);
1299 client->pinentry->which = PINENTRY_SAVE;
1300 rc = pinentry_fork(ctx);
1302 if (rc) {
1303 unlock_pin_mutex(client->pinentry);
1304 return send_error(ctx, rc);
1307 client->pinentry->cb = save_command_finalize;
1308 client->pinentry->status = PINENTRY_INIT;
1309 return 0;
1310 #else
1311 /* Empty keys are allowed. */
1312 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1313 goto done;
1314 #endif
1316 else {
1317 CACHE_UNLOCK;
1318 cached = TRUE;
1321 else {
1322 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, line,
1323 strlen(line));
1324 memset(line, 0, strlen(line));
1327 done:
1328 return save_command_finalize(ctx, client->crypto->key, cached);
1331 static int delete_command(assuan_context_t ctx, char *line)
1333 struct client_s *client = assuan_get_pointer(ctx);
1334 gchar **req;
1335 gpg_error_t rc;
1336 xmlNodePtr n;
1338 rc = file_modified(client);
1340 if (rc)
1341 return send_error(ctx, rc);
1343 if (strchr(line, '\t'))
1344 req = split_input_line(line, "\t", -1);
1345 else
1346 req = split_input_line(line, " ", -1);
1348 if (!req || !*req)
1349 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1351 n = find_account(client->doc, &req, &rc, NULL, 0);
1353 if (!n) {
1354 g_strfreev(req);
1355 return send_error(ctx, rc);
1359 * No sub-node defined. Remove the entire node (account).
1361 if (!req[1]) {
1362 if (n) {
1363 xmlUnlinkNode(n);
1364 xmlFreeNode(n);
1367 g_strfreev(req);
1368 return send_error(ctx, 0);
1371 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1372 g_strfreev(req);
1374 if (!n)
1375 return send_error(ctx, rc);
1377 if (n) {
1378 xmlUnlinkNode(n);
1379 xmlFreeNode(n);
1382 return send_error(ctx, 0);
1386 * Don't return with assuan_process_done() here. This has been called from
1387 * assuan_process_next() and the command should be finished in
1388 * client_thread().
1390 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1391 gsize len)
1393 assuan_context_t ctx = data;
1394 struct client_s *client = assuan_get_pointer(ctx);
1395 gchar **req;
1396 xmlNodePtr n;
1397 gpg_error_t rc = file_modified(client);
1399 if (assuan_rc || rc) {
1400 if (line)
1401 xfree(line);
1402 return assuan_rc ? assuan_rc : rc;
1405 req = split_input_line((gchar *)line, "\t", 0);
1406 xfree(line);
1408 if (!req || !*req)
1409 return EPWMD_COMMAND_SYNTAX;
1411 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1412 g_strfreev(req);
1413 return EPWMD_INVALID_ELEMENT;
1416 if (valid_element_path(req+1, TRUE) == FALSE) {
1417 g_strfreev(req);
1418 return EPWMD_INVALID_ELEMENT;
1421 again:
1422 n = find_account(client->doc, &req, &rc, NULL, 0);
1424 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1425 rc = new_account(client->doc, *req);
1427 if (rc) {
1428 g_strfreev(req);
1429 return rc;
1432 goto again;
1435 if (!n) {
1436 g_strfreev(req);
1437 return rc;
1440 if (req[1]) {
1441 if (!n->children)
1442 create_elements_cb(n, req+1, &rc, NULL);
1443 else
1444 find_elements(client->doc, n->children, req+1, &rc,
1445 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1448 g_strfreev(req);
1449 client->inquire_status = INQUIRE_DONE;
1450 return rc;
1453 static int store_command(assuan_context_t ctx, char *line)
1455 struct client_s *client = assuan_get_pointer(ctx);
1456 gpg_error_t rc = file_modified(client);
1458 if (rc)
1459 return send_error(ctx, rc);
1461 pthread_testcancel();
1462 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1463 pthread_testcancel();
1465 if (rc)
1466 return send_error(ctx, rc);
1468 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1469 client->inquire_status = INQUIRE_BUSY;
1470 return 0;
1473 static int get_command(assuan_context_t ctx, char *line)
1475 struct client_s *client = assuan_get_pointer(ctx);
1476 gchar **req;
1477 gpg_error_t rc;
1478 xmlNodePtr n;
1480 rc = file_modified(client);
1482 if (rc)
1483 return send_error(ctx, rc);
1485 req = split_input_line(line, "\t", -1);
1487 if (!req || !*req) {
1488 g_strfreev(req);
1489 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1492 n = find_account(client->doc, &req, &rc, NULL, 0);
1494 if (!n) {
1495 g_strfreev(req);
1496 return send_error(ctx, rc);
1499 if (req[1])
1500 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1502 g_strfreev(req);
1504 if (rc)
1505 return send_error(ctx, rc);
1507 if (!n || !n->children)
1508 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1510 n = find_text_node(n->children);
1512 if (!n || !n->content || !*n->content)
1513 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1515 pthread_testcancel();
1516 rc = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1517 pthread_testcancel();
1518 return send_error(ctx, rc);
1521 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1522 gpg_error_t *rc, gchar **req_orig, void *data)
1524 gchar *path = *(gchar **)data;
1525 gchar *tmp = NULL, *result;
1527 if (path) {
1528 g_free(path);
1529 *(gchar **)data = NULL;
1532 path = g_strjoinv("\t", target);
1534 if (!path) {
1535 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1536 *rc = gpg_error_from_errno(ENOMEM);
1537 return NULL;
1540 if (req_orig) {
1541 tmp = g_strjoinv("\t", req_orig);
1543 if (!tmp) {
1544 g_free(path);
1545 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1546 *rc = gpg_error_from_errno(ENOMEM);
1547 return NULL;
1551 if (tmp && *tmp)
1552 result = g_strdup_printf("%s\t%s", path, tmp);
1553 else
1554 result = g_strdup(path);
1556 if (!result) {
1557 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1558 *rc = gpg_error_from_errno(ENOMEM);
1559 g_free(path);
1560 g_free(tmp);
1561 return NULL;
1564 g_free(path);
1565 g_free(tmp);
1566 *(gchar **)data = result;
1567 return node;
1570 static void list_command_cleanup1(void *arg);
1571 static int realpath_command(assuan_context_t ctx, char *line)
1573 gpg_error_t rc;
1574 struct client_s *client = assuan_get_pointer(ctx);
1575 gchar **req;
1576 gchar *t;
1577 gint i;
1578 xmlNodePtr n;
1579 GString *string;
1580 gchar *rp = NULL;
1582 rc = file_modified(client);
1584 if (rc)
1585 return send_error(ctx, rc);
1587 if (strchr(line, '\t') != NULL) {
1588 if ((req = split_input_line(line, "\t", 0)) == NULL)
1589 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1591 else {
1592 if ((req = split_input_line(line, " ", 0)) == NULL)
1593 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1596 n = find_account(client->doc, &req, &rc, NULL, 0);
1598 if (!n) {
1599 g_strfreev(req);
1600 return send_error(ctx, rc);
1603 rp = g_strjoinv("\t", req);
1605 if (!rp) {
1606 g_strfreev(req);
1607 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1608 return send_syserror(ctx, ENOMEM);
1611 if (req[1]) {
1612 n = find_elements(client->doc, n->children, req+1, &rc,
1613 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1615 if (!n) {
1616 g_free(rp);
1617 g_strfreev(req);
1618 return send_error(ctx, rc);
1622 string = g_string_new(rp);
1623 g_free(rp);
1624 g_strfreev(req);
1626 if (!string) {
1627 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1628 return send_syserror(ctx, ENOMEM);
1631 again:
1632 for (i = 0, t = string->str + i; *t; t++, i++) {
1633 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1634 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1635 goto again;
1639 pthread_cleanup_push(list_command_cleanup1, string);
1640 pthread_testcancel();
1641 rc = assuan_send_data(ctx, string->str, string->len);
1642 pthread_testcancel();
1643 pthread_cleanup_pop(1);
1644 return send_error(ctx, rc);
1647 static void list_command_cleanup1(void *arg)
1649 g_string_free((GString *)arg, TRUE);
1652 static void list_command_cleanup2(void *arg)
1654 struct element_list_s *elements = arg;
1656 if (elements) {
1657 gint total = g_slist_length(elements->list);
1658 gint i;
1660 for (i = 0; i < total; i++) {
1661 gchar *tmp = g_slist_nth_data(elements->list, i);
1662 g_free(tmp);
1665 g_slist_free(elements->list);
1667 if (elements->prefix)
1668 g_free(elements->prefix);
1670 g_free(elements);
1674 static int list_command(assuan_context_t ctx, char *line)
1676 struct client_s *client = assuan_get_pointer(ctx);
1677 gpg_error_t rc;
1678 struct element_list_s *elements = NULL;
1679 gchar *tmp;
1681 if (disable_list_and_dump == TRUE)
1682 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1684 rc = file_modified(client);
1686 if (rc)
1687 return send_error(ctx, rc);
1689 if (!*line) {
1690 GString *str;
1692 rc = list_accounts(client->doc, &str);
1693 pthread_cleanup_push(list_command_cleanup1, str);
1695 if (rc)
1696 return send_error(ctx, rc);
1698 pthread_testcancel();
1699 rc = assuan_send_data(ctx, str->str, str->len);
1700 pthread_testcancel();
1701 pthread_cleanup_pop(1);
1702 return send_error(ctx, rc);
1705 elements = g_malloc0(sizeof(struct element_list_s));
1707 if (!elements) {
1708 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1709 rc = gpg_err_code_from_errno(ENOMEM);
1710 goto fail;
1713 pthread_cleanup_push(list_command_cleanup2, elements);
1714 pthread_testcancel();
1715 rc = create_path_list(client->doc, elements, line);
1716 pthread_testcancel();
1718 if (rc)
1719 goto fail;
1721 if (elements) {
1722 gint total = g_slist_length(elements->list);
1723 gint i;
1724 GString *str;
1726 if (!total) {
1727 rc = EPWMD_EMPTY_ELEMENT;
1728 goto fail;
1731 str = g_string_new(NULL);
1733 if (!str) {
1734 rc = gpg_err_code_from_errno(ENOMEM);
1735 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1736 goto fail;
1739 pthread_cleanup_push(list_command_cleanup1, str);
1741 for (i = 0; i < total; i++) {
1742 tmp = g_slist_nth_data(elements->list, i);
1743 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1746 pthread_testcancel();
1747 rc = assuan_send_data(ctx, str->str, str->len);
1748 pthread_testcancel();
1749 pthread_cleanup_pop(1);
1751 else
1752 rc = EPWMD_EMPTY_ELEMENT;
1754 fail:
1755 if (1) {}
1756 pthread_cleanup_pop(1);
1757 return send_error(ctx, rc);
1760 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1761 const gchar *value)
1763 xmlAttrPtr a;
1765 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1766 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1768 if (!a)
1769 return EPWMD_LIBXML_ERROR;
1771 else
1772 xmlNodeSetContent(a->children, (xmlChar *)value);
1774 return 0;
1778 * req[0] - element path
1780 static int attribute_list(assuan_context_t ctx, gchar **req)
1782 struct client_s *client = assuan_get_pointer(ctx);
1783 gchar **attrlist = NULL;
1784 gint i = 0;
1785 gchar **path = NULL;
1786 xmlAttrPtr a;
1787 xmlNodePtr n, an;
1788 gchar *line;
1789 gpg_error_t rc;
1791 if (!req || !req[0])
1792 return EPWMD_COMMAND_SYNTAX;
1794 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1796 * The first argument may be only an account.
1798 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1799 return EPWMD_COMMAND_SYNTAX;
1802 n = find_account(client->doc, &path, &rc, NULL, 0);
1804 if (!n) {
1805 g_strfreev(path);
1806 return rc;
1809 if (path[1]) {
1810 n = find_elements(client->doc, n->children, path+1, &rc,
1811 NULL, NULL, NULL, FALSE, 0, NULL);
1813 if (!n) {
1814 g_strfreev(path);
1815 return rc;
1819 g_strfreev(path);
1821 for (a = n->properties; a; a = a->next) {
1822 gchar **pa;
1824 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1825 if (attrlist)
1826 g_strfreev(attrlist);
1828 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1829 return gpg_error_from_errno(ENOMEM);
1832 attrlist = pa;
1833 an = a->children;
1834 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
1836 if (!attrlist[i]) {
1837 g_strfreev(attrlist);
1838 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1839 return gpg_error_from_errno(ENOMEM);
1842 attrlist[++i] = NULL;
1845 if (!attrlist)
1846 return EPWMD_EMPTY_ELEMENT;
1848 line = g_strjoinv("\n", attrlist);
1850 if (!line) {
1851 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1852 g_strfreev(attrlist);
1853 return gpg_error_from_errno(ENOMEM);
1856 pthread_cleanup_push(g_free, line);
1857 pthread_cleanup_push(req_cleanup, attrlist);
1858 pthread_testcancel();
1859 rc = assuan_send_data(ctx, line, strlen(line));
1860 pthread_testcancel();
1861 pthread_cleanup_pop(1);
1862 pthread_cleanup_pop(1);
1863 return rc;
1867 * req[0] - attribute
1868 * req[1] - element path
1870 static int attribute_delete(struct client_s *client, gchar **req)
1872 xmlAttrPtr a;
1873 xmlNodePtr n;
1874 gchar **path = NULL;
1875 gpg_error_t rc;
1877 if (!req || !req[0] || !req[1])
1878 return EPWMD_COMMAND_SYNTAX;
1880 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1882 * The first argument may be only an account.
1884 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1885 return EPWMD_COMMAND_SYNTAX;
1889 * Don't remove the "name" attribute for the account element. To remove an
1890 * account use DELETE <account>.
1892 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1893 rc = EPWMD_ATTR_SYNTAX;
1894 goto fail;
1897 n = find_account(client->doc, &path, &rc, NULL, 0);
1899 if (!n)
1900 goto fail;
1902 if (path[1]) {
1903 n = find_elements(client->doc, n->children, path+1, &rc,
1904 NULL, NULL, NULL, FALSE, 0, NULL);
1906 if (!n)
1907 goto fail;
1910 g_strfreev(path);
1912 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1913 return EPWMD_ATTR_NOT_FOUND;
1915 if (xmlRemoveProp(a) == -1)
1916 return EPWMD_LIBXML_ERROR;
1918 return 0;
1920 fail:
1921 g_strfreev(path);
1922 return rc;
1925 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
1926 gpg_error_t *rc)
1928 gchar **src = *path;
1929 gchar **src_orig = g_strdupv(src);
1930 xmlNodePtr n = NULL;
1932 *rc = 0;
1934 if (!src_orig) {
1935 *rc = gpg_error_from_errno(ENOMEM);
1936 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1937 goto fail;
1940 again:
1941 n = find_account(client->doc, &src, rc, NULL, 0);
1943 if (!n) {
1944 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
1945 *rc = new_account(client->doc, src[0]);
1947 if (*rc)
1948 goto fail;
1950 goto again;
1952 else
1953 goto fail;
1956 if (src[1]) {
1957 if (!n->children)
1958 n = create_target_elements_cb(n, src+1, rc, NULL);
1959 else
1960 n = find_elements(client->doc, n->children, src+1, rc,
1961 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
1963 if (!n)
1964 goto fail;
1967 * Reset the position of the element tree now that the elements
1968 * have been created.
1970 g_strfreev(src);
1971 src = src_orig;
1972 src_orig = NULL;
1973 n = find_account(client->doc, &src, rc, NULL, 0);
1975 if (!n)
1976 goto fail;
1978 n = find_elements(client->doc, n->children, src+1, rc,
1979 NULL, NULL, NULL, FALSE, 0, NULL);
1981 if (!n)
1982 goto fail;
1985 fail:
1986 if (src_orig)
1987 g_strfreev(src_orig);
1989 *path = src;
1990 return n;
1994 * Creates a "target" attribute. When other commands encounter an element with
1995 * this attribute, the element path is modified to the target value. If the
1996 * source element path doesn't exist when using 'ATTR SET target', it is
1997 * created, but the destination element path must exist.
1999 * req[0] - source element path
2000 * req[1] - destination element path
2002 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2004 gchar **src, **dst, *line = NULL;
2005 gpg_error_t rc;
2006 xmlNodePtr n;
2008 if (!req || !req[0] || !req[1])
2009 return EPWMD_COMMAND_SYNTAX;
2011 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2013 * The first argument may be only an account.
2015 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2016 return EPWMD_COMMAND_SYNTAX;
2019 if (valid_element_path(src, FALSE) == FALSE) {
2020 g_strfreev(src);
2021 return EPWMD_INVALID_ELEMENT;
2024 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2026 * The first argument may be only an account.
2028 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2029 rc = EPWMD_COMMAND_SYNTAX;
2030 goto fail;
2034 n = find_account(client->doc, &dst, &rc, NULL, 0);
2037 * Make sure the destination element path exists.
2039 if (!n)
2040 goto fail;
2042 if (dst[1]) {
2043 n = find_elements(client->doc, n->children, dst+1, &rc,
2044 NULL, NULL, NULL, FALSE, 0, NULL);
2046 if (!n)
2047 goto fail;
2050 n = create_element_path(client, &src, &rc);
2052 if (rc)
2053 goto fail;
2055 line = g_strjoinv("\t", dst);
2057 if (!line) {
2058 rc = gpg_error_from_errno(ENOMEM);
2059 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2060 goto fail;
2063 rc = add_attribute(n, "target", line);
2065 fail:
2066 g_free(line);
2067 g_strfreev(src);
2068 g_strfreev(dst);
2069 return rc;
2073 * req[0] - account name
2074 * req[1] - new name
2076 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2078 gpg_error_t rc;
2079 gchar **tmp;
2080 xmlNodePtr n;
2082 tmp = g_strdupv(req);
2084 if (!tmp) {
2085 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2086 return gpg_error_from_errno(ENOMEM);
2089 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2090 g_strfreev(tmp);
2092 if (!n)
2093 return rc;
2095 if (g_utf8_collate(req[0], req[1]) == 0)
2096 return 0;
2099 * Will not overwrite an existing account.
2101 tmp = g_strdupv(req+1);
2103 if (!tmp) {
2104 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2105 return gpg_error_from_errno(ENOMEM);
2108 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2109 g_strfreev(tmp);
2111 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
2112 return rc;
2114 if (n)
2115 return EPWMD_ACCOUNT_EXISTS;
2118 * Whitespace not allowed in account names.
2120 if (contains_whitespace(req[1]) == TRUE)
2121 return EPWMD_ATTR_SYNTAX;
2123 tmp = g_strdupv(req);
2125 if (!tmp) {
2126 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2127 return gpg_error_from_errno(ENOMEM);
2130 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2131 g_strfreev(tmp);
2133 if (!n)
2134 return EPWMD_ELEMENT_NOT_FOUND;
2136 return add_attribute(n, "name", req[1]);
2140 * req[0] - attribute
2141 * req[1] - element path
2143 static int attribute_get(assuan_context_t ctx, gchar **req)
2145 struct client_s *client = assuan_get_pointer(ctx);
2146 xmlNodePtr n;
2147 xmlChar *a;
2148 gchar **path= NULL;
2149 gpg_error_t rc;
2151 if (!req || !req[0] || !req[1])
2152 return EPWMD_COMMAND_SYNTAX;
2154 if (strchr(req[1], '\t')) {
2155 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2156 return EPWMD_COMMAND_SYNTAX;
2158 else {
2159 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2160 return EPWMD_COMMAND_SYNTAX;
2163 n = find_account(client->doc, &path, &rc, NULL, 0);
2165 if (!n)
2166 goto fail;
2168 if (path[1]) {
2169 n = find_elements(client->doc, n->children, path+1, &rc,
2170 NULL, NULL, NULL, FALSE, 0, NULL);
2172 if (!n)
2173 goto fail;
2176 g_strfreev(path);
2178 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2179 return EPWMD_ATTR_NOT_FOUND;
2181 pthread_cleanup_push(xmlFree, a);
2182 pthread_testcancel();
2183 rc = assuan_send_data(ctx, a, xmlStrlen(a));
2184 pthread_testcancel();
2185 pthread_cleanup_pop(1);
2186 return rc;
2188 fail:
2189 g_strfreev(path);
2190 return rc;
2194 * req[0] - attribute
2195 * req[1] - element path
2196 * req[2] - value
2198 static int attribute_set(struct client_s *client, gchar **req)
2200 gchar **path = NULL;
2201 gpg_error_t rc;
2202 xmlNodePtr n;
2204 if (!req || !req[0] || !req[1] || !req[2])
2205 return EPWMD_COMMAND_SYNTAX;
2208 * Reserved attribute names.
2210 if (g_utf8_collate(req[0], "name") == 0) {
2212 * Only reserved for the account element. Not the rest of the
2213 * document.
2215 if (strchr(req[1], '\t') == NULL)
2216 return name_attribute(client, req + 1);
2218 else if (g_utf8_collate(req[0], "target") == 0)
2219 return target_attribute(client, req + 1);
2221 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2223 * The first argument may be only an account.
2225 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2226 return EPWMD_COMMAND_SYNTAX;
2229 n = find_account(client->doc, &path, &rc, NULL, 0);
2231 if (!n)
2232 goto fail;
2234 if (path[1]) {
2235 n = find_elements(client->doc, n->children, path+1, &rc,
2236 NULL, NULL, NULL, FALSE, 0, NULL);
2238 if (!n)
2239 goto fail;
2242 g_strfreev(path);
2243 return add_attribute(n, req[0], req[2]);
2245 fail:
2246 g_strfreev(path);
2247 return rc;
2251 * req[0] - command
2252 * req[1] - attribute name or element path if command is LIST
2253 * req[2] - element path
2254 * req[2] - element path or value
2256 static int attr_command(assuan_context_t ctx, char *line)
2258 struct client_s *client = assuan_get_pointer(ctx);
2259 gchar **req;
2260 gpg_error_t rc = 0;
2262 rc = file_modified(client);
2264 if (rc)
2265 return send_error(ctx, rc);
2267 req = split_input_line(line, " ", 4);
2269 if (!req || !req[0] || !req[1]) {
2270 g_strfreev(req);
2271 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2274 pthread_cleanup_push(req_cleanup, req);
2276 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2277 rc = attribute_set(client, req+1);
2278 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2279 rc = attribute_get(ctx, req+1);
2280 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2281 rc = attribute_delete(client, req+1);
2282 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2283 rc = attribute_list(ctx, req+1);
2284 else
2285 rc = EPWMD_COMMAND_SYNTAX;
2287 pthread_cleanup_pop(1);
2288 return send_error(ctx, rc);
2291 static int iscached_command(assuan_context_t ctx, char *line)
2293 gchar **req = split_input_line(line, " ", 0);
2294 guchar md5file[16];
2296 if (!req || !*req) {
2297 g_strfreev(req);
2298 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2301 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2302 g_strfreev(req);
2303 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2304 CACHE_LOCK(ctx);
2306 if (cache_iscached(md5file) == FALSE) {
2307 CACHE_UNLOCK;
2308 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2311 CACHE_UNLOCK;
2312 pthread_cleanup_pop(0);
2313 return send_error(ctx, 0);
2316 static int clearcache_command(assuan_context_t ctx, char *line)
2318 struct client_s *client = assuan_get_pointer(ctx);
2319 gchar **req = split_input_line(line, " ", 0);
2320 guchar md5file[16];
2322 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2323 CACHE_LOCK(ctx);
2325 if (!req || !*req) {
2326 g_strfreev(req);
2327 cache_clear(client->md5file, 2);
2328 CACHE_UNLOCK;
2329 return send_error(ctx, 0);
2332 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2333 g_strfreev(req);
2335 if (cache_clear(md5file, 1) == FALSE) {
2336 CACHE_UNLOCK;
2337 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2340 CACHE_UNLOCK;
2341 pthread_cleanup_pop(0);
2342 return send_error(ctx, 0);
2345 static int cachetimeout_command(assuan_context_t ctx, char *line)
2347 guchar md5file[16];
2348 glong timeout;
2349 gchar **req = split_input_line(line, " ", 0);
2350 gchar *p;
2351 struct client_s *client = assuan_get_pointer(ctx);
2353 pthread_cleanup_push(req_cleanup, req);
2355 if (!req || !*req || !req[1]) {
2356 g_strfreev(req);
2357 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2360 errno = 0;
2361 timeout = strtol(req[0], &p, 10);
2363 if (errno != 0 || *p != 0) {
2364 g_strfreev(req);
2365 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2368 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2369 pthread_cleanup_pop(1);
2370 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2371 CACHE_LOCK(client->ctx);
2373 if (cache_set_timeout(md5file, timeout) == FALSE) {
2374 CACHE_UNLOCK;
2375 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2378 CACHE_UNLOCK;
2379 pthread_cleanup_pop(0);
2380 return send_error(ctx, 0);
2383 static int dump_command(assuan_context_t ctx, char *line)
2385 xmlChar *xml;
2386 gssize len;
2387 struct client_s *client = assuan_get_pointer(ctx);
2388 gpg_error_t rc;
2390 if (disable_list_and_dump == TRUE)
2391 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2393 rc = file_modified(client);
2395 if (rc)
2396 return send_error(ctx, rc);
2398 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2400 if (!xml) {
2401 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2402 return send_syserror(ctx, ENOMEM);
2405 pthread_cleanup_push(xmlFree, xml);
2406 pthread_testcancel();
2407 rc = assuan_send_data(ctx, xml, len);
2408 pthread_testcancel();
2409 pthread_cleanup_pop(1);
2410 return send_error(ctx, rc);
2413 static int getconfig_command(assuan_context_t ctx, gchar *line)
2415 struct client_s *client = assuan_get_pointer(ctx);
2416 gpg_error_t rc = 0;
2417 gchar filename[255]={0}, param[747]={0};
2418 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2420 if (strchr(line, ' ')) {
2421 sscanf(line, " %254[^ ] %746c", filename, param);
2422 fp = filename;
2423 paramp = param;
2426 if (fp && !valid_filename(fp))
2427 return send_error(ctx, EPWMD_INVALID_FILENAME);
2429 paramp = g_ascii_strdown(paramp, -1);
2431 if (!paramp) {
2432 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2433 return send_syserror(ctx, ENOMEM);
2436 p = get_key_file_string(fp ? fp : "global", paramp);
2437 g_free(paramp);
2439 if (!p)
2440 return send_error(ctx, GPG_ERR_NO_VALUE);
2442 tmp = expand_homedir(p);
2443 g_free(p);
2445 if (!tmp) {
2446 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2447 return send_syserror(ctx, ENOMEM);
2450 p = tmp;
2451 pthread_cleanup_push(g_free, p);
2452 pthread_testcancel();
2453 rc = assuan_send_data(ctx, p, strlen(p));
2454 pthread_testcancel();
2455 pthread_cleanup_pop(1);
2456 return send_error(ctx, rc);
2459 struct xpath_s {
2460 xmlXPathContextPtr xp;
2461 xmlXPathObjectPtr result;
2462 xmlBufferPtr buf;
2463 gchar **req;
2466 static void xpath_command_cleanup(void *arg)
2468 struct xpath_s *xpath = arg;
2470 req_cleanup(xpath->req);
2472 if (xpath->buf)
2473 xmlBufferFree(xpath->buf);
2475 if (xpath->result)
2476 xmlXPathFreeObject(xpath->result);
2478 if (xpath->xp)
2479 xmlXPathFreeContext(xpath->xp);
2482 static int xpath_command(assuan_context_t ctx, gchar *line)
2484 struct client_s *client = assuan_get_pointer(ctx);
2485 gpg_error_t rc;
2486 struct xpath_s xpath;
2488 if (disable_list_and_dump == TRUE)
2489 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2491 rc = file_modified(client);
2493 if (rc)
2494 return send_error(ctx, rc);
2496 if (!line || !*line)
2497 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2499 memset(&xpath, 0, sizeof(struct xpath_s));
2500 pthread_cleanup_push(xpath_command_cleanup, &xpath);
2502 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
2503 if (strv_printf(&xpath.req, "%s", line) == FALSE)
2504 return send_syserror(ctx, ENOMEM);
2507 xpath.xp = xmlXPathNewContext(client->doc);
2509 if (!xpath.xp) {
2510 xpath_command_cleanup(&xpath);
2511 return send_error(ctx, EPWMD_LIBXML_ERROR);
2514 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
2516 if (!xpath.result) {
2517 xpath_command_cleanup(&xpath);
2518 return send_error(ctx, EPWMD_LIBXML_ERROR);
2521 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
2522 rc = EPWMD_EMPTY_ELEMENT;
2523 goto fail;
2526 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
2527 (xmlChar *)xpath.req[1], &xpath.buf);
2529 if (rc)
2530 goto fail;
2531 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
2532 rc = EPWMD_EMPTY_ELEMENT;
2533 goto fail;
2535 else if (xpath.req[1])
2536 goto fail;
2538 pthread_testcancel();
2539 rc = assuan_send_data(ctx, xmlBufferContent(xpath.buf),
2540 xmlBufferLength(xpath.buf));
2541 pthread_testcancel();
2543 fail:
2544 if (1) {}
2545 pthread_cleanup_pop(1);
2546 return send_error(ctx, rc);
2549 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2550 gsize len)
2552 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2553 gpg_error_t rc = file_modified(client);
2554 gchar **req, **path = NULL, **path_orig = NULL, *content;
2555 xmlDocPtr doc;
2556 xmlNodePtr n, root, copy;
2558 if (assuan_rc || rc) {
2559 if (line)
2560 xfree(line);
2561 return assuan_rc ? assuan_rc : rc;
2564 req = split_input_line((gchar *)line, " ", 2);
2565 xfree(line);
2567 if (!req || !*req)
2568 return EPWMD_COMMAND_SYNTAX;
2570 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2571 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2572 return EPWMD_COMMAND_SYNTAX;
2575 content = req[1];
2577 if (!content || !*content) {
2578 rc = EPWMD_COMMAND_SYNTAX;
2579 goto fail;
2582 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2583 rc = EPWMD_INVALID_ELEMENT;
2584 goto fail;
2587 if (valid_element_path(path+1, FALSE) == FALSE) {
2588 rc = EPWMD_INVALID_ELEMENT;
2589 goto fail;
2592 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2594 if (!doc) {
2595 rc = EPWMD_LIBXML_ERROR;
2596 goto fail;
2599 root = xmlDocGetRootElement(doc);
2600 path_orig = g_strdupv(path);
2602 if (!path_orig) {
2603 xmlFreeDoc(doc);
2604 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2605 rc = gpg_error_from_errno(ENOMEM);
2606 goto fail;
2609 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2610 g_strfreev(path_orig);
2611 xmlFreeDoc(doc);
2612 rc = gpg_error_from_errno(ENOMEM);
2613 goto fail;
2616 n = find_account(client->doc, &path, &rc, NULL, 0);
2618 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2619 g_strfreev(path_orig);
2620 xmlFreeDoc(doc);
2621 goto fail;
2623 else if (!rc) {
2624 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2626 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2627 g_strfreev(path_orig);
2628 xmlFreeDoc(doc);
2629 goto fail;
2631 else if (!rc) {
2632 xmlNodePtr parent = n->parent;
2634 xmlUnlinkNode(n);
2635 xmlFreeNode(n);
2636 n = parent;
2640 g_strfreev(path);
2641 path = path_orig;
2643 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2644 n = create_element_path(client, &path, &rc);
2646 if (rc) {
2647 xmlFreeDoc(doc);
2648 goto fail;
2652 copy = xmlCopyNode(root, 1);
2653 n = xmlAddChild(n, copy);
2654 xmlFreeDoc(doc);
2656 if (!n)
2657 rc = EPWMD_LIBXML_ERROR;
2659 fail:
2660 g_strfreev(path);
2661 g_strfreev(req);
2662 client->inquire_status = INQUIRE_DONE;
2663 return rc;
2666 static int import_command(assuan_context_t ctx, gchar *line)
2668 gpg_error_t rc;
2669 struct client_s *client = assuan_get_pointer(ctx);
2671 rc = file_modified(client);
2673 if (rc)
2674 return send_error(ctx, rc);
2676 pthread_testcancel();
2677 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2678 pthread_testcancel();
2680 if (rc)
2681 return send_error(ctx, rc);
2683 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2684 client->inquire_status = INQUIRE_BUSY;
2685 return 0;
2688 static int lock_command(assuan_context_t ctx, gchar *line)
2690 gpg_error_t rc;
2691 struct client_s *client = assuan_get_pointer(ctx);
2693 rc = file_modified(client);
2695 if (rc)
2696 return send_error(ctx, rc);
2698 rc = lock_file_mutex(client);
2700 if (!rc)
2701 client->is_lock_cmd = TRUE;
2703 return send_error(ctx, rc);
2706 static int unlock_command(assuan_context_t ctx, gchar *line)
2708 struct client_s *client = assuan_get_pointer(ctx);
2709 gpg_error_t rc = file_modified(client);
2711 if (rc)
2712 return send_error(ctx, rc);
2714 unlock_file_mutex(client);
2715 return send_error(ctx, 0);
2718 static int getpid_command(assuan_context_t ctx, gchar *line)
2720 gpg_error_t rc;
2721 gchar buf[32];
2722 pid_t pid = getpid();
2724 print_fmt(buf, sizeof(buf), "%i", pid);
2725 pthread_testcancel();
2726 rc = assuan_send_data(ctx, buf, strlen(buf));
2727 pthread_testcancel();
2728 return send_error(ctx, rc);
2731 static int version_command(assuan_context_t ctx, gchar *line)
2733 gpg_error_t rc;
2734 gchar buf[32];
2736 print_fmt(buf, sizeof(buf), "%s", PACKAGE_VERSION);
2737 pthread_testcancel();
2738 rc = assuan_send_data(ctx, buf, strlen(buf));
2739 pthread_testcancel();
2740 return send_error(ctx, rc);
2743 static void bye_notify(assuan_context_t ctx)
2745 struct client_s *cl = assuan_get_pointer(ctx);
2746 #ifdef WITH_GNUTLS
2747 gint rc;
2749 if (!cl->thd->remote)
2750 return;
2752 do {
2753 rc = gnutls_bye(cl->thd->tls->ses, GNUTLS_SHUT_RDWR);
2754 } while (rc == GNUTLS_E_AGAIN);
2755 #endif
2757 /* This will let assuan_process_next() return. */
2758 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
2761 static void reset_notify(assuan_context_t ctx)
2763 struct client_s *cl = assuan_get_pointer(ctx);
2765 if (cl)
2766 cleanup_client(cl);
2769 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2771 gchar name[32] = {0}, value[256] = {0};
2773 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2774 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2776 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2777 struct client_s *cl = assuan_get_pointer(ctx);
2779 if (cl->thd->name)
2780 g_free(cl->thd->name);
2782 cl->thd->name = g_strdup(value);
2783 log_write("OPTION CLIENT %s", line);
2785 else
2786 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2788 return 0;
2791 static int option_handler(assuan_context_t ctx, const gchar *name,
2792 const gchar *value)
2794 struct client_s *client = assuan_get_pointer(ctx);
2796 if (!value || !*value)
2797 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2799 if (g_strcasecmp(name, (gchar *)"client") == 0)
2800 return parse_client_option(ctx, value);
2802 if (g_strcasecmp(name, (gchar *)"iterations") == 0) {
2803 long n;
2804 gchar *p = NULL;
2806 errno = 0;
2807 n = strtol(value, &p, 10);
2809 if (errno || (p && *p) || n < 0)
2810 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2812 g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", (guint)n);
2813 send_status_all(STATUS_CONFIG);
2815 #ifdef WITH_PINENTRY
2816 else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2817 g_free(client->pinentry->ttyname);
2818 client->pinentry->ttyname = g_strdup(value);
2820 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2821 g_free(client->pinentry->ttytype);
2822 client->pinentry->ttytype = g_strdup(value);
2824 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2825 g_free(client->pinentry->display);
2826 client->pinentry->display = g_strdup(value);
2828 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2829 g_free(client->pinentry->path);
2830 client->pinentry->path = g_strdup(value);
2832 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2833 g_free(client->pinentry->title);
2834 client->pinentry->title = g_strdup(value);
2836 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2837 g_free(client->pinentry->prompt);
2838 client->pinentry->prompt = g_strdup(value);
2840 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2841 g_free(client->pinentry->desc);
2842 client->pinentry->desc = g_strdup(value);
2845 * Look at client_thread() to see how this works.
2847 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2848 gchar *p = NULL;
2849 gint n = strtol(value, &p, 10);
2851 if (*p || n < 0)
2852 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2854 client->pinentry->timeout = n;
2856 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2857 gchar *p = NULL;
2858 gint n = strtol(value, &p, 10);
2860 if (*p || n < 0 || n > 1)
2861 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2863 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2865 #endif
2866 else
2867 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2869 log_write("OPTION %s=%s", name, value);
2870 return 0;
2873 gpg_error_t register_commands(assuan_context_t ctx)
2875 static struct {
2876 const gchar *name;
2877 gint (*handler)(assuan_context_t, gchar *line);
2878 } table[] = {
2879 { "OPEN", open_command },
2880 { "SAVE", save_command },
2881 { "LIST", list_command },
2882 { "REALPATH", realpath_command },
2883 { "STORE", store_command },
2884 { "DELETE", delete_command },
2885 { "GET", get_command },
2886 { "ATTR", attr_command },
2887 { "ISCACHED", iscached_command },
2888 { "CLEARCACHE", clearcache_command },
2889 { "CACHETIMEOUT", cachetimeout_command },
2890 { "GETCONFIG", getconfig_command },
2891 { "DUMP", dump_command },
2892 { "XPATH", xpath_command },
2893 { "IMPORT", import_command },
2894 { "LOCK", lock_command },
2895 { "UNLOCK", unlock_command },
2896 { "GETPID", getpid_command },
2897 { "VERSION", version_command },
2898 { "INPUT", NULL },
2899 { "OUTPUT", NULL },
2900 { NULL, NULL }
2902 gint i, rc;
2904 for (i=0; table[i].name; i++) {
2905 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2907 if (rc)
2908 return rc;
2911 rc = assuan_register_bye_notify(ctx, bye_notify);
2913 if (rc)
2914 return rc;
2916 rc = assuan_register_option_handler(ctx, option_handler);
2918 if (rc)
2919 return rc;
2921 rc = assuan_register_reset_notify(ctx, reset_notify);
2923 if (rc)
2924 return rc;
2926 return assuan_register_post_cmd_notify(ctx, command_finalize);
2929 gpg_error_t try_xml_decrypt(assuan_context_t ctx, guchar *key,
2930 struct client_crypto_s *crypto, gpointer *dst, gsize *dst_len)
2932 gsize insize, len;
2933 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2934 guint iter = 0, n_iter = 0, iter_progress = 0;
2935 gint zrc = 0;
2936 gulong outsize = 0;
2937 gpg_error_t rc;
2938 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->fh1) : sizeof(crypto->fh->fh2);
2939 glong fh_iter = crypto->fh->v1 ? crypto->fh->fh1.iter : crypto->fh->fh2.iter;
2941 lseek(crypto->fh->fd, fh_size, SEEK_SET);
2942 insize = crypto->fh->st.st_size - fh_size;
2943 crypto->iv = gcry_malloc(gcryblocksize);
2945 if (!crypto->iv) {
2946 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2947 return gpg_error_from_errno(ENOMEM);
2950 /* No encryption iterations. This is a plain (gzipped) file. */
2951 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0)) {
2953 * cache_file_count() needs both .used == TRUE and a valid key in
2954 * order for it to count as a used cache entry. Fixes CACHE status
2955 * messages.
2957 memset(key, '!', gcrykeysize);
2960 if (crypto->fh->v1)
2961 memcpy(crypto->iv, crypto->fh->fh1.iv, gcryblocksize);
2962 else
2963 memcpy(crypto->iv, crypto->fh->fh2.iv, gcryblocksize);
2965 crypto->inbuf = gcry_malloc(insize);
2967 if (!crypto->inbuf) {
2968 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2969 return gpg_error_from_errno(ENOMEM);
2972 crypto->insize = insize;
2973 len = read(crypto->fh->fd, crypto->inbuf, crypto->insize);
2975 if (len != crypto->insize)
2976 return GPG_ERR_INV_LENGTH;
2978 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0))
2979 goto decompress;
2981 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
2982 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2983 return rc;
2986 if ((rc = gcry_cipher_setkey(crypto->gh, key, gcrykeysize))) {
2987 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2988 return rc;
2991 iter_progress = (guint)get_key_file_integer(client && client->filename ?
2992 client->filename : "global", "iteration_progress");
2994 if (iter_progress > 0 && fh_iter >= iter_progress) {
2995 rc = send_status(ctx, STATUS_DECRYPT, "%u %u", 0, fh_iter);
2997 if (rc)
2998 return rc;
3001 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3003 if (rc)
3004 return rc;
3006 crypto->tkey = gcry_malloc(gcrykeysize);
3008 if (!crypto->tkey) {
3009 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
3010 return gpg_error_from_errno(ENOMEM);
3013 memcpy(crypto->tkey, key, gcrykeysize);
3014 guchar *tkey = crypto->tkey;
3015 tkey[0] ^= 1;
3017 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
3018 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3019 return rc;
3022 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
3023 if (iter_progress > 0 && iter >= iter_progress) {
3024 if (!(iter % iter_progress)) {
3025 rc = send_status(ctx, STATUS_DECRYPT, "%u %u",
3026 ++n_iter * iter_progress, fh_iter);
3028 if (rc)
3029 return rc;
3033 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3034 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3035 return rc;
3038 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3040 if (rc) {
3041 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3042 return rc;
3045 iter++;
3048 if (iter_progress && fh_iter >= iter_progress) {
3049 rc = send_status(ctx, STATUS_DECRYPT, "%u %u", fh_iter, fh_iter);
3051 if (rc)
3052 return rc;
3055 decompress:
3056 if (do_decompress(ctx, crypto->inbuf, crypto->insize,
3057 (gpointer *)&crypto->outbuf, &outsize, &zrc) == FALSE) {
3058 if (zrc == Z_MEM_ERROR)
3059 return gpg_error_from_errno(ENOMEM);
3060 else
3061 return EPWMD_BADKEY; // Not a valid gzip header. Must be a bad key.
3064 if (g_strncasecmp(crypto->outbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
3065 gcry_free(crypto->outbuf);
3066 crypto->outbuf = NULL;
3067 return EPWMD_BADKEY;
3070 if (ctx) {
3071 client->xml = crypto->outbuf;
3072 client->len = outsize;
3073 crypto->outbuf = NULL;
3075 else if (dst) {
3076 *dst = crypto->outbuf;
3077 *dst_len = outsize;
3078 crypto->outbuf = NULL;
3081 /* The calling function should free the crypto struct. */
3082 return 0;
3086 * This is called after every Assuan command.
3088 void command_finalize(assuan_context_t ctx, gint rc)
3090 struct client_s *client = assuan_get_pointer(ctx);
3092 if (!client->is_lock_cmd)
3093 unlock_file_mutex(client);