Fixed status messages to use the new iteration type.
[pwmd.git] / src / commands.c
blobc64ac1b0fc71b2bf5951a0df12c2dcfdd4f736e5
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);
152 pthread_testcancel();
154 if (rc)
155 return rc;
157 sleep(1);
160 else {
161 MUTEX_LOCK(&m->mutex);
164 else {
165 MUTEX_LOCK_DEBUG;
168 client->has_lock = TRUE;
169 return 0;
172 void free_client(struct client_s *client)
174 if (client->doc)
175 xmlFreeDoc(client->doc);
177 if (client->xml)
178 gcry_free(client->xml);
180 if (client->filename)
181 g_free(client->filename);
183 if (client->crypto)
184 cleanup_crypto(&client->crypto);
186 if (client->xml_error)
187 xmlResetError(client->xml_error);
190 void cleanup_client(struct client_s *client)
192 assuan_context_t ctx = client->ctx;
193 struct client_thread_s *thd = client->thd;
194 gboolean has_lock = client->has_lock;
195 #ifdef WITH_PINENTRY
196 struct pinentry_s *pin = client->pinentry;
197 #endif
199 unlock_file_mutex(client);
200 CACHE_LOCK(client->ctx);
201 cache_decr_refcount(client->md5file);
204 * This may be a new file so don't use a cache slot. save_command() will
205 * set this to FALSE on success.
207 if (client->new == TRUE)
208 cache_clear(client->md5file, 1);
210 CACHE_UNLOCK;
211 free_client(client);
212 memset(client, 0, sizeof(struct client_s));
213 client->state = STATE_CONNECTED;
214 client->ctx = ctx;
215 client->thd = thd;
216 client->freed = TRUE;
217 #ifdef WITH_PINENTRY
218 client->pinentry = pin;
219 #endif
220 client->has_lock = has_lock;
223 static void gz_cleanup(void *arg)
225 struct gz_s **gz = (struct gz_s **)arg;
227 if (!gz)
228 return;
230 if (!(*gz)->done && (*gz)->out)
231 gcry_free((*gz)->out);
234 if ((*gz)->which == STATUS_COMPRESS) {
235 if ((*gz)->z.zalloc)
236 deflateEnd(&(*gz)->z);
238 else {
239 if ((*gz)->z.zalloc)
240 inflateEnd(&(*gz)->z);
243 g_free(*gz);
244 *gz = NULL;
247 gboolean do_decompress(assuan_context_t ctx, gpointer in, gulong insize,
248 gpointer *out, gulong *outsize, gint *rc)
250 struct gz_s *gz;
251 gz_header h;
252 gchar buf[17];
254 gz = g_malloc0(sizeof(struct gz_s));
256 if (!gz) {
257 *rc = gpg_error_from_errno(ENOMEM);
258 return FALSE;
261 gz->which = STATUS_DECOMPRESS;
262 pthread_cleanup_push(gz_cleanup, &gz);
263 gz->z.zalloc = z_alloc;
264 gz->z.zfree = z_free;
265 gz->z.next_in = in;
266 gz->z.avail_in = (uInt)insize;
267 gz->z.avail_out = zlib_bufsize;
268 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
270 if (!gz->out) {
271 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
272 *rc = Z_MEM_ERROR;
273 gz_cleanup(&gz);
274 return FALSE;
277 *rc = inflateInit2(&gz->z, 47);
279 if (*rc != Z_OK) {
280 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
281 gz_cleanup(&gz);
282 return FALSE;
285 memset(&h, 0, sizeof(gz_header));
286 h.comment = (guchar *)buf;
287 h.comm_max = sizeof(buf);
288 *rc = inflateGetHeader(&gz->z, &h);
290 if (*rc != Z_OK) {
291 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
292 gz_cleanup(&gz);
293 return FALSE;
296 *rc = inflate(&gz->z, Z_BLOCK);
298 if (*rc != Z_OK) {
299 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
300 gz_cleanup(&gz);
301 return FALSE;
304 if (h.comment)
305 insize = (gulong)strtol((gchar *)h.comment, NULL, 10);
307 do {
308 gpointer p;
310 *rc = inflate(&gz->z, Z_FINISH);
312 switch (*rc) {
313 case Z_OK:
314 break;
315 case Z_BUF_ERROR:
316 if (!gz->z.avail_out) {
317 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
319 if (!p) {
320 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
321 *rc = Z_MEM_ERROR;
322 goto fail;
325 gz->out = p;
326 gz->z.next_out = gz->out + gz->z.total_out;
327 gz->z.avail_out = zlib_bufsize;
328 pthread_cleanup_push(gz_cleanup, &gz);
329 pthread_testcancel();
330 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li",
331 gz->z.total_out, insize);
332 pthread_testcancel();
333 pthread_cleanup_pop(0);
335 if (*rc)
336 goto fail;
338 break;
339 case Z_STREAM_END:
340 break;
341 default:
342 goto fail;
343 break;
345 } while (*rc != Z_STREAM_END);
347 pthread_testcancel();
348 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li", gz->z.total_out,
349 insize);
350 pthread_testcancel();
352 if (*rc)
353 goto fail;
355 *out = gz->out;
356 *outsize = gz->z.total_out;
357 gz->done = TRUE;
358 gz_cleanup(&gz);
359 *rc = 0;
360 return TRUE;
362 fail:
363 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
364 pthread_cleanup_pop(1);
365 return FALSE;
368 static void read_file_header_handler(void *arg)
370 close((int)arg);
373 file_header_internal_t *read_file_header(const gchar *filename, gboolean v1,
374 gpg_error_t *rc)
376 gint fd;
377 gsize len;
378 file_header_internal_t *fh = g_malloc0(sizeof(file_header_internal_t));
379 gsize fh_size;
381 *rc = 0;
383 if (!fh) {
384 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
385 *rc = gpg_error_from_errno(ENOMEM);
386 return NULL;
389 fh_size = v1 ? sizeof(fh->fh1) : sizeof(fh->fh2);
391 if (lstat(filename, &fh->st) == -1) {
392 *rc = gpg_error_from_syserror();
393 g_free(fh);
394 return NULL;
397 if (!S_ISREG(fh->st.st_mode)) {
398 *rc = GPG_ERR_ENOANO;
399 g_free(fh);
400 return NULL;
403 fd = open(filename, O_RDONLY);
405 if (fd == -1) {
406 *rc = gpg_error_from_errno(errno);
407 g_free(fh);
408 return NULL;
411 if (v1)
412 len = read(fd, &fh->fh1, fh_size);
413 else
414 len = read(fd, &fh->fh2, fh_size);
416 if (len != fh_size) {
417 gint n = errno;
418 pthread_cleanup_push(read_file_header_handler, (void *)fd);
419 g_free(fh);
420 pthread_cleanup_pop(1);
421 *rc = gpg_error_from_errno(n);
422 return NULL;
425 fh->v1 = v1;
426 fh->fd = fd;
427 return fh;
430 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar *key,
431 gboolean cached)
433 struct client_s *client = assuan_get_pointer(ctx);
434 gpg_error_t rc;
435 gint timeout;
437 /* New file. */
438 if (!client->crypto->fh) {
439 if (key[0])
440 goto update_cache;
442 goto done;
445 rc = try_xml_decrypt(ctx, key, client->crypto, NULL, NULL);
447 if (rc) {
448 cleanup_client(client);
449 return send_error(ctx, rc);
452 update_cache:
453 CACHE_LOCK(client->ctx);
455 if (cached == FALSE) {
456 if (cache_update_key(client->md5file, key) == FALSE) {
457 cleanup_client(client);
458 CACHE_UNLOCK;
459 return send_syserror(ctx, ENOMEM);
462 timeout = get_key_file_integer(client->filename, "cache_timeout");
463 cache_reset_timeout(client->md5file, timeout);
465 else
466 cache_set_timeout(client->md5file, -2);
468 CACHE_UNLOCK;
470 done:
471 rc = parse_xml(ctx);
473 if (client->xml) {
474 gcry_free(client->xml);
475 client->xml = NULL;
478 if (!rc) {
479 if (client->new == FALSE)
480 send_status_all(STATUS_CACHE);
482 client->state = STATE_OPEN;
485 if (!rc && client->new == FALSE &&
486 client->crypto->fh->fh2.iter != (guint64)get_key_file_integer(client->filename, "iterations")) {
487 g_key_file_set_integer(keyfileh, client->filename, "iterations",
488 client->crypto->fh->fh2.iter);
489 send_status_all(STATUS_CONFIG);
492 if (!rc)
493 log_write("OPEN '%s'", client->filename);
495 cleanup_crypto(&client->crypto);
496 return send_error(ctx, rc);
499 #ifdef WITH_GNUTLS
500 static gboolean validate_access(struct client_s *cl, const gchar *filename)
502 gchar *access = get_key_file_string(filename, "tcp_access");
503 gchar **list, **p;
505 if (!access)
506 return TRUE;
508 list = g_strsplit(access, ",", -1);
509 g_free(access);
511 if (!list) {
512 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
513 return FALSE;
516 for (p = list; *p; p++) {
517 gboolean not = FALSE;
518 gchar *fp = *p;
520 if (*fp == '!') {
521 not = TRUE;
522 fp++;
524 if (!*fp)
525 break;
528 if (strcasecmp(cl->thd->tls->fp, fp) == 0) {
529 if (not == TRUE)
530 continue;
532 g_strfreev(list);
533 return TRUE;
537 /* Not allowed. */
538 g_strfreev(list);
539 return FALSE;
541 #endif
543 static void req_cleanup(void *arg)
545 if (!arg)
546 return;
548 g_strfreev((gchar **)arg);
551 static int open_command(assuan_context_t ctx, char *line)
553 gboolean cached = FALSE;
554 gpg_error_t rc;
555 struct client_s *client = assuan_get_pointer(ctx);
556 gchar **req;
557 gchar *filename = NULL;
559 if ((req = split_input_line(line, " ", 2)) != NULL)
560 filename = req[0];
562 pthread_cleanup_push(req_cleanup, req);
564 if (!filename || !*filename) {
565 g_strfreev(req);
566 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
569 if (valid_filename(filename) == FALSE) {
570 g_strfreev(req);
571 return send_error(ctx, EPWMD_INVALID_FILENAME);
574 if (client->state == STATE_OPEN)
575 cleanup_client(client);
577 #ifdef WITH_GNUTLS
578 if (client->thd->remote == TRUE) {
579 if (validate_access(client, filename) == FALSE) {
580 log_write(N_("client validation failed for file '%s'"), filename);
581 g_strfreev(req);
582 return send_error(ctx, EPWMD_FILE_ACCESS);
585 #endif
587 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
588 CACHE_LOCK(client->ctx);
590 if (cache_has_file(client->md5file) == FALSE) {
591 if (cache_add_file(client->md5file, NULL) == FALSE) {
592 g_strfreev(req);
593 CACHE_UNLOCK;
594 return send_syserror(ctx, ENOMEM);
598 cache_incr_refcount(client->md5file);
599 CACHE_UNLOCK;
600 rc = lock_file_mutex(client);
602 if (rc) {
603 g_strfreev(req);
604 return send_error(ctx, rc);
607 client->freed = FALSE;
608 client->crypto = init_client_crypto();
610 if (!client->crypto) {
611 g_strfreev(req);
612 cleanup_client(client);
613 return send_syserror(ctx, ENOMEM);
616 client->crypto->key = gcry_malloc(gcrykeysize);
618 if (!client->crypto->key) {
619 g_strfreev(req);
620 log_write("%s(%i): %s", __FUNCTION__, __LINE__,
621 gpg_error_from_errno(ENOMEM));
622 cleanup_client(client);
623 return send_syserror(ctx, ENOMEM);
626 memset(client->crypto->key, 0, gcrykeysize);
627 client->crypto->fh = read_file_header(filename, FALSE, &rc);
628 pthread_testcancel();
630 if (!client->crypto->fh) {
631 if (gpg_err_code_to_errno(rc) != ENOENT) {
632 log_write("%s: %s", filename, pwmd_strerror(rc));
633 g_strfreev(req);
634 cleanup_client(client);
635 return send_error(ctx, rc);
639 * New files don't need a key.
641 if ((client->xml = new_document()) == NULL) {
642 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
643 g_strfreev(req);
644 cleanup_client(client);
645 return send_syserror(ctx, ENOMEM);
648 client->len = xmlStrlen(client->xml);
649 client->new = TRUE;
650 client->filename = g_strdup(filename);
652 if (!client->filename) {
653 g_strfreev(req);
654 cleanup_client(client);
655 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
656 return send_syserror(ctx, ENOMEM);
659 if (req[1] && *req[1])
660 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
661 strlen(req[1]));
663 g_strfreev(req);
664 #ifdef WITH_PINENTRY
665 client->pinentry->filename = g_strdup(client->filename);
667 if (!client->pinentry->filename) {
668 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
669 cleanup_client(client);
670 return send_syserror(ctx, ENOMEM);
672 #endif
673 return open_command_finalize(ctx, client->crypto->key, cached);
675 else
676 client->mtime = client->crypto->fh->st.st_mtime;
678 client->filename = g_strdup(filename);
680 if (!client->filename) {
681 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
682 g_strfreev(req);
683 cleanup_client(client);
684 return send_syserror(ctx, ENOMEM);
687 #ifdef WITH_PINENTRY
688 client->pinentry->filename = g_strdup(client->filename);
690 if (!client->pinentry->filename) {
691 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
692 g_strfreev(req);
693 cleanup_client(client);
694 return send_syserror(ctx, ENOMEM);
696 #endif
698 if (client->crypto->fh->fh2.iter <= 0)
699 goto done;
701 #ifdef WITH_GNUTLS
702 if (client->thd->remote == FALSE ||
703 get_key_file_boolean(client->filename, "tcp_require_key") == FALSE)
705 #endif
706 CACHE_LOCK(client->ctx);
707 cached = cache_get_key(client->md5file, client->crypto->key);
708 CACHE_UNLOCK;
709 #ifdef WITH_GNUTLS
711 else
712 cached = FALSE;
713 #endif
715 if (cached == FALSE) {
716 gchar *tmp = get_key_file_string(filename, "key_file");
718 if (tmp) {
719 g_free(tmp);
720 cleanup_client(client);
721 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
725 * No key specified and no matching filename found in the cache. Use
726 * pinentry to retrieve the key. Cannot return assuan_process_done()
727 * here otherwise the command will be interrupted. The event loop in
728 * client_thread() will poll the file descriptor waiting for it to
729 * become ready to read a pinentry_key_s which will contain the
730 * entered key or an error code. It will then call
731 * open_command_finalize() to to finish the command.
733 if (!req[1] || !*req[1]) {
734 #ifdef WITH_PINENTRY
735 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
737 /* From set_pinentry_defaults(). */
738 if (client->pinentry->enable == FALSE ||
739 (client->pinentry->enable == -1 && b == FALSE)) {
740 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
741 goto done;
744 g_strfreev(req);
745 rc = lock_pin_mutex(client);
747 if (rc) {
748 unlock_pin_mutex(client->pinentry);
749 cleanup_client(client);
750 return send_error(ctx, rc);
753 client->pinentry->which = PINENTRY_OPEN;
754 rc = pinentry_fork(ctx);
756 if (rc) {
757 unlock_pin_mutex(client->pinentry);
758 cleanup_client(client);
759 return send_error(ctx, rc);
762 // Called from pinentry iterate.
763 client->pinentry->cb = open_command_finalize;
764 client->pinentry->status = PINENTRY_INIT;
765 return 0;
766 #else
767 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
768 goto done;
769 #endif
772 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
773 strlen(req[1]));
776 done:
777 if (1) {}
778 pthread_cleanup_pop(1);
779 return open_command_finalize(ctx, client->crypto->key, cached);
782 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
783 gulong size, gpointer *out, gulong *outsize, gint *rc)
785 struct gz_s *gz;
786 gz_header h;
787 gchar buf[17];
788 gint cmd = Z_NO_FLUSH;
790 gz = g_malloc0(sizeof(struct gz_s));
792 if (!gz) {
793 *rc = gpg_error_from_errno(ENOMEM);
794 return FALSE;
797 gz->which = STATUS_COMPRESS;
798 pthread_cleanup_push(gz_cleanup, &gz);
799 gz->z.zalloc = z_alloc;
800 gz->z.zfree = z_free;
801 gz->z.next_in = data;
802 gz->z.avail_in = size < zlib_bufsize ? (uInt)size : (uInt)zlib_bufsize;
803 gz->z.avail_out = (uInt)zlib_bufsize;
804 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
806 if (!gz->out) {
807 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
808 *rc = Z_MEM_ERROR;
809 gz_cleanup(&gz);
810 return FALSE;
813 *rc = deflateInit2(&gz->z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
815 if (*rc != Z_OK) {
816 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
817 gz_cleanup(&gz);
818 return FALSE;
821 /* Rather than store the size of the uncompressed data in the file header,
822 * store it in the comment field of the gzip header. Don't give anyone too
823 * much information. Not sure why really, but it seems the right way. :)
825 memset(&h, 0, sizeof(gz_header));
826 g_snprintf(buf, sizeof(buf), "%li", size);
827 h.comment = (guchar *)buf;
828 *rc = deflateSetHeader(&gz->z, &h);
830 if (*rc != Z_OK) {
831 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
832 gz_cleanup(&gz);
833 return FALSE;
836 do {
837 gpointer p;
839 *rc = deflate(&gz->z, cmd);
841 switch (*rc) {
842 case Z_OK:
843 break;
844 case Z_BUF_ERROR:
845 if (!gz->z.avail_out) {
846 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
848 if (!p) {
849 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
850 *rc = Z_MEM_ERROR;
851 goto fail;
854 gz->out = p;
855 gz->z.next_out = gz->out + gz->z.total_out;
856 gz->z.avail_out = zlib_bufsize;
859 if (!gz->z.avail_in && gz->z.total_in < size) {
860 if (gz->z.total_in + zlib_bufsize > size)
861 gz->z.avail_in = size - gz->z.total_in;
862 else
863 gz->z.avail_in = zlib_bufsize;
865 pthread_cleanup_push(gz_cleanup, &gz);
866 pthread_testcancel();
867 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li",
868 gz->z.total_in, size);
869 pthread_testcancel();
870 pthread_cleanup_pop(0);
872 if (*rc)
873 goto fail;
876 if (gz->z.total_in >= size)
877 cmd = Z_FINISH;
879 break;
880 case Z_STREAM_END:
881 break;
882 default:
883 goto fail;
885 } while (*rc != Z_STREAM_END);
887 pthread_testcancel();
888 *rc = send_status(ctx, STATUS_COMPRESS, "%li %li", gz->z.total_in, size);
889 pthread_testcancel();
891 if (*rc)
892 goto fail;
894 *out = gz->out;
895 *outsize = gz->z.total_out;
896 *rc = 0;
897 gz->done = TRUE;
898 gz_cleanup(&gz);
899 return TRUE;
901 fail:
902 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
903 pthread_cleanup_pop(1);
904 return FALSE;
907 #define CRYPTO_BLOCKSIZE (gcryblocksize * 1024)
909 static gpg_error_t iterate_crypto_once(struct client_s *client,
910 struct client_crypto_s *crypto, status_msg_t which)
912 gpg_error_t rc = 0;
913 gsize len = CRYPTO_BLOCKSIZE;
914 gpointer p = gcry_malloc(len);
915 gsize total = 0;
916 gpointer inbuf;
918 if (!p)
919 return gpg_err_code_from_errno(ENOMEM);
921 pthread_cleanup_push(gcry_free, p);
923 if (crypto->insize < CRYPTO_BLOCKSIZE)
924 len = crypto->insize;
926 for (;;) {
927 inbuf = crypto->inbuf + total;
928 guchar *tmp;
930 if (len + total > crypto->insize)
931 len = gcryblocksize;
933 if (which == STATUS_ENCRYPT)
934 rc = gcry_cipher_encrypt(crypto->gh, p, len, inbuf, len);
935 else
936 rc = gcry_cipher_decrypt(crypto->gh, p, len, inbuf, len);
938 if (rc)
939 goto done;
941 tmp = crypto->inbuf+total;
942 memmove(tmp, p, len);
943 total += len;
945 if (total >= crypto->insize)
946 break;
948 pthread_testcancel();
951 done:
952 if (1) {}
953 pthread_cleanup_pop(1);
954 return rc;
957 /* The crypto struct must be setup for iterations and .key. */
958 gpg_error_t do_xml_encrypt(struct client_s *client,
959 struct client_crypto_s *crypto, const gchar *filename)
961 gsize len = crypto->insize;
962 gpointer inbuf;
963 gchar *p;
964 gpg_error_t rc;
965 guint64 iter_progress = 0, n_iter = 0, xiter = 0;
966 gchar tmp[FILENAME_MAX];
967 struct stat st;
968 mode_t mode = 0;
970 if (!crypto->fh->fh2.iter) {
972 * cache_file_count() needs both .used == TRUE and a valid key in
973 * order for it to count as a used cache entry. Fixes CACHE status
974 * messages.
976 memset(crypto->key, '!', gcrykeysize);
977 goto write_file;
981 * Resize the existing xml buffer to the block size required by gcrypt
982 * rather than duplicating it and wasting memory.
984 if (crypto->insize / gcryblocksize) {
985 len = (crypto->insize / gcryblocksize) * gcryblocksize;
987 if (crypto->insize % gcryblocksize)
988 len += gcryblocksize;
991 inbuf = gcry_realloc(crypto->inbuf, len);
993 if (!inbuf) {
994 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
995 return gpg_error_from_errno(ENOMEM);
998 crypto->inbuf = inbuf;
999 crypto->insize = len;
1000 gcry_create_nonce(crypto->fh->fh2.iv, sizeof(crypto->fh->fh2.iv));
1001 crypto->tkey = gcry_malloc(gcrykeysize);
1003 if (!crypto->tkey) {
1004 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1005 return gpg_error_from_errno(ENOMEM);
1008 memcpy(crypto->tkey, crypto->key, gcrykeysize);
1009 guchar *tkey = crypto->tkey;
1010 tkey[0] ^= 1;
1012 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
1013 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1014 return rc;
1017 iter_progress = (guint64)get_key_file_integer(
1018 client ? client->filename : "global", "iteration_progress");
1020 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
1021 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1022 "0 %llu", crypto->fh->fh2.iter);
1023 pthread_testcancel();
1025 if (rc)
1026 return rc;
1029 while (xiter < crypto->fh->fh2.iter-1) {
1030 if (iter_progress > 0 && xiter >= iter_progress) {
1031 if (!(xiter % iter_progress)) {
1032 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1033 "%llu %llu", ++n_iter * iter_progress,
1034 crypto->fh->fh2.iter);
1035 pthread_testcancel();
1037 if (rc)
1038 return rc;
1042 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
1043 sizeof(crypto->fh->fh2.iv)))) {
1044 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1045 return rc;
1048 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1050 if (rc) {
1051 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1052 return rc;
1055 xiter++;
1058 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->fh2.iv,
1059 sizeof(crypto->fh->fh2.iv)))) {
1060 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1061 return rc;
1064 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, gcrykeysize))) {
1065 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1066 return rc;
1069 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1071 if (rc)
1072 return rc;
1074 if (iter_progress && crypto->fh->fh2.iter >= iter_progress) {
1075 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1076 "%llu %llu", crypto->fh->fh2.iter, crypto->fh->fh2.iter);
1077 pthread_testcancel();
1079 if (rc)
1080 return rc;
1083 write_file:
1084 if (filename) {
1085 if (!client && !strcmp(filename, "-")) {
1086 crypto->fh->fd = STDOUT_FILENO;
1087 goto do_write_file;
1090 if (lstat(filename, &st) == 0) {
1091 pthread_testcancel();
1092 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
1095 * FIXME What if the file has an ACL?
1097 if (!(mode & S_IWUSR))
1098 return gpg_error_from_errno(EACCES);
1100 else {
1101 pthread_testcancel();
1102 if (errno != ENOENT)
1103 return gpg_error_from_errno(errno);
1106 g_snprintf(tmp, sizeof(tmp), "%s.XXXXXX", filename);
1107 crypto->fh->fd = mkstemp(tmp);
1109 if (crypto->fh->fd == -1) {
1110 rc = errno;
1111 p = strrchr(tmp, '/');
1112 p++;
1113 log_write("%s: %s", p, strerror(rc));
1114 return gpg_error_from_errno(rc);
1117 else
1119 * xml_import() or convert_file() from command line.
1121 crypto->fh->fd = STDOUT_FILENO;
1123 do_write_file:
1124 crypto->fh->fh2.version = VERSION_HEX;
1125 len = write(crypto->fh->fd, &crypto->fh->fh2, sizeof(crypto->fh->fh2));
1126 pthread_testcancel();
1128 if (len != sizeof(crypto->fh->fh2)) {
1129 len = errno;
1131 if (filename && strcmp(filename, "-"))
1132 unlink(tmp);
1134 return gpg_error_from_errno(len);
1137 len = write(crypto->fh->fd, crypto->inbuf, crypto->insize);
1138 pthread_testcancel();
1140 if (len != crypto->insize) {
1141 pthread_testcancel();
1142 len = errno;
1144 if (filename && strcmp(filename, "-")) {
1145 unlink(tmp);
1146 pthread_testcancel();
1149 return gpg_error_from_errno(len);
1152 if (fsync(crypto->fh->fd) == -1) {
1153 pthread_testcancel();
1154 len = errno;
1156 if (filename && strcmp(filename, "-"))
1157 unlink(tmp);
1159 return gpg_error_from_errno(len);
1162 if (filename && strcmp(filename, "-")) {
1163 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
1164 gchar tmp2[FILENAME_MAX];
1166 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
1168 if (rename(filename, tmp2) == -1) {
1169 pthread_testcancel();
1170 unlink(tmp);
1171 len = errno;
1172 return gpg_error_from_errno(len);
1176 if (rename(tmp, filename) == -1) {
1177 pthread_testcancel();
1178 len = errno;
1179 unlink(tmp);
1180 return gpg_error_from_errno(len);
1183 if (mode) {
1184 chmod(filename, mode);
1185 pthread_testcancel();
1189 if (client && lstat(filename, &st) == 0)
1190 client->mtime = st.st_mtime;
1192 pthread_testcancel();
1193 return 0;
1196 static gpg_error_t save_command_finalize(assuan_context_t ctx, guchar *key,
1197 gboolean cached)
1199 struct client_s *client = assuan_get_pointer(ctx);
1200 gpointer xmlbuf;
1201 gulong len, outsize = 0;
1202 guint iter;
1203 gint timeout;
1204 gpointer outbuf;
1205 gint zrc;
1206 gpg_error_t rc;
1208 if (client->crypto->key && client->crypto->key != key)
1209 gcry_free(client->crypto->key);
1211 client->crypto->key = key;
1212 xmlDocDumpFormatMemory(client->doc, (xmlChar **)&xmlbuf, (gint *)&len, 0);
1213 iter = (guint)get_key_file_integer(client->filename, "compression_level");
1215 if (iter < 0)
1216 iter = 0;
1218 if (do_compress(ctx, (gint)iter, xmlbuf, len, &outbuf, &outsize, &zrc)
1219 == FALSE) {
1220 xmlFree(xmlbuf);
1221 cleanup_crypto(&client->crypto);
1223 if (zrc == Z_MEM_ERROR)
1224 return send_syserror(ctx, ENOMEM);
1225 else
1226 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1228 else {
1229 gcry_free(xmlbuf);
1230 xmlbuf = outbuf;
1231 len = outsize;
1234 client->crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1236 if (!client->crypto->fh) {
1237 cleanup_crypto(&client->crypto);
1238 gcry_free(outbuf);
1239 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1240 return send_syserror(ctx, ENOMEM);
1243 iter = get_key_file_integer(client->filename, "iterations");
1244 client->crypto->fh->fh2.iter = iter < 0 ? 0 : iter;
1245 client->crypto->inbuf = xmlbuf;
1246 client->crypto->insize = len;
1247 rc = do_xml_encrypt(client, client->crypto, client->filename);
1249 if (rc) {
1250 cleanup_crypto(&client->crypto);
1251 return send_error(ctx, rc);
1254 timeout = get_key_file_integer(client->filename, "cache_timeout");
1255 CACHE_LOCK(client->ctx);
1257 if (cached) {
1258 cache_reset_timeout(client->md5file, timeout);
1259 CACHE_UNLOCK;
1261 if (client->new == TRUE)
1262 send_status_all(STATUS_CACHE);
1264 client->new = FALSE;
1265 cleanup_crypto(&client->crypto);
1266 return send_error(ctx, 0);
1269 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1270 CACHE_UNLOCK;
1271 cleanup_crypto(&client->crypto);
1272 return send_syserror(ctx, ENOMEM);
1275 client->new = FALSE;
1276 cache_reset_timeout(client->md5file, timeout);
1277 CACHE_UNLOCK;
1278 send_status_all(STATUS_CACHE);
1279 cleanup_crypto(&client->crypto);
1280 return send_error(ctx, 0);
1283 static int save_command(assuan_context_t ctx, char *line)
1285 gboolean cached = FALSE;
1286 struct stat st;
1287 struct client_s *client = assuan_get_pointer(ctx);
1288 gpg_error_t rc;
1290 rc = lock_file_mutex(client);
1292 if (rc)
1293 return send_error(ctx, rc);
1295 rc = file_modified(client);
1297 if (rc)
1298 return send_error(ctx, rc);
1300 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1301 return send_syserror(ctx, errno);
1303 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1304 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1305 return send_error(ctx, GPG_ERR_ENOANO);
1308 CACHE_LOCK(ctx);
1309 cached = cache_iscached(client->md5file);
1310 CACHE_UNLOCK;
1313 * If a cache entry doesn't exist for this file and the file has a
1314 * "key_file" or "key" parameter, then it's an error. The reason is that
1315 * cache expiration would be useless.
1317 if (cached == FALSE) {
1318 gchar *tmp = get_key_file_string(client->filename, "key_file");
1320 if (tmp) {
1321 g_free(tmp);
1322 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1326 cached = FALSE;
1327 client->crypto = init_client_crypto();
1329 if (!client->crypto) {
1330 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1331 return send_syserror(ctx, ENOMEM);
1334 client->crypto->key = gcry_malloc(gcrykeysize);
1336 if (!client->crypto->key) {
1337 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1338 cleanup_crypto(&client->crypto);
1339 return send_syserror(ctx, ENOMEM);
1342 memset(client->crypto->key, '!', gcrykeysize);
1344 if (get_key_file_integer(client->filename, "iterations") <= 0)
1345 goto done;
1347 if (!line || !*line) {
1348 client->crypto->tkey = gcry_malloc(gcrykeysize);
1350 if (!client->crypto->tkey) {
1351 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1352 cleanup_crypto(&client->crypto);
1353 return send_syserror(ctx, ENOMEM);
1356 memset(client->crypto->tkey, '!', gcrykeysize);
1357 CACHE_LOCK(ctx);
1359 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1360 memcmp(client->crypto->key, client->crypto->tkey,
1361 gcrykeysize) == 0) {
1362 CACHE_UNLOCK;
1364 #ifdef WITH_PINENTRY
1365 if (client->pinentry->enable == FALSE ||
1366 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1367 /* Empty keys are allowed. */
1368 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1369 goto done;
1372 lock_pin_mutex(client);
1373 client->pinentry->which = PINENTRY_SAVE;
1374 rc = pinentry_fork(ctx);
1376 if (rc) {
1377 unlock_pin_mutex(client->pinentry);
1378 return send_error(ctx, rc);
1381 client->pinentry->cb = save_command_finalize;
1382 client->pinentry->status = PINENTRY_INIT;
1383 return 0;
1384 #else
1385 /* Empty keys are allowed. */
1386 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1387 goto done;
1388 #endif
1390 else {
1391 CACHE_UNLOCK;
1392 cached = TRUE;
1395 else {
1396 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, line,
1397 strlen(line));
1398 memset(line, 0, strlen(line));
1401 done:
1402 return save_command_finalize(ctx, client->crypto->key, cached);
1405 static int delete_command(assuan_context_t ctx, char *line)
1407 struct client_s *client = assuan_get_pointer(ctx);
1408 gchar **req;
1409 gpg_error_t rc;
1410 xmlNodePtr n;
1412 rc = file_modified(client);
1414 if (rc)
1415 return send_error(ctx, rc);
1417 if (strchr(line, '\t'))
1418 req = split_input_line(line, "\t", -1);
1419 else
1420 req = split_input_line(line, " ", -1);
1422 if (!req || !*req)
1423 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1425 n = find_account(client->doc, &req, &rc, NULL, 0);
1427 if (!n) {
1428 g_strfreev(req);
1429 return send_error(ctx, rc);
1433 * No sub-node defined. Remove the entire node (account).
1435 if (!req[1]) {
1436 if (n) {
1437 xmlUnlinkNode(n);
1438 xmlFreeNode(n);
1441 g_strfreev(req);
1442 return send_error(ctx, 0);
1445 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1446 g_strfreev(req);
1448 if (!n)
1449 return send_error(ctx, rc);
1451 if (n) {
1452 xmlUnlinkNode(n);
1453 xmlFreeNode(n);
1456 return send_error(ctx, 0);
1460 * Don't return with assuan_process_done() here. This has been called from
1461 * assuan_process_next() and the command should be finished in
1462 * client_thread().
1464 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1465 gsize len)
1467 assuan_context_t ctx = data;
1468 struct client_s *client = assuan_get_pointer(ctx);
1469 gchar **req;
1470 xmlNodePtr n;
1471 gpg_error_t rc = file_modified(client);
1473 if (assuan_rc || rc) {
1474 if (line)
1475 xfree(line);
1476 return assuan_rc ? assuan_rc : rc;
1479 req = split_input_line((gchar *)line, "\t", 0);
1480 xfree(line);
1482 if (!req || !*req)
1483 return EPWMD_COMMAND_SYNTAX;
1485 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1486 g_strfreev(req);
1487 return EPWMD_INVALID_ELEMENT;
1490 if (valid_element_path(req+1, TRUE) == FALSE) {
1491 g_strfreev(req);
1492 return EPWMD_INVALID_ELEMENT;
1495 again:
1496 n = find_account(client->doc, &req, &rc, NULL, 0);
1498 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1499 rc = new_account(client->doc, *req);
1501 if (rc) {
1502 g_strfreev(req);
1503 return rc;
1506 goto again;
1509 if (!n) {
1510 g_strfreev(req);
1511 return rc;
1514 if (req[1]) {
1515 if (!n->children)
1516 create_elements_cb(n, req+1, &rc, NULL);
1517 else
1518 find_elements(client->doc, n->children, req+1, &rc,
1519 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1522 g_strfreev(req);
1523 client->inquire_status = INQUIRE_DONE;
1524 return rc;
1527 static int store_command(assuan_context_t ctx, char *line)
1529 struct client_s *client = assuan_get_pointer(ctx);
1530 gpg_error_t rc = file_modified(client);
1532 if (rc)
1533 return send_error(ctx, rc);
1535 pthread_testcancel();
1536 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1537 pthread_testcancel();
1539 if (rc)
1540 return send_error(ctx, rc);
1542 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1543 client->inquire_status = INQUIRE_BUSY;
1544 return 0;
1547 static int get_command(assuan_context_t ctx, char *line)
1549 struct client_s *client = assuan_get_pointer(ctx);
1550 gchar **req;
1551 gpg_error_t rc;
1552 xmlNodePtr n;
1554 rc = file_modified(client);
1556 if (rc)
1557 return send_error(ctx, rc);
1559 req = split_input_line(line, "\t", -1);
1561 if (!req || !*req) {
1562 g_strfreev(req);
1563 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1566 n = find_account(client->doc, &req, &rc, NULL, 0);
1568 if (!n) {
1569 g_strfreev(req);
1570 return send_error(ctx, rc);
1573 if (req[1])
1574 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1576 g_strfreev(req);
1578 if (rc)
1579 return send_error(ctx, rc);
1581 if (!n || !n->children)
1582 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1584 n = find_text_node(n->children);
1586 if (!n || !n->content || !*n->content)
1587 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1589 pthread_testcancel();
1590 rc = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1591 pthread_testcancel();
1592 return send_error(ctx, rc);
1595 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1596 gpg_error_t *rc, gchar **req_orig, void *data)
1598 gchar *path = *(gchar **)data;
1599 gchar *tmp = NULL, *result;
1601 if (path) {
1602 g_free(path);
1603 *(gchar **)data = NULL;
1606 path = g_strjoinv("\t", target);
1608 if (!path) {
1609 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1610 *rc = gpg_error_from_errno(ENOMEM);
1611 return NULL;
1614 if (req_orig) {
1615 tmp = g_strjoinv("\t", req_orig);
1617 if (!tmp) {
1618 g_free(path);
1619 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1620 *rc = gpg_error_from_errno(ENOMEM);
1621 return NULL;
1625 if (tmp && *tmp)
1626 result = g_strdup_printf("%s\t%s", path, tmp);
1627 else
1628 result = g_strdup(path);
1630 if (!result) {
1631 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1632 *rc = gpg_error_from_errno(ENOMEM);
1633 g_free(path);
1634 g_free(tmp);
1635 return NULL;
1638 g_free(path);
1639 g_free(tmp);
1640 *(gchar **)data = result;
1641 return node;
1644 static void list_command_cleanup1(void *arg);
1645 static int realpath_command(assuan_context_t ctx, char *line)
1647 gpg_error_t rc;
1648 struct client_s *client = assuan_get_pointer(ctx);
1649 gchar **req;
1650 gchar *t;
1651 gint i;
1652 xmlNodePtr n;
1653 GString *string;
1654 gchar *rp = NULL;
1656 rc = file_modified(client);
1658 if (rc)
1659 return send_error(ctx, rc);
1661 if (strchr(line, '\t') != NULL) {
1662 if ((req = split_input_line(line, "\t", 0)) == NULL)
1663 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1665 else {
1666 if ((req = split_input_line(line, " ", 0)) == NULL)
1667 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1670 n = find_account(client->doc, &req, &rc, NULL, 0);
1672 if (!n) {
1673 g_strfreev(req);
1674 return send_error(ctx, rc);
1677 rp = g_strjoinv("\t", req);
1679 if (!rp) {
1680 g_strfreev(req);
1681 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1682 return send_syserror(ctx, ENOMEM);
1685 if (req[1]) {
1686 n = find_elements(client->doc, n->children, req+1, &rc,
1687 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1689 if (!n) {
1690 g_free(rp);
1691 g_strfreev(req);
1692 return send_error(ctx, rc);
1696 string = g_string_new(rp);
1697 g_free(rp);
1698 g_strfreev(req);
1700 if (!string) {
1701 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1702 return send_syserror(ctx, ENOMEM);
1705 again:
1706 for (i = 0, t = string->str + i; *t; t++, i++) {
1707 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1708 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1709 goto again;
1713 pthread_cleanup_push(list_command_cleanup1, string);
1714 pthread_testcancel();
1715 rc = assuan_send_data(ctx, string->str, string->len);
1716 pthread_testcancel();
1717 pthread_cleanup_pop(1);
1718 return send_error(ctx, rc);
1721 static void list_command_cleanup1(void *arg)
1723 g_string_free((GString *)arg, TRUE);
1726 static void list_command_cleanup2(void *arg)
1728 struct element_list_s *elements = arg;
1730 if (elements) {
1731 gint total = g_slist_length(elements->list);
1732 gint i;
1734 for (i = 0; i < total; i++) {
1735 gchar *tmp = g_slist_nth_data(elements->list, i);
1736 g_free(tmp);
1739 g_slist_free(elements->list);
1741 if (elements->prefix)
1742 g_free(elements->prefix);
1744 g_free(elements);
1748 static int list_command(assuan_context_t ctx, char *line)
1750 struct client_s *client = assuan_get_pointer(ctx);
1751 gpg_error_t rc;
1752 struct element_list_s *elements = NULL;
1753 gchar *tmp;
1755 if (disable_list_and_dump == TRUE)
1756 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1758 rc = file_modified(client);
1760 if (rc)
1761 return send_error(ctx, rc);
1763 if (!*line) {
1764 GString *str;
1766 rc = list_accounts(client->doc, &str);
1767 pthread_cleanup_push(list_command_cleanup1, str);
1769 if (rc)
1770 return send_error(ctx, rc);
1772 pthread_testcancel();
1773 rc = assuan_send_data(ctx, str->str, str->len);
1774 pthread_testcancel();
1775 pthread_cleanup_pop(1);
1776 return send_error(ctx, rc);
1779 elements = g_malloc0(sizeof(struct element_list_s));
1781 if (!elements) {
1782 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1783 rc = gpg_err_code_from_errno(ENOMEM);
1784 goto fail;
1787 pthread_cleanup_push(list_command_cleanup2, elements);
1788 pthread_testcancel();
1789 rc = create_path_list(client->doc, elements, line);
1790 pthread_testcancel();
1792 if (rc)
1793 goto fail;
1795 if (elements) {
1796 gint total = g_slist_length(elements->list);
1797 gint i;
1798 GString *str;
1800 if (!total) {
1801 rc = EPWMD_EMPTY_ELEMENT;
1802 goto fail;
1805 str = g_string_new(NULL);
1807 if (!str) {
1808 rc = gpg_err_code_from_errno(ENOMEM);
1809 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1810 goto fail;
1813 pthread_cleanup_push(list_command_cleanup1, str);
1815 for (i = 0; i < total; i++) {
1816 tmp = g_slist_nth_data(elements->list, i);
1817 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1820 pthread_testcancel();
1821 rc = assuan_send_data(ctx, str->str, str->len);
1822 pthread_testcancel();
1823 pthread_cleanup_pop(1);
1825 else
1826 rc = EPWMD_EMPTY_ELEMENT;
1828 fail:
1829 if (1) {}
1830 pthread_cleanup_pop(1);
1831 return send_error(ctx, rc);
1834 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1835 const gchar *value)
1837 xmlAttrPtr a;
1839 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1840 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1842 if (!a)
1843 return EPWMD_LIBXML_ERROR;
1845 else
1846 xmlNodeSetContent(a->children, (xmlChar *)value);
1848 return 0;
1852 * req[0] - element path
1854 static int attribute_list(assuan_context_t ctx, gchar **req)
1856 struct client_s *client = assuan_get_pointer(ctx);
1857 gchar **attrlist = NULL;
1858 gint i = 0;
1859 gchar **path = NULL;
1860 xmlAttrPtr a;
1861 xmlNodePtr n, an;
1862 gchar *line;
1863 gpg_error_t rc;
1865 if (!req || !req[0])
1866 return EPWMD_COMMAND_SYNTAX;
1868 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1870 * The first argument may be only an account.
1872 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1873 return EPWMD_COMMAND_SYNTAX;
1876 n = find_account(client->doc, &path, &rc, NULL, 0);
1878 if (!n) {
1879 g_strfreev(path);
1880 return rc;
1883 if (path[1]) {
1884 n = find_elements(client->doc, n->children, path+1, &rc,
1885 NULL, NULL, NULL, FALSE, 0, NULL);
1887 if (!n) {
1888 g_strfreev(path);
1889 return rc;
1893 g_strfreev(path);
1895 for (a = n->properties; a; a = a->next) {
1896 gchar **pa;
1898 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1899 if (attrlist)
1900 g_strfreev(attrlist);
1902 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1903 return gpg_error_from_errno(ENOMEM);
1906 attrlist = pa;
1907 an = a->children;
1908 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
1910 if (!attrlist[i]) {
1911 g_strfreev(attrlist);
1912 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1913 return gpg_error_from_errno(ENOMEM);
1916 attrlist[++i] = NULL;
1919 if (!attrlist)
1920 return EPWMD_EMPTY_ELEMENT;
1922 line = g_strjoinv("\n", attrlist);
1924 if (!line) {
1925 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1926 g_strfreev(attrlist);
1927 return gpg_error_from_errno(ENOMEM);
1930 pthread_cleanup_push(g_free, line);
1931 pthread_cleanup_push(req_cleanup, attrlist);
1932 pthread_testcancel();
1933 rc = assuan_send_data(ctx, line, strlen(line));
1934 pthread_testcancel();
1935 pthread_cleanup_pop(1);
1936 pthread_cleanup_pop(1);
1937 return rc;
1941 * req[0] - attribute
1942 * req[1] - element path
1944 static int attribute_delete(struct client_s *client, gchar **req)
1946 xmlAttrPtr a;
1947 xmlNodePtr n;
1948 gchar **path = NULL;
1949 gpg_error_t rc;
1951 if (!req || !req[0] || !req[1])
1952 return EPWMD_COMMAND_SYNTAX;
1954 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1956 * The first argument may be only an account.
1958 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1959 return EPWMD_COMMAND_SYNTAX;
1963 * Don't remove the "name" attribute for the account element. To remove an
1964 * account use DELETE <account>.
1966 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1967 rc = EPWMD_ATTR_SYNTAX;
1968 goto fail;
1971 n = find_account(client->doc, &path, &rc, NULL, 0);
1973 if (!n)
1974 goto fail;
1976 if (path[1]) {
1977 n = find_elements(client->doc, n->children, path+1, &rc,
1978 NULL, NULL, NULL, FALSE, 0, NULL);
1980 if (!n)
1981 goto fail;
1984 g_strfreev(path);
1986 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1987 return EPWMD_ATTR_NOT_FOUND;
1989 if (xmlRemoveProp(a) == -1)
1990 return EPWMD_LIBXML_ERROR;
1992 return 0;
1994 fail:
1995 g_strfreev(path);
1996 return rc;
1999 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
2000 gpg_error_t *rc)
2002 gchar **src = *path;
2003 gchar **src_orig = g_strdupv(src);
2004 xmlNodePtr n = NULL;
2006 *rc = 0;
2008 if (!src_orig) {
2009 *rc = gpg_error_from_errno(ENOMEM);
2010 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2011 goto fail;
2014 again:
2015 n = find_account(client->doc, &src, rc, NULL, 0);
2017 if (!n) {
2018 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
2019 *rc = new_account(client->doc, src[0]);
2021 if (*rc)
2022 goto fail;
2024 goto again;
2026 else
2027 goto fail;
2030 if (src[1]) {
2031 if (!n->children)
2032 n = create_target_elements_cb(n, src+1, rc, NULL);
2033 else
2034 n = find_elements(client->doc, n->children, src+1, rc,
2035 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
2037 if (!n)
2038 goto fail;
2041 * Reset the position of the element tree now that the elements
2042 * have been created.
2044 g_strfreev(src);
2045 src = src_orig;
2046 src_orig = NULL;
2047 n = find_account(client->doc, &src, rc, NULL, 0);
2049 if (!n)
2050 goto fail;
2052 n = find_elements(client->doc, n->children, src+1, rc,
2053 NULL, NULL, NULL, FALSE, 0, NULL);
2055 if (!n)
2056 goto fail;
2059 fail:
2060 if (src_orig)
2061 g_strfreev(src_orig);
2063 *path = src;
2064 return n;
2068 * Creates a "target" attribute. When other commands encounter an element with
2069 * this attribute, the element path is modified to the target value. If the
2070 * source element path doesn't exist when using 'ATTR SET target', it is
2071 * created, but the destination element path must exist.
2073 * req[0] - source element path
2074 * req[1] - destination element path
2076 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2078 gchar **src, **dst, *line = NULL;
2079 gpg_error_t rc;
2080 xmlNodePtr n;
2082 if (!req || !req[0] || !req[1])
2083 return EPWMD_COMMAND_SYNTAX;
2085 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2087 * The first argument may be only an account.
2089 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2090 return EPWMD_COMMAND_SYNTAX;
2093 if (valid_element_path(src, FALSE) == FALSE) {
2094 g_strfreev(src);
2095 return EPWMD_INVALID_ELEMENT;
2098 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2100 * The first argument may be only an account.
2102 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2103 rc = EPWMD_COMMAND_SYNTAX;
2104 goto fail;
2108 n = find_account(client->doc, &dst, &rc, NULL, 0);
2111 * Make sure the destination element path exists.
2113 if (!n)
2114 goto fail;
2116 if (dst[1]) {
2117 n = find_elements(client->doc, n->children, dst+1, &rc,
2118 NULL, NULL, NULL, FALSE, 0, NULL);
2120 if (!n)
2121 goto fail;
2124 n = create_element_path(client, &src, &rc);
2126 if (rc)
2127 goto fail;
2129 line = g_strjoinv("\t", dst);
2131 if (!line) {
2132 rc = gpg_error_from_errno(ENOMEM);
2133 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2134 goto fail;
2137 rc = add_attribute(n, "target", line);
2139 fail:
2140 g_free(line);
2141 g_strfreev(src);
2142 g_strfreev(dst);
2143 return rc;
2147 * req[0] - account name
2148 * req[1] - new name
2150 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2152 gpg_error_t rc;
2153 gchar **tmp;
2154 xmlNodePtr n;
2156 tmp = g_strdupv(req);
2158 if (!tmp) {
2159 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2160 return gpg_error_from_errno(ENOMEM);
2163 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2164 g_strfreev(tmp);
2166 if (!n)
2167 return rc;
2169 if (g_utf8_collate(req[0], req[1]) == 0)
2170 return 0;
2173 * Will not overwrite an existing account.
2175 tmp = g_strdupv(req+1);
2177 if (!tmp) {
2178 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2179 return gpg_error_from_errno(ENOMEM);
2182 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2183 g_strfreev(tmp);
2185 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
2186 return rc;
2188 if (n)
2189 return EPWMD_ACCOUNT_EXISTS;
2192 * Whitespace not allowed in account names.
2194 if (contains_whitespace(req[1]) == TRUE)
2195 return EPWMD_ATTR_SYNTAX;
2197 tmp = g_strdupv(req);
2199 if (!tmp) {
2200 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2201 return gpg_error_from_errno(ENOMEM);
2204 n = find_account(client->doc, &tmp, &rc, NULL, 0);
2205 g_strfreev(tmp);
2207 if (!n)
2208 return EPWMD_ELEMENT_NOT_FOUND;
2210 return add_attribute(n, "name", req[1]);
2214 * req[0] - attribute
2215 * req[1] - element path
2217 static int attribute_get(assuan_context_t ctx, gchar **req)
2219 struct client_s *client = assuan_get_pointer(ctx);
2220 xmlNodePtr n;
2221 xmlChar *a;
2222 gchar **path= NULL;
2223 gpg_error_t rc;
2225 if (!req || !req[0] || !req[1])
2226 return EPWMD_COMMAND_SYNTAX;
2228 if (strchr(req[1], '\t')) {
2229 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2230 return EPWMD_COMMAND_SYNTAX;
2232 else {
2233 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2234 return EPWMD_COMMAND_SYNTAX;
2237 n = find_account(client->doc, &path, &rc, NULL, 0);
2239 if (!n)
2240 goto fail;
2242 if (path[1]) {
2243 n = find_elements(client->doc, n->children, path+1, &rc,
2244 NULL, NULL, NULL, FALSE, 0, NULL);
2246 if (!n)
2247 goto fail;
2250 g_strfreev(path);
2252 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2253 return EPWMD_ATTR_NOT_FOUND;
2255 pthread_cleanup_push(xmlFree, a);
2256 pthread_testcancel();
2257 rc = assuan_send_data(ctx, a, xmlStrlen(a));
2258 pthread_testcancel();
2259 pthread_cleanup_pop(1);
2260 return rc;
2262 fail:
2263 g_strfreev(path);
2264 return rc;
2268 * req[0] - attribute
2269 * req[1] - element path
2270 * req[2] - value
2272 static int attribute_set(struct client_s *client, gchar **req)
2274 gchar **path = NULL;
2275 gpg_error_t rc;
2276 xmlNodePtr n;
2278 if (!req || !req[0] || !req[1] || !req[2])
2279 return EPWMD_COMMAND_SYNTAX;
2282 * Reserved attribute names.
2284 if (g_utf8_collate(req[0], "name") == 0) {
2286 * Only reserved for the account element. Not the rest of the
2287 * document.
2289 if (strchr(req[1], '\t') == NULL)
2290 return name_attribute(client, req + 1);
2292 else if (g_utf8_collate(req[0], "target") == 0)
2293 return target_attribute(client, req + 1);
2295 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2297 * The first argument may be only an account.
2299 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2300 return EPWMD_COMMAND_SYNTAX;
2303 n = find_account(client->doc, &path, &rc, NULL, 0);
2305 if (!n)
2306 goto fail;
2308 if (path[1]) {
2309 n = find_elements(client->doc, n->children, path+1, &rc,
2310 NULL, NULL, NULL, FALSE, 0, NULL);
2312 if (!n)
2313 goto fail;
2316 g_strfreev(path);
2317 return add_attribute(n, req[0], req[2]);
2319 fail:
2320 g_strfreev(path);
2321 return rc;
2325 * req[0] - command
2326 * req[1] - attribute name or element path if command is LIST
2327 * req[2] - element path
2328 * req[2] - element path or value
2330 static int attr_command(assuan_context_t ctx, char *line)
2332 struct client_s *client = assuan_get_pointer(ctx);
2333 gchar **req;
2334 gpg_error_t rc = 0;
2336 rc = file_modified(client);
2338 if (rc)
2339 return send_error(ctx, rc);
2341 req = split_input_line(line, " ", 4);
2343 if (!req || !req[0] || !req[1]) {
2344 g_strfreev(req);
2345 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2348 pthread_cleanup_push(req_cleanup, req);
2350 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2351 rc = attribute_set(client, req+1);
2352 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2353 rc = attribute_get(ctx, req+1);
2354 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2355 rc = attribute_delete(client, req+1);
2356 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2357 rc = attribute_list(ctx, req+1);
2358 else
2359 rc = EPWMD_COMMAND_SYNTAX;
2361 pthread_cleanup_pop(1);
2362 return send_error(ctx, rc);
2365 static int iscached_command(assuan_context_t ctx, char *line)
2367 gchar **req = split_input_line(line, " ", 0);
2368 guchar md5file[16];
2370 if (!req || !*req) {
2371 g_strfreev(req);
2372 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2375 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2376 g_strfreev(req);
2377 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2378 CACHE_LOCK(ctx);
2380 if (cache_iscached(md5file) == FALSE) {
2381 CACHE_UNLOCK;
2382 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2385 CACHE_UNLOCK;
2386 pthread_cleanup_pop(0);
2387 return send_error(ctx, 0);
2390 static int clearcache_command(assuan_context_t ctx, char *line)
2392 struct client_s *client = assuan_get_pointer(ctx);
2393 gchar **req = split_input_line(line, " ", 0);
2394 guchar md5file[16];
2396 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2397 CACHE_LOCK(ctx);
2399 if (!req || !*req) {
2400 g_strfreev(req);
2401 cache_clear(client->md5file, 2);
2402 CACHE_UNLOCK;
2403 return send_error(ctx, 0);
2406 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2407 g_strfreev(req);
2409 if (cache_clear(md5file, 1) == FALSE) {
2410 CACHE_UNLOCK;
2411 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2414 CACHE_UNLOCK;
2415 pthread_cleanup_pop(0);
2416 return send_error(ctx, 0);
2419 static int cachetimeout_command(assuan_context_t ctx, char *line)
2421 guchar md5file[16];
2422 glong timeout;
2423 gchar **req = split_input_line(line, " ", 0);
2424 gchar *p;
2425 struct client_s *client = assuan_get_pointer(ctx);
2427 pthread_cleanup_push(req_cleanup, req);
2429 if (!req || !*req || !req[1]) {
2430 g_strfreev(req);
2431 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2434 errno = 0;
2435 timeout = strtol(req[0], &p, 10);
2437 if (errno != 0 || *p != 0) {
2438 g_strfreev(req);
2439 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2442 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2443 pthread_cleanup_pop(1);
2444 pthread_cleanup_push(pthread_mutex_unlock, (void *)&cache_mutex);
2445 CACHE_LOCK(client->ctx);
2447 if (cache_set_timeout(md5file, timeout) == FALSE) {
2448 CACHE_UNLOCK;
2449 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2452 CACHE_UNLOCK;
2453 pthread_cleanup_pop(0);
2454 return send_error(ctx, 0);
2457 static int dump_command(assuan_context_t ctx, char *line)
2459 xmlChar *xml;
2460 gssize len;
2461 struct client_s *client = assuan_get_pointer(ctx);
2462 gpg_error_t rc;
2464 if (disable_list_and_dump == TRUE)
2465 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2467 rc = file_modified(client);
2469 if (rc)
2470 return send_error(ctx, rc);
2472 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2474 if (!xml) {
2475 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2476 return send_syserror(ctx, ENOMEM);
2479 pthread_cleanup_push(xmlFree, xml);
2480 pthread_testcancel();
2481 rc = assuan_send_data(ctx, xml, len);
2482 pthread_testcancel();
2483 pthread_cleanup_pop(1);
2484 return send_error(ctx, rc);
2487 static int getconfig_command(assuan_context_t ctx, gchar *line)
2489 struct client_s *client = assuan_get_pointer(ctx);
2490 gpg_error_t rc = 0;
2491 gchar filename[255]={0}, param[747]={0};
2492 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2494 if (strchr(line, ' ')) {
2495 sscanf(line, " %254[^ ] %746c", filename, param);
2496 fp = filename;
2497 paramp = param;
2500 if (fp && !valid_filename(fp))
2501 return send_error(ctx, EPWMD_INVALID_FILENAME);
2503 paramp = g_ascii_strdown(paramp, -1);
2505 if (!paramp) {
2506 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2507 return send_syserror(ctx, ENOMEM);
2510 p = get_key_file_string(fp ? fp : "global", paramp);
2511 g_free(paramp);
2513 if (!p)
2514 return send_error(ctx, GPG_ERR_NO_VALUE);
2516 tmp = expand_homedir(p);
2517 g_free(p);
2519 if (!tmp) {
2520 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2521 return send_syserror(ctx, ENOMEM);
2524 p = tmp;
2525 pthread_cleanup_push(g_free, p);
2526 pthread_testcancel();
2527 rc = assuan_send_data(ctx, p, strlen(p));
2528 pthread_testcancel();
2529 pthread_cleanup_pop(1);
2530 return send_error(ctx, rc);
2533 struct xpath_s {
2534 xmlXPathContextPtr xp;
2535 xmlXPathObjectPtr result;
2536 xmlBufferPtr buf;
2537 gchar **req;
2540 static void xpath_command_cleanup(void *arg)
2542 struct xpath_s *xpath = arg;
2544 req_cleanup(xpath->req);
2546 if (xpath->buf)
2547 xmlBufferFree(xpath->buf);
2549 if (xpath->result)
2550 xmlXPathFreeObject(xpath->result);
2552 if (xpath->xp)
2553 xmlXPathFreeContext(xpath->xp);
2556 static int xpath_command(assuan_context_t ctx, gchar *line)
2558 struct client_s *client = assuan_get_pointer(ctx);
2559 gpg_error_t rc;
2560 struct xpath_s xpath;
2562 if (disable_list_and_dump == TRUE)
2563 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2565 rc = file_modified(client);
2567 if (rc)
2568 return send_error(ctx, rc);
2570 if (!line || !*line)
2571 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2573 memset(&xpath, 0, sizeof(struct xpath_s));
2574 pthread_cleanup_push(xpath_command_cleanup, &xpath);
2576 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
2577 if (strv_printf(&xpath.req, "%s", line) == FALSE)
2578 return send_syserror(ctx, ENOMEM);
2581 xpath.xp = xmlXPathNewContext(client->doc);
2583 if (!xpath.xp) {
2584 xpath_command_cleanup(&xpath);
2585 return send_error(ctx, EPWMD_LIBXML_ERROR);
2588 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
2590 if (!xpath.result) {
2591 xpath_command_cleanup(&xpath);
2592 return send_error(ctx, EPWMD_LIBXML_ERROR);
2595 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
2596 rc = EPWMD_EMPTY_ELEMENT;
2597 goto fail;
2600 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
2601 (xmlChar *)xpath.req[1], &xpath.buf);
2603 if (rc)
2604 goto fail;
2605 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
2606 rc = EPWMD_EMPTY_ELEMENT;
2607 goto fail;
2609 else if (xpath.req[1])
2610 goto fail;
2612 pthread_testcancel();
2613 rc = assuan_send_data(ctx, xmlBufferContent(xpath.buf),
2614 xmlBufferLength(xpath.buf));
2615 pthread_testcancel();
2617 fail:
2618 if (1) {}
2619 pthread_cleanup_pop(1);
2620 return send_error(ctx, rc);
2623 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2624 gsize len)
2626 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2627 gpg_error_t rc = file_modified(client);
2628 gchar **req, **path = NULL, **path_orig = NULL, *content;
2629 xmlDocPtr doc;
2630 xmlNodePtr n, root, copy;
2632 if (assuan_rc || rc) {
2633 if (line)
2634 xfree(line);
2635 return assuan_rc ? assuan_rc : rc;
2638 req = split_input_line((gchar *)line, " ", 2);
2639 xfree(line);
2641 if (!req || !*req)
2642 return EPWMD_COMMAND_SYNTAX;
2644 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2645 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2646 return EPWMD_COMMAND_SYNTAX;
2649 content = req[1];
2651 if (!content || !*content) {
2652 rc = EPWMD_COMMAND_SYNTAX;
2653 goto fail;
2656 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2657 rc = EPWMD_INVALID_ELEMENT;
2658 goto fail;
2661 if (valid_element_path(path+1, FALSE) == FALSE) {
2662 rc = EPWMD_INVALID_ELEMENT;
2663 goto fail;
2666 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2668 if (!doc) {
2669 rc = EPWMD_LIBXML_ERROR;
2670 goto fail;
2673 root = xmlDocGetRootElement(doc);
2674 path_orig = g_strdupv(path);
2676 if (!path_orig) {
2677 xmlFreeDoc(doc);
2678 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2679 rc = gpg_error_from_errno(ENOMEM);
2680 goto fail;
2683 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2684 g_strfreev(path_orig);
2685 xmlFreeDoc(doc);
2686 rc = gpg_error_from_errno(ENOMEM);
2687 goto fail;
2690 n = find_account(client->doc, &path, &rc, NULL, 0);
2692 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2693 g_strfreev(path_orig);
2694 xmlFreeDoc(doc);
2695 goto fail;
2697 else if (!rc) {
2698 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2700 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2701 g_strfreev(path_orig);
2702 xmlFreeDoc(doc);
2703 goto fail;
2705 else if (!rc) {
2706 xmlNodePtr parent = n->parent;
2708 xmlUnlinkNode(n);
2709 xmlFreeNode(n);
2710 n = parent;
2714 g_strfreev(path);
2715 path = path_orig;
2717 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2718 n = create_element_path(client, &path, &rc);
2720 if (rc) {
2721 xmlFreeDoc(doc);
2722 goto fail;
2726 copy = xmlCopyNode(root, 1);
2727 n = xmlAddChild(n, copy);
2728 xmlFreeDoc(doc);
2730 if (!n)
2731 rc = EPWMD_LIBXML_ERROR;
2733 fail:
2734 g_strfreev(path);
2735 g_strfreev(req);
2736 client->inquire_status = INQUIRE_DONE;
2737 return rc;
2740 static int import_command(assuan_context_t ctx, gchar *line)
2742 gpg_error_t rc;
2743 struct client_s *client = assuan_get_pointer(ctx);
2745 rc = file_modified(client);
2747 if (rc)
2748 return send_error(ctx, rc);
2750 pthread_testcancel();
2751 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2752 pthread_testcancel();
2754 if (rc)
2755 return send_error(ctx, rc);
2757 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2758 client->inquire_status = INQUIRE_BUSY;
2759 return 0;
2762 static int lock_command(assuan_context_t ctx, gchar *line)
2764 gpg_error_t rc;
2765 struct client_s *client = assuan_get_pointer(ctx);
2767 rc = file_modified(client);
2769 if (rc)
2770 return send_error(ctx, rc);
2772 rc = lock_file_mutex(client);
2774 if (!rc)
2775 client->is_lock_cmd = TRUE;
2777 return send_error(ctx, rc);
2780 static int unlock_command(assuan_context_t ctx, gchar *line)
2782 struct client_s *client = assuan_get_pointer(ctx);
2783 gpg_error_t rc = file_modified(client);
2785 if (rc)
2786 return send_error(ctx, rc);
2788 unlock_file_mutex(client);
2789 return send_error(ctx, 0);
2792 static int getpid_command(assuan_context_t ctx, gchar *line)
2794 gpg_error_t rc;
2795 gchar buf[32];
2796 pid_t pid = getpid();
2798 print_fmt(buf, sizeof(buf), "%i", pid);
2799 pthread_testcancel();
2800 rc = assuan_send_data(ctx, buf, strlen(buf));
2801 pthread_testcancel();
2802 return send_error(ctx, rc);
2805 static int version_command(assuan_context_t ctx, gchar *line)
2807 gpg_error_t rc;
2808 gchar buf[32];
2810 print_fmt(buf, sizeof(buf), "%s", PACKAGE_VERSION);
2811 pthread_testcancel();
2812 rc = assuan_send_data(ctx, buf, strlen(buf));
2813 pthread_testcancel();
2814 return send_error(ctx, rc);
2817 static void bye_notify(assuan_context_t ctx)
2819 struct client_s *cl = assuan_get_pointer(ctx);
2820 #ifdef WITH_GNUTLS
2821 gint rc;
2823 if (!cl->thd->remote)
2824 return;
2826 do {
2827 rc = gnutls_bye(cl->thd->tls->ses, GNUTLS_SHUT_RDWR);
2828 } while (rc == GNUTLS_E_AGAIN);
2829 #endif
2831 /* This will let assuan_process_next() return. */
2832 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
2835 static void reset_notify(assuan_context_t ctx)
2837 struct client_s *cl = assuan_get_pointer(ctx);
2839 if (cl)
2840 cleanup_client(cl);
2843 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2845 gchar name[32] = {0}, value[256] = {0};
2847 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2848 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2850 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2851 struct client_s *cl = assuan_get_pointer(ctx);
2853 if (cl->thd->name)
2854 g_free(cl->thd->name);
2856 cl->thd->name = g_strdup(value);
2857 log_write("OPTION CLIENT %s", line);
2859 else
2860 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2862 return 0;
2865 static int option_handler(assuan_context_t ctx, const gchar *name,
2866 const gchar *value)
2868 struct client_s *client = assuan_get_pointer(ctx);
2870 if (!value || !*value)
2871 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2873 if (g_strcasecmp(name, (gchar *)"client") == 0)
2874 return parse_client_option(ctx, value);
2876 if (g_strcasecmp(name, (gchar *)"iterations") == 0) {
2877 long n;
2878 gchar *p = NULL;
2880 errno = 0;
2881 n = strtol(value, &p, 10);
2883 if (errno || (p && *p) || n < 0)
2884 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2886 g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", (guint)n);
2887 send_status_all(STATUS_CONFIG);
2889 #ifdef WITH_PINENTRY
2890 else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2891 g_free(client->pinentry->ttyname);
2892 client->pinentry->ttyname = g_strdup(value);
2894 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2895 g_free(client->pinentry->ttytype);
2896 client->pinentry->ttytype = g_strdup(value);
2898 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2899 g_free(client->pinentry->display);
2900 client->pinentry->display = g_strdup(value);
2902 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2903 g_free(client->pinentry->path);
2904 client->pinentry->path = g_strdup(value);
2906 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2907 g_free(client->pinentry->title);
2908 client->pinentry->title = g_strdup(value);
2910 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2911 g_free(client->pinentry->prompt);
2912 client->pinentry->prompt = g_strdup(value);
2914 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2915 g_free(client->pinentry->desc);
2916 client->pinentry->desc = g_strdup(value);
2919 * Look at client_thread() to see how this works.
2921 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2922 gchar *p = NULL;
2923 gint n = strtol(value, &p, 10);
2925 if (*p || n < 0)
2926 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2928 client->pinentry->timeout = n;
2930 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2931 gchar *p = NULL;
2932 gint n = strtol(value, &p, 10);
2934 if (*p || n < 0 || n > 1)
2935 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2937 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2939 #endif
2940 else
2941 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2943 log_write("OPTION %s=%s", name, value);
2944 return 0;
2947 gpg_error_t register_commands(assuan_context_t ctx)
2949 static struct {
2950 const gchar *name;
2951 gint (*handler)(assuan_context_t, gchar *line);
2952 } table[] = {
2953 { "OPEN", open_command },
2954 { "SAVE", save_command },
2955 { "LIST", list_command },
2956 { "REALPATH", realpath_command },
2957 { "STORE", store_command },
2958 { "DELETE", delete_command },
2959 { "GET", get_command },
2960 { "ATTR", attr_command },
2961 { "ISCACHED", iscached_command },
2962 { "CLEARCACHE", clearcache_command },
2963 { "CACHETIMEOUT", cachetimeout_command },
2964 { "GETCONFIG", getconfig_command },
2965 { "DUMP", dump_command },
2966 { "XPATH", xpath_command },
2967 { "IMPORT", import_command },
2968 { "LOCK", lock_command },
2969 { "UNLOCK", unlock_command },
2970 { "GETPID", getpid_command },
2971 { "VERSION", version_command },
2972 { "INPUT", NULL },
2973 { "OUTPUT", NULL },
2974 { NULL, NULL }
2976 gint i, rc;
2978 for (i=0; table[i].name; i++) {
2979 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2981 if (rc)
2982 return rc;
2985 rc = assuan_register_bye_notify(ctx, bye_notify);
2987 if (rc)
2988 return rc;
2990 rc = assuan_register_option_handler(ctx, option_handler);
2992 if (rc)
2993 return rc;
2995 rc = assuan_register_reset_notify(ctx, reset_notify);
2997 if (rc)
2998 return rc;
3000 return assuan_register_post_cmd_notify(ctx, command_finalize);
3003 gpg_error_t try_xml_decrypt(assuan_context_t ctx, guchar *key,
3004 struct client_crypto_s *crypto, gpointer *dst, gsize *dst_len)
3006 gsize insize, len;
3007 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
3008 guint64 iter = 0, n_iter = 0, iter_progress = 0;
3009 gint zrc = 0;
3010 gulong outsize = 0;
3011 gpg_error_t rc;
3012 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->fh1) : sizeof(crypto->fh->fh2);
3013 guint64 fh_iter = crypto->fh->v1 ? crypto->fh->fh1.iter : crypto->fh->fh2.iter;
3015 lseek(crypto->fh->fd, fh_size, SEEK_SET);
3016 insize = crypto->fh->st.st_size - fh_size;
3017 crypto->iv = gcry_malloc(gcryblocksize);
3019 if (!crypto->iv) {
3020 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3021 return gpg_error_from_errno(ENOMEM);
3024 /* No encryption iterations. This is a plain (gzipped) file. */
3025 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0)) {
3027 * cache_file_count() needs both .used == TRUE and a valid key in
3028 * order for it to count as a used cache entry. Fixes CACHE status
3029 * messages.
3031 memset(key, '!', gcrykeysize);
3034 if (crypto->fh->v1)
3035 memcpy(crypto->iv, crypto->fh->fh1.iv, gcryblocksize);
3036 else
3037 memcpy(crypto->iv, crypto->fh->fh2.iv, gcryblocksize);
3039 crypto->inbuf = gcry_malloc(insize);
3041 if (!crypto->inbuf) {
3042 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3043 return gpg_error_from_errno(ENOMEM);
3046 crypto->insize = insize;
3047 len = read(crypto->fh->fd, crypto->inbuf, crypto->insize);
3048 pthread_testcancel();
3050 if (len != crypto->insize)
3051 return GPG_ERR_INV_LENGTH;
3053 if ((crypto->fh->v1 && fh_iter < 0) || (!crypto->fh->v1 && fh_iter <= 0))
3054 goto decompress;
3056 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3057 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3058 return rc;
3061 if ((rc = gcry_cipher_setkey(crypto->gh, key, gcrykeysize))) {
3062 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3063 return rc;
3066 iter_progress = (guint64)get_key_file_integer(client && client->filename ?
3067 client->filename : "global", "iteration_progress");
3069 if (iter_progress > 0 && fh_iter >= iter_progress) {
3070 rc = send_status(ctx, STATUS_DECRYPT, "0 %llu", fh_iter);
3071 pthread_testcancel();
3073 if (rc)
3074 return rc;
3077 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3079 if (rc)
3080 return rc;
3082 crypto->tkey = gcry_malloc(gcrykeysize);
3084 if (!crypto->tkey) {
3085 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
3086 return gpg_error_from_errno(ENOMEM);
3089 memcpy(crypto->tkey, key, gcrykeysize);
3090 guchar *tkey = crypto->tkey;
3091 tkey[0] ^= 1;
3093 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, gcrykeysize))) {
3094 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3095 return rc;
3098 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
3099 if (iter_progress > 0 && iter >= iter_progress) {
3100 if (!(iter % iter_progress)) {
3101 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu",
3102 ++n_iter * iter_progress, fh_iter);
3103 pthread_testcancel();
3105 if (rc)
3106 return rc;
3110 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, gcryblocksize))) {
3111 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3112 return rc;
3115 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3117 if (rc) {
3118 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
3119 return rc;
3122 iter++;
3125 if (iter_progress && fh_iter >= iter_progress) {
3126 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu", fh_iter, fh_iter);
3127 pthread_testcancel();
3129 if (rc)
3130 return rc;
3133 decompress:
3134 if (do_decompress(ctx, crypto->inbuf, crypto->insize,
3135 (gpointer *)&crypto->outbuf, &outsize, &zrc) == FALSE) {
3136 if (zrc == Z_MEM_ERROR)
3137 return gpg_error_from_errno(ENOMEM);
3138 else
3139 return EPWMD_BADKEY; // Not a valid gzip header. Must be a bad key.
3142 if (g_strncasecmp(crypto->outbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
3143 gcry_free(crypto->outbuf);
3144 crypto->outbuf = NULL;
3145 return EPWMD_BADKEY;
3148 if (ctx) {
3149 client->xml = crypto->outbuf;
3150 client->len = outsize;
3151 crypto->outbuf = NULL;
3153 else if (dst) {
3154 *dst = crypto->outbuf;
3155 *dst_len = outsize;
3156 crypto->outbuf = NULL;
3159 /* The calling function should free the crypto struct. */
3160 return 0;
3164 * This is called after every Assuan command.
3166 void command_finalize(assuan_context_t ctx, gint rc)
3168 struct client_s *client = assuan_get_pointer(ctx);
3170 if (!client->is_lock_cmd)
3171 unlock_file_mutex(client);