Thread cancellation tests in do_compress() and do_decompress().
[pwmd.git] / src / commands.c
blobdfe17d0449147c171e9212a9d483e97d10eb170c
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 struct gz_s {
54 z_stream z;
55 gpointer out;
56 gboolean done;
57 status_msg_t which;
60 static void *z_alloc(void *data, unsigned items, unsigned size)
62 return gcry_calloc(items, size);
65 static void z_free(void *data, void *p)
67 gcry_free(p);
70 static gpg_error_t file_modified(struct client_s *client)
72 struct stat st;
73 gpg_error_t rc;
75 if (client->state != STATE_OPEN)
76 return EPWMD_NO_FILE;
78 rc = lock_file_mutex(client);
80 if (rc)
81 return rc;
83 if (lstat(client->filename, &st) == 0 && client->mtime) {
84 if (client->mtime != st.st_mtime)
85 return EPWMD_FILE_MODIFIED;
88 pthread_testcancel();
89 return 0;
92 static gpg_error_t parse_xml(assuan_context_t ctx)
94 struct client_s *client = assuan_get_pointer(ctx);
96 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
98 if (!client->doc)
99 return EPWMD_LIBXML_ERROR;
101 return 0;
104 void unlock_file_mutex(struct client_s *client)
106 struct file_mutex_s *m;
108 #ifdef WITH_PINENTRY
109 if (client->has_lock == FALSE || client->pinentry->status != PINENTRY_NONE)
110 #else
111 if (client->has_lock == FALSE)
112 #endif
113 return;
115 CACHE_LOCK(client->ctx);
117 if (cache_get_mutex(client->md5file, &m) == FALSE) {
118 CACHE_UNLOCK;
119 return;
122 CACHE_UNLOCK;
123 MUTEX_UNLOCK(&m->mutex);
124 client->has_lock = client->is_lock_cmd = FALSE;
127 gpg_error_t lock_file_mutex(struct client_s *client)
129 struct file_mutex_s *m;
131 if (client->has_lock == TRUE)
132 return 0;
134 CACHE_LOCK(client->ctx);
136 if (cache_get_mutex(client->md5file, &m) == FALSE) {
137 CACHE_UNLOCK;
138 return 0;
141 CACHE_UNLOCK;
143 if (pthread_mutex_trylock(&m->mutex) == EBUSY) {
144 if (client->ctx) {
146 * If a client disconnects unexpectedly while waiting for a
147 * lock, this lets the thread terminate because send_status()
148 * will return an error.
150 while (pthread_mutex_trylock(&m->mutex) == EBUSY) {
151 gpg_error_t rc = send_status(client->ctx, STATUS_LOCKED, NULL);
153 if (rc)
154 return rc;
156 sleep(1);
159 else {
160 MUTEX_LOCK(&m->mutex);
163 else {
164 MUTEX_LOCK_DEBUG;
167 client->has_lock = TRUE;
168 return 0;
171 void free_client(struct client_s *client)
173 if (client->doc)
174 xmlFreeDoc(client->doc);
176 if (client->xml)
177 gcry_free(client->xml);
179 if (client->filename)
180 g_free(client->filename);
182 if (client->crypto)
183 cleanup_crypto(&client->crypto);
185 if (client->xml_error)
186 xmlResetError(client->xml_error);
189 void cleanup_client(struct client_s *client)
191 assuan_context_t ctx = client->ctx;
192 struct client_thread_s *thd = client->thd;
193 gboolean has_lock = client->has_lock;
194 #ifdef WITH_PINENTRY
195 struct pinentry_s *pin = client->pinentry;
196 #endif
198 unlock_file_mutex(client);
199 CACHE_LOCK(client->ctx);
200 cache_decr_refcount(client->md5file);
203 * This may be a new file so don't use a cache slot. save_command() will
204 * set this to FALSE on success.
206 if (client->new == TRUE)
207 cache_clear(client->md5file, 1);
209 CACHE_UNLOCK;
210 free_client(client);
211 memset(client, 0, sizeof(struct client_s));
212 client->state = STATE_CONNECTED;
213 client->ctx = ctx;
214 client->thd = thd;
215 client->freed = TRUE;
216 #ifdef WITH_PINENTRY
217 client->pinentry = pin;
218 #endif
219 client->has_lock = has_lock;
222 static void gz_cleanup(void *arg)
224 struct gz_s **gz = (struct gz_s **)arg;
226 if (!gz)
227 return;
229 if (!(*gz)->done && (*gz)->out)
230 gcry_free((*gz)->out);
233 if ((*gz)->which == STATUS_COMPRESS) {
234 if ((*gz)->z.zalloc)
235 deflateEnd(&(*gz)->z);
237 else {
238 if ((*gz)->z.zalloc)
239 inflateEnd(&(*gz)->z);
242 g_free(*gz);
243 *gz = NULL;
246 gboolean do_decompress(assuan_context_t ctx, gpointer in, gulong insize,
247 gpointer *out, gulong *outsize, gint *rc)
249 struct gz_s *gz;
250 gz_header h;
251 gchar buf[17];
253 gz = g_malloc0(sizeof(struct gz_s));
255 if (!gz) {
256 *rc = gpg_error_from_errno(ENOMEM);
257 return FALSE;
260 gz->which = STATUS_DECOMPRESS;
261 pthread_cleanup_push(gz_cleanup, &gz);
262 gz->z.zalloc = z_alloc;
263 gz->z.zfree = z_free;
264 gz->z.next_in = in;
265 gz->z.avail_in = (uInt)insize;
266 gz->z.avail_out = zlib_bufsize;
267 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
269 if (!gz->out) {
270 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
271 *rc = Z_MEM_ERROR;
272 gz_cleanup(&gz);
273 return FALSE;
276 *rc = inflateInit2(&gz->z, 47);
278 if (*rc != Z_OK) {
279 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
280 gz_cleanup(&gz);
281 return FALSE;
284 memset(&h, 0, sizeof(gz_header));
285 h.comment = (guchar *)buf;
286 h.comm_max = sizeof(buf);
287 *rc = inflateGetHeader(&gz->z, &h);
289 if (*rc != Z_OK) {
290 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
291 gz_cleanup(&gz);
292 return FALSE;
295 *rc = inflate(&gz->z, Z_BLOCK);
297 if (*rc != Z_OK) {
298 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
299 gz_cleanup(&gz);
300 return FALSE;
303 if (h.comment)
304 insize = (gulong)strtol((gchar *)h.comment, NULL, 10);
306 do {
307 gpointer p;
309 *rc = inflate(&gz->z, Z_FINISH);
311 switch (*rc) {
312 case Z_OK:
313 break;
314 case Z_BUF_ERROR:
315 if (!gz->z.avail_out) {
316 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
318 if (!p) {
319 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
320 *rc = Z_MEM_ERROR;
321 goto fail;
324 gz->out = p;
325 gz->z.next_out = gz->out + gz->z.total_out;
326 gz->z.avail_out = zlib_bufsize;
327 pthread_cleanup_push(gz_cleanup, &gz);
328 pthread_testcancel();
329 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li",
330 gz->z.total_out, insize);
331 pthread_testcancel();
332 pthread_cleanup_pop(0);
334 if (*rc)
335 goto fail;
337 break;
338 case Z_STREAM_END:
339 break;
340 default:
341 goto fail;
342 break;
344 } while (*rc != Z_STREAM_END);
346 pthread_testcancel();
347 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li", gz->z.total_out,
348 insize);
349 pthread_testcancel();
351 if (*rc)
352 goto fail;
354 *out = gz->out;
355 *outsize = gz->z.total_out;
356 gz->done = TRUE;
357 gz_cleanup(&gz);
358 *rc = 0;
359 return TRUE;
361 fail:
362 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
363 pthread_cleanup_pop(1);
364 return FALSE;
367 static void read_file_header_handler(void *arg)
369 close((int)arg);
372 file_header_internal_t *read_file_header(const gchar *filename, gboolean v1,
373 gpg_error_t *rc)
375 gint fd;
376 gsize len;
377 file_header_internal_t *fh = g_malloc0(sizeof(file_header_internal_t));
378 gsize fh_size;
380 *rc = 0;
382 if (!fh) {
383 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
384 *rc = gpg_error_from_errno(ENOMEM);
385 return NULL;
388 fh_size = v1 ? sizeof(fh->fh1) : sizeof(fh->fh2);
390 if (lstat(filename, &fh->st) == -1) {
391 *rc = gpg_error_from_syserror();
392 g_free(fh);
393 return NULL;
396 if (!S_ISREG(fh->st.st_mode)) {
397 *rc = GPG_ERR_ENOANO;
398 g_free(fh);
399 return NULL;
402 fd = open(filename, O_RDONLY);
404 if (fd == -1) {
405 *rc = gpg_error_from_errno(errno);
406 g_free(fh);
407 return NULL;
410 if (v1)
411 len = read(fd, &fh->fh1, fh_size);
412 else
413 len = read(fd, &fh->fh2, fh_size);
415 if (len != fh_size) {
416 gint n = errno;
417 pthread_cleanup_push(read_file_header_handler, (void *)fd);
418 g_free(fh);
419 pthread_cleanup_pop(1);
420 *rc = gpg_error_from_errno(n);
421 return NULL;
424 fh->v1 = v1;
425 fh->fd = fd;
426 return fh;
429 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar *key,
430 gboolean cached)
432 struct client_s *client = assuan_get_pointer(ctx);
433 gpg_error_t rc;
434 gint timeout;
436 /* New file. */
437 if (!client->crypto->fh) {
438 if (key[0])
439 goto update_cache;
441 goto done;
444 rc = try_xml_decrypt(ctx, key, client->crypto, NULL, NULL);
446 if (rc) {
447 cleanup_client(client);
448 return send_error(ctx, rc);
451 update_cache:
452 CACHE_LOCK(client->ctx);
454 if (cached == FALSE) {
455 if (cache_update_key(client->md5file, key) == FALSE) {
456 cleanup_client(client);
457 CACHE_UNLOCK;
458 return send_syserror(ctx, ENOMEM);
461 timeout = get_key_file_integer(client->filename, "cache_timeout");
462 cache_reset_timeout(client->md5file, timeout);
464 else
465 cache_set_timeout(client->md5file, -2);
467 CACHE_UNLOCK;
469 done:
470 rc = parse_xml(ctx);
472 if (client->xml) {
473 gcry_free(client->xml);
474 client->xml = NULL;
477 if (!rc) {
478 if (client->new == FALSE)
479 send_status_all(STATUS_CACHE);
481 client->state = STATE_OPEN;
484 if (!rc && client->new == FALSE &&
485 client->crypto->fh->fh2.iter != (guint)get_key_file_integer(client->filename, "iterations")) {
486 g_key_file_set_integer(keyfileh, client->filename, "iterations",
487 client->crypto->fh->fh2.iter);
488 send_status_all(STATUS_CONFIG);
491 if (!rc)
492 log_write("OPEN '%s'", client->filename);
494 cleanup_crypto(&client->crypto);
495 return send_error(ctx, rc);
498 #ifdef WITH_GNUTLS
499 static gboolean validate_access(struct client_s *cl, const gchar *filename)
501 gchar *access = get_key_file_string(filename, "tcp_access");
502 gchar **list, **p;
504 if (!access)
505 return TRUE;
507 list = g_strsplit(access, ",", -1);
508 g_free(access);
510 if (!list) {
511 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
512 return FALSE;
515 for (p = list; *p; p++) {
516 gboolean not = FALSE;
517 gchar *fp = *p;
519 if (*fp == '!') {
520 not = TRUE;
521 fp++;
523 if (!*fp)
524 break;
527 if (strcasecmp(cl->thd->tls->fp, fp) == 0) {
528 if (not == TRUE)
529 continue;
531 g_strfreev(list);
532 return TRUE;
536 /* Not allowed. */
537 g_strfreev(list);
538 return FALSE;
540 #endif
542 static void req_cleanup(void *arg)
544 if (!arg)
545 return;
547 g_strfreev((gchar **)arg);
550 static int open_command(assuan_context_t ctx, char *line)
552 gboolean cached = FALSE;
553 gpg_error_t rc;
554 struct client_s *client = assuan_get_pointer(ctx);
555 gchar **req;
556 gchar *filename = NULL;
558 if ((req = split_input_line(line, " ", 2)) != NULL)
559 filename = req[0];
561 pthread_cleanup_push(req_cleanup, req);
563 if (!filename || !*filename) {
564 g_strfreev(req);
565 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
568 if (valid_filename(filename) == FALSE) {
569 g_strfreev(req);
570 return send_error(ctx, EPWMD_INVALID_FILENAME);
573 if (client->state == STATE_OPEN)
574 cleanup_client(client);
576 #ifdef WITH_GNUTLS
577 if (client->thd->remote == TRUE) {
578 if (validate_access(client, filename) == FALSE) {
579 log_write(N_("client validation failed for file '%s'"), filename);
580 g_strfreev(req);
581 return send_error(ctx, EPWMD_FILE_ACCESS);
584 #endif
586 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
587 CACHE_LOCK(client->ctx);
589 if (cache_has_file(client->md5file) == FALSE) {
590 if (cache_add_file(client->md5file, NULL) == FALSE) {
591 g_strfreev(req);
592 CACHE_UNLOCK;
593 return send_syserror(ctx, ENOMEM);
597 cache_incr_refcount(client->md5file);
598 CACHE_UNLOCK;
599 rc = lock_file_mutex(client);
601 if (rc) {
602 g_strfreev(req);
603 return send_error(ctx, rc);
606 client->freed = FALSE;
607 client->crypto = init_client_crypto();
609 if (!client->crypto) {
610 g_strfreev(req);
611 cleanup_client(client);
612 return send_syserror(ctx, ENOMEM);
615 client->crypto->key = gcry_malloc(gcrykeysize);
617 if (!client->crypto->key) {
618 g_strfreev(req);
619 log_write("%s(%i): %s", __FUNCTION__, __LINE__,
620 gpg_error_from_errno(ENOMEM));
621 cleanup_client(client);
622 return send_syserror(ctx, ENOMEM);
625 memset(client->crypto->key, 0, gcrykeysize);
626 client->crypto->fh = read_file_header(filename, FALSE, &rc);
627 pthread_testcancel();
629 if (!client->crypto->fh) {
630 if (gpg_err_code_to_errno(rc) != ENOENT) {
631 log_write("%s: %s", filename, pwmd_strerror(rc));
632 g_strfreev(req);
633 cleanup_client(client);
634 return send_error(ctx, rc);
638 * New files don't need a key.
640 if ((client->xml = new_document()) == NULL) {
641 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
642 g_strfreev(req);
643 cleanup_client(client);
644 return send_syserror(ctx, ENOMEM);
647 client->len = xmlStrlen(client->xml);
648 client->new = TRUE;
649 client->filename = g_strdup(filename);
651 if (!client->filename) {
652 g_strfreev(req);
653 cleanup_client(client);
654 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
655 return send_syserror(ctx, ENOMEM);
658 if (req[1] && *req[1])
659 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
660 strlen(req[1]));
662 g_strfreev(req);
663 #ifdef WITH_PINENTRY
664 client->pinentry->filename = g_strdup(client->filename);
666 if (!client->pinentry->filename) {
667 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
668 cleanup_client(client);
669 return send_syserror(ctx, ENOMEM);
671 #endif
672 return open_command_finalize(ctx, client->crypto->key, cached);
674 else
675 client->mtime = client->crypto->fh->st.st_mtime;
677 client->filename = g_strdup(filename);
679 if (!client->filename) {
680 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
681 g_strfreev(req);
682 cleanup_client(client);
683 return send_syserror(ctx, ENOMEM);
686 #ifdef WITH_PINENTRY
687 client->pinentry->filename = g_strdup(client->filename);
689 if (!client->pinentry->filename) {
690 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
691 g_strfreev(req);
692 cleanup_client(client);
693 return send_syserror(ctx, ENOMEM);
695 #endif
697 if (client->crypto->fh->fh2.iter <= 0)
698 goto done;
700 #ifdef WITH_GNUTLS
701 if (client->thd->remote == FALSE ||
702 get_key_file_boolean(client->filename, "tcp_require_key") == FALSE)
704 #endif
705 CACHE_LOCK(client->ctx);
706 cached = cache_get_key(client->md5file, client->crypto->key);
707 CACHE_UNLOCK;
708 #ifdef WITH_GNUTLS
710 else
711 cached = FALSE;
712 #endif
714 if (cached == FALSE) {
715 gchar *tmp = get_key_file_string(filename, "key_file");
717 if (tmp) {
718 g_free(tmp);
719 cleanup_client(client);
720 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
724 * No key specified and no matching filename found in the cache. Use
725 * pinentry to retrieve the key. Cannot return assuan_process_done()
726 * here otherwise the command will be interrupted. The event loop in
727 * client_thread() will poll the file descriptor waiting for it to
728 * become ready to read a pinentry_key_s which will contain the
729 * entered key or an error code. It will then call
730 * open_command_finalize() to to finish the command.
732 if (!req[1] || !*req[1]) {
733 #ifdef WITH_PINENTRY
734 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
736 /* From set_pinentry_defaults(). */
737 if (client->pinentry->enable == FALSE ||
738 (client->pinentry->enable == -1 && b == FALSE)) {
739 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
740 goto done;
743 g_strfreev(req);
744 rc = lock_pin_mutex(client);
746 if (rc) {
747 unlock_pin_mutex(client->pinentry);
748 cleanup_client(client);
749 return send_error(ctx, rc);
752 client->pinentry->which = PINENTRY_OPEN;
753 rc = pinentry_fork(ctx);
755 if (rc) {
756 unlock_pin_mutex(client->pinentry);
757 cleanup_client(client);
758 return send_error(ctx, rc);
761 // Called from pinentry iterate.
762 client->pinentry->cb = open_command_finalize;
763 client->pinentry->status = PINENTRY_INIT;
764 return 0;
765 #else
766 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
767 goto done;
768 #endif
771 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
772 strlen(req[1]));
775 done:
776 if (1) {}
777 pthread_cleanup_pop(1);
778 return open_command_finalize(ctx, client->crypto->key, cached);
781 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
782 gulong size, gpointer *out, gulong *outsize, gint *rc)
784 struct gz_s *gz;
785 gz_header h;
786 gchar buf[17];
787 gint cmd = Z_NO_FLUSH;
789 gz = g_malloc0(sizeof(struct gz_s));
791 if (!gz) {
792 *rc = gpg_error_from_errno(ENOMEM);
793 return FALSE;
796 gz->which = STATUS_COMPRESS;
797 pthread_cleanup_push(gz_cleanup, &gz);
798 gz->z.zalloc = z_alloc;
799 gz->z.zfree = z_free;
800 gz->z.next_in = data;
801 gz->z.avail_in = size < zlib_bufsize ? (uInt)size : (uInt)zlib_bufsize;
802 gz->z.avail_out = (uInt)zlib_bufsize;
803 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
805 if (!gz->out) {
806 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
807 *rc = Z_MEM_ERROR;
808 gz_cleanup(&gz);
809 return FALSE;
812 *rc = deflateInit2(&gz->z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
814 if (*rc != Z_OK) {
815 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
816 gz_cleanup(&gz);
817 return FALSE;
820 /* Rather than store the size of the uncompressed data in the file header,
821 * store it in the comment field of the gzip header. Don't give anyone too
822 * much information. Not sure why really, but it seems the right way. :)
824 memset(&h, 0, sizeof(gz_header));
825 g_snprintf(buf, sizeof(buf), "%li", size);
826 h.comment = (guchar *)buf;
827 *rc = deflateSetHeader(&gz->z, &h);
829 if (*rc != Z_OK) {
830 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
831 gz_cleanup(&gz);
832 return FALSE;
835 do {
836 gpointer p;
838 *rc = deflate(&gz->z, cmd);
840 switch (*rc) {
841 case Z_OK:
842 break;
843 case Z_BUF_ERROR:
844 if (!gz->z.avail_out) {
845 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
847 if (!p) {
848 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
849 *rc = Z_MEM_ERROR;
850 goto fail;
853 gz->out = p;
854 gz->z.next_out = gz->out + gz->z.total_out;
855 gz->z.avail_out = zlib_bufsize;
858 if (!gz->z.avail_in && gz->z.total_in < size) {
859 if (gz->z.total_in + zlib_bufsize > size)
860 gz->z.avail_in = size - gz->z.total_in;
861 else
862 gz->z.avail_in = zlib_bufsize;
864 pthread_cleanup_push(gz_cleanup, &gz);
865 pthread_testcancel();
866 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li",
867 gz->z.total_in, size);
868 pthread_testcancel();
869 pthread_cleanup_pop(0);
871 if (*rc)
872 goto fail;
875 if (gz->z.total_in >= size)
876 cmd = Z_FINISH;
878 break;
879 case Z_STREAM_END:
880 break;
881 default:
882 goto fail;
884 } while (*rc != Z_STREAM_END);
886 pthread_testcancel();
887 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li", gz->z.total_in, size);
888 pthread_testcancel();
890 if (*rc)
891 goto fail;
893 *out = gz->out;
894 *outsize = gz->z.total_out;
895 *rc = 0;
896 gz->done = TRUE;
897 gz_cleanup(&gz);
898 return TRUE;
900 fail:
901 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
902 pthread_cleanup_pop(1);
903 return FALSE;
906 #define CRYPTO_BLOCKSIZE (gcryblocksize * 1024)
908 static gpg_error_t iterate_crypto_once(struct client_s *client,
909 struct client_crypto_s *crypto, status_msg_t which)
911 gpg_error_t rc = 0;
912 gsize len = CRYPTO_BLOCKSIZE;
913 gpointer p = gcry_malloc(len);
914 gsize total = 0;
915 gpointer inbuf;
917 if (!p)
918 return gpg_err_code_from_errno(ENOMEM);
920 pthread_cleanup_push(gcry_free, p);
922 if (crypto->insize < CRYPTO_BLOCKSIZE)
923 len = crypto->insize;
925 for (;;) {
926 inbuf = crypto->inbuf + total;
927 guchar *tmp;
929 if (len + total > crypto->insize)
930 len = gcryblocksize;
932 if (which == STATUS_ENCRYPT)
933 rc = gcry_cipher_encrypt(crypto->gh, p, len, inbuf, len);
934 else
935 rc = gcry_cipher_decrypt(crypto->gh, p, len, inbuf, len);
937 if (rc)
938 goto done;
940 tmp = crypto->inbuf+total;
941 memmove(tmp, p, len);
942 total += len;
944 if (total >= crypto->insize)
945 break;
947 pthread_testcancel();
950 done:
951 if (1) {}
952 pthread_cleanup_pop(1);
953 return rc;
956 /* The crypto struct must be setup for iterations and .key. */
957 gpg_error_t do_xml_encrypt(struct client_s *client,
958 struct client_crypto_s *crypto, const gchar *filename)
960 gsize len = crypto->insize;
961 gpointer inbuf;
962 gchar *p;
963 gpg_error_t rc;
964 guint iter_progress = 0, n_iter = 0, xiter = 0;
965 gchar tmp[FILENAME_MAX];
966 struct stat st;
967 mode_t mode = 0;
969 if (!crypto->fh->fh2.iter) {
971 * cache_file_count() needs both .used == TRUE and a valid key in
972 * order for it to count as a used cache entry. Fixes CACHE status
973 * messages.
975 memset(crypto->key, '!', gcrykeysize);
976 goto write_file;
980 * Resize the existing xml buffer to the block size required by gcrypt
981 * rather than duplicating it and wasting memory.
983 if (crypto->insize / gcryblocksize) {
984 len = (crypto->insize / gcryblocksize) * gcryblocksize;
986 if (crypto->insize % gcryblocksize)
987 len += gcryblocksize;
990 inbuf = gcry_realloc(crypto->inbuf, len);
992 if (!inbuf) {
993 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
994 return gpg_error_from_errno(ENOMEM);
997 crypto->inbuf = inbuf;
998 crypto->insize = len;
999 gcry_create_nonce(crypto->fh->fh2.iv, sizeof(crypto->fh->fh2.iv));
1000 crypto->tkey = gcry_malloc(gcrykeysize);
1002 if (!crypto->tkey) {
1003 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1004 return gpg_error_from_errno(ENOMEM);
1007 memcpy(crypto->tkey, crypto->key, gcrykeysize);
1008 guchar *tkey = crypto->tkey;
1009 tkey[0] ^= 1;
1011 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
1012 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1013 return rc;
1016 iter_progress = get_key_file_integer(client ? client->filename : "global",
1017 "iteration_progress");
1019 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
1020 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1021 "%u %u", 0, crypto->fh->fh2.iter);
1023 if (rc)
1024 return rc;
1027 while (xiter < crypto->fh->fh2.iter-1) {
1028 if (iter_progress > 0 && xiter >= iter_progress) {
1029 if (!(xiter % iter_progress)) {
1030 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1031 "%u %u", ++n_iter * iter_progress, crypto->fh->fh2.iter);
1033 if (rc)
1034 return rc;
1038 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
1039 sizeof(crypto->fh->fh2.iv)))) {
1040 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1041 return rc;
1044 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1046 if (rc) {
1047 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1048 return rc;
1051 xiter++;
1054 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
1055 sizeof(crypto->fh->fh2.iv)))) {
1056 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1057 return rc;
1060 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, gcrykeysize))) {
1061 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1062 return rc;
1065 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1067 if (rc)
1068 return rc;
1070 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
1071 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1072 "%u %u", crypto->fh->fh2.iter, crypto->fh->fh2.iter);
1074 if (rc)
1075 return rc;
1078 write_file:
1079 if (filename) {
1080 if (!client && !strcmp(filename, "-")) {
1081 crypto->fh->fd = STDOUT_FILENO;
1082 goto do_write_file;
1085 if (lstat(filename, &st) == 0) {
1086 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
1089 * FIXME What if the file has an ACL?
1091 if (!(mode & S_IWUSR))
1092 return gpg_error_from_errno(EACCES);
1094 else {
1095 if (errno != ENOENT)
1096 return gpg_error_from_errno(errno);
1099 g_snprintf(tmp, sizeof(tmp), "%s.XXXXXX", filename);
1100 crypto->fh->fd = mkstemp(tmp);
1102 if (crypto->fh->fd == -1) {
1103 rc = errno;
1104 p = strrchr(tmp, '/');
1105 p++;
1106 log_write("%s: %s", p, strerror(rc));
1107 return gpg_error_from_errno(rc);
1110 else
1112 * xml_import() or convert_file() from command line.
1114 crypto->fh->fd = STDOUT_FILENO;
1116 do_write_file:
1117 crypto->fh->fh2.version = VERSION_HEX;
1118 len = write(crypto->fh->fd, &crypto->fh->fh2, sizeof(crypto->fh->fh2));
1120 if (len != sizeof(crypto->fh->fh2)) {
1121 len = errno;
1123 if (filename && strcmp(filename, "-"))
1124 unlink(tmp);
1126 return gpg_error_from_errno(len);
1129 len = write(crypto->fh->fd, crypto->inbuf, crypto->insize);
1131 if (len != crypto->insize) {
1132 len = errno;
1134 if (filename && strcmp(filename, "-"))
1135 unlink(tmp);
1137 return gpg_error_from_errno(len);
1140 if (fsync(crypto->fh->fd) == -1) {
1141 len = errno;
1143 if (filename && strcmp(filename, "-"))
1144 unlink(tmp);
1146 return gpg_error_from_errno(len);
1149 if (filename && strcmp(filename, "-")) {
1150 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
1151 gchar tmp2[FILENAME_MAX];
1153 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
1155 if (rename(filename, tmp2) == -1) {
1156 unlink(tmp);
1157 len = errno;
1158 return gpg_error_from_errno(len);
1162 if (rename(tmp, filename) == -1) {
1163 len = errno;
1164 unlink(tmp);
1165 return gpg_error_from_errno(len);
1168 if (mode)
1169 chmod(filename, mode);
1172 if (client && lstat(filename, &st) == 0)
1173 client->mtime = st.st_mtime;
1175 return 0;
1178 static gpg_error_t save_command_finalize(assuan_context_t ctx, guchar *key,
1179 gboolean cached)
1181 struct client_s *client = assuan_get_pointer(ctx);
1182 gpointer xmlbuf;
1183 gulong len, outsize = 0;
1184 guint iter;
1185 gint timeout;
1186 gpointer outbuf;
1187 gint zrc;
1188 gpg_error_t rc;
1190 if (client->crypto->key && client->crypto->key != key)
1191 gcry_free(client->crypto->key);
1193 client->crypto->key = key;
1194 xmlDocDumpFormatMemory(client->doc, (xmlChar **)&xmlbuf, (gint *)&len, 0);
1195 iter = (guint)get_key_file_integer(client->filename, "compression_level");
1197 if (iter < 0)
1198 iter = 0;
1200 if (do_compress(ctx, (gint)iter, xmlbuf, len, &outbuf, &outsize, &zrc)
1201 == FALSE) {
1202 xmlFree(xmlbuf);
1203 cleanup_crypto(&client->crypto);
1205 if (zrc == Z_MEM_ERROR)
1206 return send_syserror(ctx, ENOMEM);
1207 else
1208 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1210 else {
1211 gcry_free(xmlbuf);
1212 xmlbuf = outbuf;
1213 len = outsize;
1216 client->crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1218 if (!client->crypto->fh) {
1219 cleanup_crypto(&client->crypto);
1220 gcry_free(outbuf);
1221 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1222 return send_syserror(ctx, ENOMEM);
1225 iter = get_key_file_integer(client->filename, "iterations");
1226 client->crypto->fh->fh2.iter = iter < 0 ? 0 : iter;
1227 client->crypto->inbuf = xmlbuf;
1228 client->crypto->insize = len;
1229 rc = do_xml_encrypt(client, client->crypto, client->filename);
1231 if (rc) {
1232 cleanup_crypto(&client->crypto);
1233 return send_error(ctx, rc);
1236 timeout = get_key_file_integer(client->filename, "cache_timeout");
1237 CACHE_LOCK(client->ctx);
1239 if (cached) {
1240 cache_reset_timeout(client->md5file, timeout);
1241 CACHE_UNLOCK;
1243 if (client->new == TRUE)
1244 send_status_all(STATUS_CACHE);
1246 client->new = FALSE;
1247 cleanup_crypto(&client->crypto);
1248 return send_error(ctx, 0);
1251 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1252 CACHE_UNLOCK;
1253 cleanup_crypto(&client->crypto);
1254 return send_syserror(ctx, ENOMEM);
1257 client->new = FALSE;
1258 cache_reset_timeout(client->md5file, timeout);
1259 CACHE_UNLOCK;
1260 send_status_all(STATUS_CACHE);
1261 cleanup_crypto(&client->crypto);
1262 return send_error(ctx, 0);
1265 static int save_command(assuan_context_t ctx, char *line)
1267 gboolean cached = FALSE;
1268 struct stat st;
1269 struct client_s *client = assuan_get_pointer(ctx);
1270 gpg_error_t rc;
1272 rc = lock_file_mutex(client);
1274 if (rc)
1275 return send_error(ctx, rc);
1277 rc = file_modified(client);
1279 if (rc)
1280 return send_error(ctx, rc);
1282 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1283 return send_syserror(ctx, errno);
1285 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1286 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1287 return send_error(ctx, GPG_ERR_ENOANO);
1290 CACHE_LOCK(ctx);
1291 cached = cache_iscached(client->md5file);
1292 CACHE_UNLOCK;
1295 * If a cache entry doesn't exist for this file and the file has a
1296 * "key_file" or "key" parameter, then it's an error. The reason is that
1297 * cache expiration would be useless.
1299 if (cached == FALSE) {
1300 gchar *tmp = get_key_file_string(client->filename, "key_file");
1302 if (tmp) {
1303 g_free(tmp);
1304 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1308 cached = FALSE;
1309 client->crypto = init_client_crypto();
1311 if (!client->crypto) {
1312 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1313 return send_syserror(ctx, ENOMEM);
1316 client->crypto->key = gcry_malloc(gcrykeysize);
1318 if (!client->crypto->key) {
1319 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1320 cleanup_crypto(&client->crypto);
1321 return send_syserror(ctx, ENOMEM);
1324 memset(client->crypto->key, '!', gcrykeysize);
1326 if (get_key_file_integer(client->filename, "iterations") <= 0)
1327 goto done;
1329 if (!line || !*line) {
1330 client->crypto->tkey = gcry_malloc(gcrykeysize);
1332 if (!client->crypto->tkey) {
1333 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1334 cleanup_crypto(&client->crypto);
1335 return send_syserror(ctx, ENOMEM);
1338 memset(client->crypto->tkey, '!', gcrykeysize);
1339 CACHE_LOCK(ctx);
1341 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1342 memcmp(client->crypto->key, client->crypto->tkey,
1343 gcrykeysize) == 0) {
1344 CACHE_UNLOCK;
1346 #ifdef WITH_PINENTRY
1347 if (client->pinentry->enable == FALSE ||
1348 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1349 /* Empty keys are allowed. */
1350 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1351 goto done;
1354 lock_pin_mutex(client);
1355 client->pinentry->which = PINENTRY_SAVE;
1356 rc = pinentry_fork(ctx);
1358 if (rc) {
1359 unlock_pin_mutex(client->pinentry);
1360 return send_error(ctx, rc);
1363 client->pinentry->cb = save_command_finalize;
1364 client->pinentry->status = PINENTRY_INIT;
1365 return 0;
1366 #else
1367 /* Empty keys are allowed. */
1368 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1369 goto done;
1370 #endif
1372 else {
1373 CACHE_UNLOCK;
1374 cached = TRUE;
1377 else {
1378 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, line,
1379 strlen(line));
1380 memset(line, 0, strlen(line));
1383 done:
1384 return save_command_finalize(ctx, client->crypto->key, cached);
1387 static int delete_command(assuan_context_t ctx, char *line)
1389 struct client_s *client = assuan_get_pointer(ctx);
1390 gchar **req;
1391 gpg_error_t rc;
1392 xmlNodePtr n;
1394 rc = file_modified(client);
1396 if (rc)
1397 return send_error(ctx, rc);
1399 if (strchr(line, '\t'))
1400 req = split_input_line(line, "\t", -1);
1401 else
1402 req = split_input_line(line, " ", -1);
1404 if (!req || !*req)
1405 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1407 n = find_account(client->doc, &req, &rc, NULL, 0);
1409 if (!n) {
1410 g_strfreev(req);
1411 return send_error(ctx, rc);
1415 * No sub-node defined. Remove the entire node (account).
1417 if (!req[1]) {
1418 if (n) {
1419 xmlUnlinkNode(n);
1420 xmlFreeNode(n);
1423 g_strfreev(req);
1424 return send_error(ctx, 0);
1427 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1428 g_strfreev(req);
1430 if (!n)
1431 return send_error(ctx, rc);
1433 if (n) {
1434 xmlUnlinkNode(n);
1435 xmlFreeNode(n);
1438 return send_error(ctx, 0);
1442 * Don't return with assuan_process_done() here. This has been called from
1443 * assuan_process_next() and the command should be finished in
1444 * client_thread().
1446 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1447 gsize len)
1449 assuan_context_t ctx = data;
1450 struct client_s *client = assuan_get_pointer(ctx);
1451 gchar **req;
1452 xmlNodePtr n;
1453 gpg_error_t rc = file_modified(client);
1455 if (assuan_rc || rc) {
1456 if (line)
1457 xfree(line);
1458 return assuan_rc ? assuan_rc : rc;
1461 req = split_input_line((gchar *)line, "\t", 0);
1462 xfree(line);
1464 if (!req || !*req)
1465 return EPWMD_COMMAND_SYNTAX;
1467 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1468 g_strfreev(req);
1469 return EPWMD_INVALID_ELEMENT;
1472 if (valid_element_path(req+1, TRUE) == FALSE) {
1473 g_strfreev(req);
1474 return EPWMD_INVALID_ELEMENT;
1477 again:
1478 n = find_account(client->doc, &req, &rc, NULL, 0);
1480 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1481 rc = new_account(client->doc, *req);
1483 if (rc) {
1484 g_strfreev(req);
1485 return rc;
1488 goto again;
1491 if (!n) {
1492 g_strfreev(req);
1493 return rc;
1496 if (req[1]) {
1497 if (!n->children)
1498 create_elements_cb(n, req+1, &rc, NULL);
1499 else
1500 find_elements(client->doc, n->children, req+1, &rc,
1501 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1504 g_strfreev(req);
1505 client->inquire_status = INQUIRE_DONE;
1506 return rc;
1509 static int store_command(assuan_context_t ctx, char *line)
1511 struct client_s *client = assuan_get_pointer(ctx);
1512 gpg_error_t rc = file_modified(client);
1514 if (rc)
1515 return send_error(ctx, rc);
1517 pthread_testcancel();
1518 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1519 pthread_testcancel();
1521 if (rc)
1522 return send_error(ctx, rc);
1524 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1525 client->inquire_status = INQUIRE_BUSY;
1526 return 0;
1529 static int get_command(assuan_context_t ctx, char *line)
1531 struct client_s *client = assuan_get_pointer(ctx);
1532 gchar **req;
1533 gpg_error_t rc;
1534 xmlNodePtr n;
1536 rc = file_modified(client);
1538 if (rc)
1539 return send_error(ctx, rc);
1541 req = split_input_line(line, "\t", -1);
1543 if (!req || !*req) {
1544 g_strfreev(req);
1545 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1548 n = find_account(client->doc, &req, &rc, NULL, 0);
1550 if (!n) {
1551 g_strfreev(req);
1552 return send_error(ctx, rc);
1555 if (req[1])
1556 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1558 g_strfreev(req);
1560 if (rc)
1561 return send_error(ctx, rc);
1563 if (!n || !n->children)
1564 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1566 n = find_text_node(n->children);
1568 if (!n || !n->content || !*n->content)
1569 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1571 pthread_testcancel();
1572 rc = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1573 pthread_testcancel();
1574 return send_error(ctx, rc);
1577 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1578 gpg_error_t *rc, gchar **req_orig, void *data)
1580 gchar *path = *(gchar **)data;
1581 gchar *tmp = NULL, *result;
1583 if (path) {
1584 g_free(path);
1585 *(gchar **)data = NULL;
1588 path = g_strjoinv("\t", target);
1590 if (!path) {
1591 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1592 *rc = gpg_error_from_errno(ENOMEM);
1593 return NULL;
1596 if (req_orig) {
1597 tmp = g_strjoinv("\t", req_orig);
1599 if (!tmp) {
1600 g_free(path);
1601 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1602 *rc = gpg_error_from_errno(ENOMEM);
1603 return NULL;
1607 if (tmp && *tmp)
1608 result = g_strdup_printf("%s\t%s", path, tmp);
1609 else
1610 result = g_strdup(path);
1612 if (!result) {
1613 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1614 *rc = gpg_error_from_errno(ENOMEM);
1615 g_free(path);
1616 g_free(tmp);
1617 return NULL;
1620 g_free(path);
1621 g_free(tmp);
1622 *(gchar **)data = result;
1623 return node;
1626 static void list_command_cleanup1(void *arg);
1627 static int realpath_command(assuan_context_t ctx, char *line)
1629 gpg_error_t rc;
1630 struct client_s *client = assuan_get_pointer(ctx);
1631 gchar **req;
1632 gchar *t;
1633 gint i;
1634 xmlNodePtr n;
1635 GString *string;
1636 gchar *rp = NULL;
1638 rc = file_modified(client);
1640 if (rc)
1641 return send_error(ctx, rc);
1643 if (strchr(line, '\t') != NULL) {
1644 if ((req = split_input_line(line, "\t", 0)) == NULL)
1645 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1647 else {
1648 if ((req = split_input_line(line, " ", 0)) == NULL)
1649 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1652 n = find_account(client->doc, &req, &rc, NULL, 0);
1654 if (!n) {
1655 g_strfreev(req);
1656 return send_error(ctx, rc);
1659 rp = g_strjoinv("\t", req);
1661 if (!rp) {
1662 g_strfreev(req);
1663 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1664 return send_syserror(ctx, ENOMEM);
1667 if (req[1]) {
1668 n = find_elements(client->doc, n->children, req+1, &rc,
1669 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1671 if (!n) {
1672 g_free(rp);
1673 g_strfreev(req);
1674 return send_error(ctx, rc);
1678 string = g_string_new(rp);
1679 g_free(rp);
1680 g_strfreev(req);
1682 if (!string) {
1683 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1684 return send_syserror(ctx, ENOMEM);
1687 again:
1688 for (i = 0, t = string->str + i; *t; t++, i++) {
1689 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1690 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1691 goto again;
1695 pthread_cleanup_push(list_command_cleanup1, string);
1696 pthread_testcancel();
1697 rc = assuan_send_data(ctx, string->str, string->len);
1698 pthread_testcancel();
1699 pthread_cleanup_pop(1);
1700 return send_error(ctx, rc);
1703 static void list_command_cleanup1(void *arg)
1705 g_string_free((GString *)arg, TRUE);
1708 static void list_command_cleanup2(void *arg)
1710 struct element_list_s *elements = arg;
1712 if (elements) {
1713 gint total = g_slist_length(elements->list);
1714 gint i;
1716 for (i = 0; i < total; i++) {
1717 gchar *tmp = g_slist_nth_data(elements->list, i);
1718 g_free(tmp);
1721 g_slist_free(elements->list);
1723 if (elements->prefix)
1724 g_free(elements->prefix);
1726 g_free(elements);
1730 static int list_command(assuan_context_t ctx, char *line)
1732 struct client_s *client = assuan_get_pointer(ctx);
1733 gpg_error_t rc;
1734 struct element_list_s *elements = NULL;
1735 gchar *tmp;
1737 if (disable_list_and_dump == TRUE)
1738 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1740 rc = file_modified(client);
1742 if (rc)
1743 return send_error(ctx, rc);
1745 if (!*line) {
1746 GString *str;
1748 rc = list_accounts(client->doc, &str);
1749 pthread_cleanup_push(list_command_cleanup1, str);
1751 if (rc)
1752 return send_error(ctx, rc);
1754 pthread_testcancel();
1755 rc = assuan_send_data(ctx, str->str, str->len);
1756 pthread_testcancel();
1757 pthread_cleanup_pop(1);
1758 return send_error(ctx, rc);
1761 elements = g_malloc0(sizeof(struct element_list_s));
1763 if (!elements) {
1764 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1765 rc = gpg_err_code_from_errno(ENOMEM);
1766 goto fail;
1769 pthread_cleanup_push(list_command_cleanup2, elements);
1770 pthread_testcancel();
1771 rc = create_path_list(client->doc, elements, line);
1772 pthread_testcancel();
1774 if (rc)
1775 goto fail;
1777 if (elements) {
1778 gint total = g_slist_length(elements->list);
1779 gint i;
1780 GString *str;
1782 if (!total) {
1783 rc = EPWMD_EMPTY_ELEMENT;
1784 goto fail;
1787 str = g_string_new(NULL);
1789 if (!str) {
1790 rc = gpg_err_code_from_errno(ENOMEM);
1791 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1792 goto fail;
1795 pthread_cleanup_push(list_command_cleanup1, str);
1797 for (i = 0; i < total; i++) {
1798 tmp = g_slist_nth_data(elements->list, i);
1799 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1802 pthread_testcancel();
1803 rc = assuan_send_data(ctx, str->str, str->len);
1804 pthread_testcancel();
1805 pthread_cleanup_pop(1);
1807 else
1808 rc = EPWMD_EMPTY_ELEMENT;
1810 fail:
1811 if (1) {}
1812 pthread_cleanup_pop(1);
1813 return send_error(ctx, rc);
1816 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1817 const gchar *value)
1819 xmlAttrPtr a;
1821 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1822 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1824 if (!a)
1825 return EPWMD_LIBXML_ERROR;
1827 else
1828 xmlNodeSetContent(a->children, (xmlChar *)value);
1830 return 0;
1834 * req[0] - element path
1836 static int attribute_list(assuan_context_t ctx, gchar **req)
1838 struct client_s *client = assuan_get_pointer(ctx);
1839 gchar **attrlist = NULL;
1840 gint i = 0;
1841 gchar **path = NULL;
1842 xmlAttrPtr a;
1843 xmlNodePtr n, an;
1844 gchar *line;
1845 gpg_error_t rc;
1847 if (!req || !req[0])
1848 return EPWMD_COMMAND_SYNTAX;
1850 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1852 * The first argument may be only an account.
1854 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1855 return EPWMD_COMMAND_SYNTAX;
1858 n = find_account(client->doc, &path, &rc, NULL, 0);
1860 if (!n) {
1861 g_strfreev(path);
1862 return rc;
1865 if (path[1]) {
1866 n = find_elements(client->doc, n->children, path+1, &rc,
1867 NULL, NULL, NULL, FALSE, 0, NULL);
1869 if (!n) {
1870 g_strfreev(path);
1871 return rc;
1875 g_strfreev(path);
1877 for (a = n->properties; a; a = a->next) {
1878 gchar **pa;
1880 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1881 if (attrlist)
1882 g_strfreev(attrlist);
1884 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1885 return gpg_error_from_errno(ENOMEM);
1888 attrlist = pa;
1889 an = a->children;
1890 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
1892 if (!attrlist[i]) {
1893 g_strfreev(attrlist);
1894 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1895 return gpg_error_from_errno(ENOMEM);
1898 attrlist[++i] = NULL;
1901 if (!attrlist)
1902 return EPWMD_EMPTY_ELEMENT;
1904 line = g_strjoinv("\n", attrlist);
1906 if (!line) {
1907 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1908 g_strfreev(attrlist);
1909 return gpg_error_from_errno(ENOMEM);
1912 pthread_cleanup_push(g_free, line);
1913 pthread_cleanup_push(req_cleanup, attrlist);
1914 pthread_testcancel();
1915 rc = assuan_send_data(ctx, line, strlen(line));
1916 pthread_testcancel();
1917 pthread_cleanup_pop(1);
1918 pthread_cleanup_pop(1);
1919 return rc;
1923 * req[0] - attribute
1924 * req[1] - element path
1926 static int attribute_delete(struct client_s *client, gchar **req)
1928 xmlAttrPtr a;
1929 xmlNodePtr n;
1930 gchar **path = NULL;
1931 gpg_error_t rc;
1933 if (!req || !req[0] || !req[1])
1934 return EPWMD_COMMAND_SYNTAX;
1936 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1938 * The first argument may be only an account.
1940 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1941 return EPWMD_COMMAND_SYNTAX;
1945 * Don't remove the "name" attribute for the account element. To remove an
1946 * account use DELETE <account>.
1948 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1949 rc = EPWMD_ATTR_SYNTAX;
1950 goto fail;
1953 n = find_account(client->doc, &path, &rc, NULL, 0);
1955 if (!n)
1956 goto fail;
1958 if (path[1]) {
1959 n = find_elements(client->doc, n->children, path+1, &rc,
1960 NULL, NULL, NULL, FALSE, 0, NULL);
1962 if (!n)
1963 goto fail;
1966 g_strfreev(path);
1968 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1969 return EPWMD_ATTR_NOT_FOUND;
1971 if (xmlRemoveProp(a) == -1)
1972 return EPWMD_LIBXML_ERROR;
1974 return 0;
1976 fail:
1977 g_strfreev(path);
1978 return rc;
1981 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
1982 gpg_error_t *rc)
1984 gchar **src = *path;
1985 gchar **src_orig = g_strdupv(src);
1986 xmlNodePtr n = NULL;
1988 *rc = 0;
1990 if (!src_orig) {
1991 *rc = gpg_error_from_errno(ENOMEM);
1992 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1993 goto fail;
1996 again:
1997 n = find_account(client->doc, &src, rc, NULL, 0);
1999 if (!n) {
2000 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
2001 *rc = new_account(client->doc, src[0]);
2003 if (*rc)
2004 goto fail;
2006 goto again;
2008 else
2009 goto fail;
2012 if (src[1]) {
2013 if (!n->children)
2014 n = create_target_elements_cb(n, src+1, rc, NULL);
2015 else
2016 n = find_elements(client->doc, n->children, src+1, rc,
2017 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
2019 if (!n)
2020 goto fail;
2023 * Reset the position of the element tree now that the elements
2024 * have been created.
2026 g_strfreev(src);
2027 src = src_orig;
2028 src_orig = NULL;
2029 n = find_account(client->doc, &src, rc, NULL, 0);
2031 if (!n)
2032 goto fail;
2034 n = find_elements(client->doc, n->children, src+1, rc,
2035 NULL, NULL, NULL, FALSE, 0, NULL);
2037 if (!n)
2038 goto fail;
2041 fail:
2042 if (src_orig)
2043 g_strfreev(src_orig);
2045 *path = src;
2046 return n;
2050 * Creates a "target" attribute. When other commands encounter an element with
2051 * this attribute, the element path is modified to the target value. If the
2052 * source element path doesn't exist when using 'ATTR SET target', it is
2053 * created, but the destination element path must exist.
2055 * req[0] - source element path
2056 * req[1] - destination element path
2058 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2060 gchar **src, **dst, *line = NULL;
2061 gpg_error_t rc;
2062 xmlNodePtr n;
2064 if (!req || !req[0] || !req[1])
2065 return EPWMD_COMMAND_SYNTAX;
2067 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2069 * The first argument may be only an account.
2071 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2072 return EPWMD_COMMAND_SYNTAX;
2075 if (valid_element_path(src, FALSE) == FALSE) {
2076 g_strfreev(src);
2077 return EPWMD_INVALID_ELEMENT;
2080 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2082 * The first argument may be only an account.
2084 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2085 rc = EPWMD_COMMAND_SYNTAX;
2086 goto fail;
2090 n = find_account(client->doc, &dst, &rc, NULL, 0);
2093 * Make sure the destination element path exists.
2095 if (!n)
2096 goto fail;
2098 if (dst[1]) {
2099 n = find_elements(client->doc, n->children, dst+1, &rc,
2100 NULL, NULL, NULL, FALSE, 0, NULL);
2102 if (!n)
2103 goto fail;
2106 n = create_element_path(client, &src, &rc);
2108 if (rc)
2109 goto fail;
2111 line = g_strjoinv("\t", dst);
2113 if (!line) {
2114 rc = gpg_error_from_errno(ENOMEM);
2115 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2116 goto fail;
2119 rc = add_attribute(n, "target", line);
2121 fail:
2122 g_free(line);
2123 g_strfreev(src);
2124 g_strfreev(dst);
2125 return rc;
2129 * req[0] - account name
2130 * req[1] - new name
2132 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2134 gpg_error_t rc;
2135 gchar **tmp;
2136 xmlNodePtr n;
2138 tmp = g_strdupv(req);
2140 if (!tmp) {
2141 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2142 return gpg_error_from_errno(ENOMEM);
2145 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2146 g_strfreev(tmp);
2148 if (!n)
2149 return rc;
2151 if (g_utf8_collate(req[0], req[1]) == 0)
2152 return 0;
2155 * Will not overwrite an existing account.
2157 tmp = g_strdupv(req+1);
2159 if (!tmp) {
2160 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2161 return gpg_error_from_errno(ENOMEM);
2164 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2165 g_strfreev(tmp);
2167 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
2168 return rc;
2170 if (n)
2171 return EPWMD_ACCOUNT_EXISTS;
2174 * Whitespace not allowed in account names.
2176 if (contains_whitespace(req[1]) == TRUE)
2177 return EPWMD_ATTR_SYNTAX;
2179 tmp = g_strdupv(req);
2181 if (!tmp) {
2182 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2183 return gpg_error_from_errno(ENOMEM);
2186 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2187 g_strfreev(tmp);
2189 if (!n)
2190 return EPWMD_ELEMENT_NOT_FOUND;
2192 return add_attribute(n, "name", req[1]);
2196 * req[0] - attribute
2197 * req[1] - element path
2199 static int attribute_get(assuan_context_t ctx, gchar **req)
2201 struct client_s *client = assuan_get_pointer(ctx);
2202 xmlNodePtr n;
2203 xmlChar *a;
2204 gchar **path= NULL;
2205 gpg_error_t rc;
2207 if (!req || !req[0] || !req[1])
2208 return EPWMD_COMMAND_SYNTAX;
2210 if (strchr(req[1], '\t')) {
2211 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2212 return EPWMD_COMMAND_SYNTAX;
2214 else {
2215 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2216 return EPWMD_COMMAND_SYNTAX;
2219 n = find_account(client->doc, &path, &rc, NULL, 0);
2221 if (!n)
2222 goto fail;
2224 if (path[1]) {
2225 n = find_elements(client->doc, n->children, path+1, &rc,
2226 NULL, NULL, NULL, FALSE, 0, NULL);
2228 if (!n)
2229 goto fail;
2232 g_strfreev(path);
2234 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2235 return EPWMD_ATTR_NOT_FOUND;
2237 pthread_cleanup_push(xmlFree, a);
2238 pthread_testcancel();
2239 rc = assuan_send_data(ctx, a, xmlStrlen(a));
2240 pthread_testcancel();
2241 pthread_cleanup_pop(1);
2242 return rc;
2244 fail:
2245 g_strfreev(path);
2246 return rc;
2250 * req[0] - attribute
2251 * req[1] - element path
2252 * req[2] - value
2254 static int attribute_set(struct client_s *client, gchar **req)
2256 gchar **path = NULL;
2257 gpg_error_t rc;
2258 xmlNodePtr n;
2260 if (!req || !req[0] || !req[1] || !req[2])
2261 return EPWMD_COMMAND_SYNTAX;
2264 * Reserved attribute names.
2266 if (g_utf8_collate(req[0], "name") == 0) {
2268 * Only reserved for the account element. Not the rest of the
2269 * document.
2271 if (strchr(req[1], '\t') == NULL)
2272 return name_attribute(client, req + 1);
2274 else if (g_utf8_collate(req[0], "target") == 0)
2275 return target_attribute(client, req + 1);
2277 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2279 * The first argument may be only an account.
2281 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2282 return EPWMD_COMMAND_SYNTAX;
2285 n = find_account(client->doc, &path, &rc, NULL, 0);
2287 if (!n)
2288 goto fail;
2290 if (path[1]) {
2291 n = find_elements(client->doc, n->children, path+1, &rc,
2292 NULL, NULL, NULL, FALSE, 0, NULL);
2294 if (!n)
2295 goto fail;
2298 g_strfreev(path);
2299 return add_attribute(n, req[0], req[2]);
2301 fail:
2302 g_strfreev(path);
2303 return rc;
2307 * req[0] - command
2308 * req[1] - attribute name or element path if command is LIST
2309 * req[2] - element path
2310 * req[2] - element path or value
2312 static int attr_command(assuan_context_t ctx, char *line)
2314 struct client_s *client = assuan_get_pointer(ctx);
2315 gchar **req;
2316 gpg_error_t rc = 0;
2318 rc = file_modified(client);
2320 if (rc)
2321 return send_error(ctx, rc);
2323 req = split_input_line(line, " ", 4);
2325 if (!req || !req[0] || !req[1]) {
2326 g_strfreev(req);
2327 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2330 pthread_cleanup_push(req_cleanup, req);
2332 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2333 rc = attribute_set(client, req+1);
2334 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2335 rc = attribute_get(ctx, req+1);
2336 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2337 rc = attribute_delete(client, req+1);
2338 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2339 rc = attribute_list(ctx, req+1);
2340 else
2341 rc = EPWMD_COMMAND_SYNTAX;
2343 pthread_cleanup_pop(1);
2344 return send_error(ctx, rc);
2347 static int iscached_command(assuan_context_t ctx, char *line)
2349 gchar **req = split_input_line(line, " ", 0);
2350 guchar md5file[16];
2352 if (!req || !*req) {
2353 g_strfreev(req);
2354 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2357 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2358 g_strfreev(req);
2359 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2360 CACHE_LOCK(ctx);
2362 if (cache_iscached(md5file) == FALSE) {
2363 CACHE_UNLOCK;
2364 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2367 CACHE_UNLOCK;
2368 pthread_cleanup_pop(0);
2369 return send_error(ctx, 0);
2372 static int clearcache_command(assuan_context_t ctx, char *line)
2374 struct client_s *client = assuan_get_pointer(ctx);
2375 gchar **req = split_input_line(line, " ", 0);
2376 guchar md5file[16];
2378 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2379 CACHE_LOCK(ctx);
2381 if (!req || !*req) {
2382 g_strfreev(req);
2383 cache_clear(client->md5file, 2);
2384 CACHE_UNLOCK;
2385 return send_error(ctx, 0);
2388 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2389 g_strfreev(req);
2391 if (cache_clear(md5file, 1) == FALSE) {
2392 CACHE_UNLOCK;
2393 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2396 CACHE_UNLOCK;
2397 pthread_cleanup_pop(0);
2398 return send_error(ctx, 0);
2401 static int cachetimeout_command(assuan_context_t ctx, char *line)
2403 guchar md5file[16];
2404 glong timeout;
2405 gchar **req = split_input_line(line, " ", 0);
2406 gchar *p;
2407 struct client_s *client = assuan_get_pointer(ctx);
2409 pthread_cleanup_push(req_cleanup, req);
2411 if (!req || !*req || !req[1]) {
2412 g_strfreev(req);
2413 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2416 errno = 0;
2417 timeout = strtol(req[0], &p, 10);
2419 if (errno != 0 || *p != 0) {
2420 g_strfreev(req);
2421 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2424 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2425 pthread_cleanup_pop(1);
2426 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2427 CACHE_LOCK(client->ctx);
2429 if (cache_set_timeout(md5file, timeout) == FALSE) {
2430 CACHE_UNLOCK;
2431 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2434 CACHE_UNLOCK;
2435 pthread_cleanup_pop(0);
2436 return send_error(ctx, 0);
2439 static int dump_command(assuan_context_t ctx, char *line)
2441 xmlChar *xml;
2442 gssize len;
2443 struct client_s *client = assuan_get_pointer(ctx);
2444 gpg_error_t rc;
2446 if (disable_list_and_dump == TRUE)
2447 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2449 rc = file_modified(client);
2451 if (rc)
2452 return send_error(ctx, rc);
2454 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2456 if (!xml) {
2457 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2458 return send_syserror(ctx, ENOMEM);
2461 pthread_cleanup_push(xmlFree, xml);
2462 pthread_testcancel();
2463 rc = assuan_send_data(ctx, xml, len);
2464 pthread_testcancel();
2465 pthread_cleanup_pop(1);
2466 return send_error(ctx, rc);
2469 static int getconfig_command(assuan_context_t ctx, gchar *line)
2471 struct client_s *client = assuan_get_pointer(ctx);
2472 gpg_error_t rc = 0;
2473 gchar filename[255]={0}, param[747]={0};
2474 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2476 if (strchr(line, ' ')) {
2477 sscanf(line, " %254[^ ] %746c", filename, param);
2478 fp = filename;
2479 paramp = param;
2482 if (fp && !valid_filename(fp))
2483 return send_error(ctx, EPWMD_INVALID_FILENAME);
2485 paramp = g_ascii_strdown(paramp, -1);
2487 if (!paramp) {
2488 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2489 return send_syserror(ctx, ENOMEM);
2492 p = get_key_file_string(fp ? fp : "global", paramp);
2493 g_free(paramp);
2495 if (!p)
2496 return send_error(ctx, GPG_ERR_NO_VALUE);
2498 tmp = expand_homedir(p);
2499 g_free(p);
2501 if (!tmp) {
2502 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2503 return send_syserror(ctx, ENOMEM);
2506 p = tmp;
2507 pthread_cleanup_push(g_free, p);
2508 pthread_testcancel();
2509 rc = assuan_send_data(ctx, p, strlen(p));
2510 pthread_testcancel();
2511 pthread_cleanup_pop(1);
2512 return send_error(ctx, rc);
2515 struct xpath_s {
2516 xmlXPathContextPtr xp;
2517 xmlXPathObjectPtr result;
2518 xmlBufferPtr buf;
2519 gchar **req;
2522 static void xpath_command_cleanup(void *arg)
2524 struct xpath_s *xpath = arg;
2526 req_cleanup(xpath->req);
2528 if (xpath->buf)
2529 xmlBufferFree(xpath->buf);
2531 if (xpath->result)
2532 xmlXPathFreeObject(xpath->result);
2534 if (xpath->xp)
2535 xmlXPathFreeContext(xpath->xp);
2538 static int xpath_command(assuan_context_t ctx, gchar *line)
2540 struct client_s *client = assuan_get_pointer(ctx);
2541 gpg_error_t rc;
2542 struct xpath_s xpath;
2544 if (disable_list_and_dump == TRUE)
2545 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2547 rc = file_modified(client);
2549 if (rc)
2550 return send_error(ctx, rc);
2552 if (!line || !*line)
2553 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2555 memset(&xpath, 0, sizeof(struct xpath_s));
2556 pthread_cleanup_push(xpath_command_cleanup, &xpath);
2558 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
2559 if (strv_printf(&xpath.req, "%s", line) == FALSE)
2560 return send_syserror(ctx, ENOMEM);
2563 xpath.xp = xmlXPathNewContext(client->doc);
2565 if (!xpath.xp) {
2566 xpath_command_cleanup(&xpath);
2567 return send_error(ctx, EPWMD_LIBXML_ERROR);
2570 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
2572 if (!xpath.result) {
2573 xpath_command_cleanup(&xpath);
2574 return send_error(ctx, EPWMD_LIBXML_ERROR);
2577 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
2578 rc = EPWMD_EMPTY_ELEMENT;
2579 goto fail;
2582 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
2583 (xmlChar *)xpath.req[1], &xpath.buf);
2585 if (rc)
2586 goto fail;
2587 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
2588 rc = EPWMD_EMPTY_ELEMENT;
2589 goto fail;
2591 else if (xpath.req[1])
2592 goto fail;
2594 pthread_testcancel();
2595 rc = assuan_send_data(ctx, xmlBufferContent(xpath.buf),
2596 xmlBufferLength(xpath.buf));
2597 pthread_testcancel();
2599 fail:
2600 if (1) {}
2601 pthread_cleanup_pop(1);
2602 return send_error(ctx, rc);
2605 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2606 gsize len)
2608 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2609 gpg_error_t rc = file_modified(client);
2610 gchar **req, **path = NULL, **path_orig = NULL, *content;
2611 xmlDocPtr doc;
2612 xmlNodePtr n, root, copy;
2614 if (assuan_rc || rc) {
2615 if (line)
2616 xfree(line);
2617 return assuan_rc ? assuan_rc : rc;
2620 req = split_input_line((gchar *)line, " ", 2);
2621 xfree(line);
2623 if (!req || !*req)
2624 return EPWMD_COMMAND_SYNTAX;
2626 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2627 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2628 return EPWMD_COMMAND_SYNTAX;
2631 content = req[1];
2633 if (!content || !*content) {
2634 rc = EPWMD_COMMAND_SYNTAX;
2635 goto fail;
2638 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2639 rc = EPWMD_INVALID_ELEMENT;
2640 goto fail;
2643 if (valid_element_path(path+1, FALSE) == FALSE) {
2644 rc = EPWMD_INVALID_ELEMENT;
2645 goto fail;
2648 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2650 if (!doc) {
2651 rc = EPWMD_LIBXML_ERROR;
2652 goto fail;
2655 root = xmlDocGetRootElement(doc);
2656 path_orig = g_strdupv(path);
2658 if (!path_orig) {
2659 xmlFreeDoc(doc);
2660 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2661 rc = gpg_error_from_errno(ENOMEM);
2662 goto fail;
2665 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2666 g_strfreev(path_orig);
2667 xmlFreeDoc(doc);
2668 rc = gpg_error_from_errno(ENOMEM);
2669 goto fail;
2672 n = find_account(client->doc, &path, &rc, NULL, 0);
2674 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2675 g_strfreev(path_orig);
2676 xmlFreeDoc(doc);
2677 goto fail;
2679 else if (!rc) {
2680 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2682 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2683 g_strfreev(path_orig);
2684 xmlFreeDoc(doc);
2685 goto fail;
2687 else if (!rc) {
2688 xmlNodePtr parent = n->parent;
2690 xmlUnlinkNode(n);
2691 xmlFreeNode(n);
2692 n = parent;
2696 g_strfreev(path);
2697 path = path_orig;
2699 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2700 n = create_element_path(client, &path, &rc);
2702 if (rc) {
2703 xmlFreeDoc(doc);
2704 goto fail;
2708 copy = xmlCopyNode(root, 1);
2709 n = xmlAddChild(n, copy);
2710 xmlFreeDoc(doc);
2712 if (!n)
2713 rc = EPWMD_LIBXML_ERROR;
2715 fail:
2716 g_strfreev(path);
2717 g_strfreev(req);
2718 client->inquire_status = INQUIRE_DONE;
2719 return rc;
2722 static int import_command(assuan_context_t ctx, gchar *line)
2724 gpg_error_t rc;
2725 struct client_s *client = assuan_get_pointer(ctx);
2727 rc = file_modified(client);
2729 if (rc)
2730 return send_error(ctx, rc);
2732 pthread_testcancel();
2733 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2734 pthread_testcancel();
2736 if (rc)
2737 return send_error(ctx, rc);
2739 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2740 client->inquire_status = INQUIRE_BUSY;
2741 return 0;
2744 static int lock_command(assuan_context_t ctx, gchar *line)
2746 gpg_error_t rc;
2747 struct client_s *client = assuan_get_pointer(ctx);
2749 rc = file_modified(client);
2751 if (rc)
2752 return send_error(ctx, rc);
2754 rc = lock_file_mutex(client);
2756 if (!rc)
2757 client->is_lock_cmd = TRUE;
2759 return send_error(ctx, rc);
2762 static int unlock_command(assuan_context_t ctx, gchar *line)
2764 struct client_s *client = assuan_get_pointer(ctx);
2765 gpg_error_t rc = file_modified(client);
2767 if (rc)
2768 return send_error(ctx, rc);
2770 unlock_file_mutex(client);
2771 return send_error(ctx, 0);
2774 static int getpid_command(assuan_context_t ctx, gchar *line)
2776 gpg_error_t rc;
2777 gchar buf[32];
2778 pid_t pid = getpid();
2780 print_fmt(buf, sizeof(buf), "%i", pid);
2781 pthread_testcancel();
2782 rc = assuan_send_data(ctx, buf, strlen(buf));
2783 pthread_testcancel();
2784 return send_error(ctx, rc);
2787 static int version_command(assuan_context_t ctx, gchar *line)
2789 gpg_error_t rc;
2790 gchar buf[32];
2792 print_fmt(buf, sizeof(buf), "%s", PACKAGE_VERSION);
2793 pthread_testcancel();
2794 rc = assuan_send_data(ctx, buf, strlen(buf));
2795 pthread_testcancel();
2796 return send_error(ctx, rc);
2799 static void bye_notify(assuan_context_t ctx)
2801 struct client_s *cl = assuan_get_pointer(ctx);
2802 #ifdef WITH_GNUTLS
2803 gint rc;
2805 if (!cl->thd->remote)
2806 return;
2808 do {
2809 rc = gnutls_bye(cl->thd->tls->ses, GNUTLS_SHUT_RDWR);
2810 } while (rc == GNUTLS_E_AGAIN);
2811 #endif
2813 /* This will let assuan_process_next() return. */
2814 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
2817 static void reset_notify(assuan_context_t ctx)
2819 struct client_s *cl = assuan_get_pointer(ctx);
2821 if (cl)
2822 cleanup_client(cl);
2825 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2827 gchar name[32] = {0}, value[256] = {0};
2829 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2830 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2832 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2833 struct client_s *cl = assuan_get_pointer(ctx);
2835 if (cl->thd->name)
2836 g_free(cl->thd->name);
2838 cl->thd->name = g_strdup(value);
2839 log_write("OPTION CLIENT %s", line);
2841 else
2842 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2844 return 0;
2847 static int option_handler(assuan_context_t ctx, const gchar *name,
2848 const gchar *value)
2850 struct client_s *client = assuan_get_pointer(ctx);
2852 if (!value || !*value)
2853 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2855 if (g_strcasecmp(name, (gchar *)"client") == 0)
2856 return parse_client_option(ctx, value);
2858 if (g_strcasecmp(name, (gchar *)"iterations") == 0) {
2859 long n;
2860 gchar *p = NULL;
2862 errno = 0;
2863 n = strtol(value, &p, 10);
2865 if (errno || (p && *p) || n < 0)
2866 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2868 g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", (guint)n);
2869 send_status_all(STATUS_CONFIG);
2871 #ifdef WITH_PINENTRY
2872 else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2873 g_free(client->pinentry->ttyname);
2874 client->pinentry->ttyname = g_strdup(value);
2876 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2877 g_free(client->pinentry->ttytype);
2878 client->pinentry->ttytype = g_strdup(value);
2880 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2881 g_free(client->pinentry->display);
2882 client->pinentry->display = g_strdup(value);
2884 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2885 g_free(client->pinentry->path);
2886 client->pinentry->path = g_strdup(value);
2888 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2889 g_free(client->pinentry->title);
2890 client->pinentry->title = g_strdup(value);
2892 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2893 g_free(client->pinentry->prompt);
2894 client->pinentry->prompt = g_strdup(value);
2896 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2897 g_free(client->pinentry->desc);
2898 client->pinentry->desc = g_strdup(value);
2901 * Look at client_thread() to see how this works.
2903 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2904 gchar *p = NULL;
2905 gint n = strtol(value, &p, 10);
2907 if (*p || n < 0)
2908 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2910 client->pinentry->timeout = n;
2912 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2913 gchar *p = NULL;
2914 gint n = strtol(value, &p, 10);
2916 if (*p || n < 0 || n > 1)
2917 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2919 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2921 #endif
2922 else
2923 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2925 log_write("OPTION %s=%s", name, value);
2926 return 0;
2929 gpg_error_t register_commands(assuan_context_t ctx)
2931 static struct {
2932 const gchar *name;
2933 gint (*handler)(assuan_context_t, gchar *line);
2934 } table[] = {
2935 { "OPEN", open_command },
2936 { "SAVE", save_command },
2937 { "LIST", list_command },
2938 { "REALPATH", realpath_command },
2939 { "STORE", store_command },
2940 { "DELETE", delete_command },
2941 { "GET", get_command },
2942 { "ATTR", attr_command },
2943 { "ISCACHED", iscached_command },
2944 { "CLEARCACHE", clearcache_command },
2945 { "CACHETIMEOUT", cachetimeout_command },
2946 { "GETCONFIG", getconfig_command },
2947 { "DUMP", dump_command },
2948 { "XPATH", xpath_command },
2949 { "IMPORT", import_command },
2950 { "LOCK", lock_command },
2951 { "UNLOCK", unlock_command },
2952 { "GETPID", getpid_command },
2953 { "VERSION", version_command },
2954 { "INPUT", NULL },
2955 { "OUTPUT", NULL },
2956 { NULL, NULL }
2958 gint i, rc;
2960 for (i=0; table[i].name; i++) {
2961 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2963 if (rc)
2964 return rc;
2967 rc = assuan_register_bye_notify(ctx, bye_notify);
2969 if (rc)
2970 return rc;
2972 rc = assuan_register_option_handler(ctx, option_handler);
2974 if (rc)
2975 return rc;
2977 rc = assuan_register_reset_notify(ctx, reset_notify);
2979 if (rc)
2980 return rc;
2982 return assuan_register_post_cmd_notify(ctx, command_finalize);
2985 gpg_error_t try_xml_decrypt(assuan_context_t ctx, guchar *key,
2986 struct client_crypto_s *crypto, gpointer *dst, gsize *dst_len)
2988 gsize insize, len;
2989 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2990 guint iter = 0, n_iter = 0, iter_progress = 0;
2991 gint zrc = 0;
2992 gulong outsize = 0;
2993 gpg_error_t rc;
2994 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->fh1) : sizeof(crypto->fh->fh2);
2995 glong fh_iter = crypto->fh->v1 ? crypto->fh->fh1.iter : crypto->fh->fh2.iter;
2997 lseek(crypto->fh->fd, fh_size, SEEK_SET);
2998 insize = crypto->fh->st.st_size - fh_size;
2999 crypto->iv = gcry_malloc(gcryblocksize);
3001 if (!crypto->iv) {
3002 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3003 return gpg_error_from_errno(ENOMEM);
3006 /* No encryption iterations. This is a plain (gzipped) file. */
3007 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0)) {
3009 * cache_file_count() needs both .used == TRUE and a valid key in
3010 * order for it to count as a used cache entry. Fixes CACHE status
3011 * messages.
3013 memset(key, '!', gcrykeysize);
3016 if (crypto->fh->v1)
3017 memcpy(crypto->iv, crypto->fh->fh1.iv, gcryblocksize);
3018 else
3019 memcpy(crypto->iv, crypto->fh->fh2.iv, gcryblocksize);
3021 crypto->inbuf = gcry_malloc(insize);
3023 if (!crypto->inbuf) {
3024 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3025 return gpg_error_from_errno(ENOMEM);
3028 crypto->insize = insize;
3029 len = read(crypto->fh->fd, crypto->inbuf, crypto->insize);
3031 if (len != crypto->insize)
3032 return GPG_ERR_INV_LENGTH;
3034 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0))
3035 goto decompress;
3037 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3038 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3039 return rc;
3042 if ((rc = gcry_cipher_setkey(crypto->gh, key, gcrykeysize))) {
3043 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3044 return rc;
3047 iter_progress = (guint)get_key_file_integer(client && client->filename ?
3048 client->filename : "global", "iteration_progress");
3050 if (iter_progress > 0 && fh_iter >= iter_progress) {
3051 rc = send_status(ctx, STATUS_DECRYPT, "%u %u", 0, fh_iter);
3053 if (rc)
3054 return rc;
3057 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3059 if (rc)
3060 return rc;
3062 crypto->tkey = gcry_malloc(gcrykeysize);
3064 if (!crypto->tkey) {
3065 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
3066 return gpg_error_from_errno(ENOMEM);
3069 memcpy(crypto->tkey, key, gcrykeysize);
3070 guchar *tkey = crypto->tkey;
3071 tkey[0] ^= 1;
3073 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
3074 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3075 return rc;
3078 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
3079 if (iter_progress > 0 && iter >= iter_progress) {
3080 if (!(iter % iter_progress)) {
3081 rc = send_status(ctx, STATUS_DECRYPT, "%u %u",
3082 ++n_iter * iter_progress, fh_iter);
3084 if (rc)
3085 return rc;
3089 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3090 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3091 return rc;
3094 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3096 if (rc) {
3097 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3098 return rc;
3101 iter++;
3104 if (iter_progress && fh_iter >= iter_progress) {
3105 rc = send_status(ctx, STATUS_DECRYPT, "%u %u", fh_iter, fh_iter);
3107 if (rc)
3108 return rc;
3111 decompress:
3112 if (do_decompress(ctx, crypto->inbuf, crypto->insize,
3113 (gpointer *)&crypto->outbuf, &outsize, &zrc) == FALSE) {
3114 if (zrc == Z_MEM_ERROR)
3115 return gpg_error_from_errno(ENOMEM);
3116 else
3117 return EPWMD_BADKEY; // Not a valid gzip header. Must be a bad key.
3120 if (g_strncasecmp(crypto->outbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
3121 gcry_free(crypto->outbuf);
3122 crypto->outbuf = NULL;
3123 return EPWMD_BADKEY;
3126 if (ctx) {
3127 client->xml = crypto->outbuf;
3128 client->len = outsize;
3129 crypto->outbuf = NULL;
3131 else if (dst) {
3132 *dst = crypto->outbuf;
3133 *dst_len = outsize;
3134 crypto->outbuf = NULL;
3137 /* The calling function should free the crypto struct. */
3138 return 0;
3142 * This is called after every Assuan command.
3144 void command_finalize(assuan_context_t ctx, gint rc)
3146 struct client_s *client = assuan_get_pointer(ctx);
3148 if (!client->is_lock_cmd)
3149 unlock_file_mutex(client);