Since guint64 is really an unsigned long dont cast it as unsigned long
[pwmd.git] / src / commands.c
blob007da65e3b71f66cbbf6db0d912a6e3a63287620
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2010 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <gcrypt.h>
30 #include <zlib.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #ifdef WITH_LIBACL
37 #include <sys/acl.h>
38 #endif
40 #include "mem.h"
41 #include "xml.h"
42 #include "common.h"
44 #ifdef WITH_PINENTRY
45 #include "pinentry.h"
46 #endif
48 #include "pwmd_error.h"
49 #include "cache.h"
50 #include "misc.h"
51 #include "commands.h"
52 #include "lock.h"
54 struct gz_s {
55 z_stream z;
56 gpointer out;
57 gboolean done;
58 status_msg_t which;
61 static void *z_alloc(void *data, unsigned items, unsigned size)
63 return gcry_calloc(items, size);
66 static void z_free(void *data, void *p)
68 gcry_free(p);
71 static gpg_error_t file_modified(struct client_s *client)
73 struct stat st;
74 gpg_error_t rc;
76 if (client->state != STATE_OPEN)
77 return EPWMD_NO_FILE;
79 rc = lock_file_mutex(client);
81 if (rc)
82 return rc;
84 if (lstat(client->filename, &st) == 0 && client->mtime) {
85 if (client->mtime != st.st_mtime)
86 return EPWMD_FILE_MODIFIED;
89 pth_cancel_point();
90 return 0;
93 static gpg_error_t parse_xml(assuan_context_t ctx)
95 struct client_s *client = assuan_get_pointer(ctx);
97 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
99 if (!client->doc)
100 return EPWMD_LIBXML_ERROR;
102 return 0;
105 void unlock_file_mutex(struct client_s *client)
107 pth_mutex_t *m;
109 #ifdef WITH_PINENTRY
110 if (client->has_lock == FALSE || client->pinentry->status != PINENTRY_NONE)
111 #else
112 if (client->has_lock == FALSE)
113 #endif
114 return;
116 CACHE_LOCK(client->ctx);
118 if (cache_get_mutex(client->md5file, &m) == FALSE) {
119 CACHE_UNLOCK;
120 return;
123 CACHE_UNLOCK;
124 MUTEX_UNLOCK(m);
125 client->has_lock = client->is_lock_cmd = FALSE;
128 gpg_error_t lock_file_mutex(struct client_s *client)
130 pth_mutex_t *m;
131 gpg_error_t rc = 0;
133 if (client->has_lock == TRUE)
134 return 0;
136 CACHE_LOCK(client->ctx);
138 if (cache_get_mutex(client->md5file, &m) == FALSE) {
139 CACHE_UNLOCK;
140 return 0;
143 CACHE_UNLOCK;
144 MUTEX_TRYLOCK(client->ctx, m, rc);
146 if (!rc)
147 client->has_lock = TRUE;
149 return rc;
152 void free_client(struct client_s *client)
154 if (client->doc)
155 xmlFreeDoc(client->doc);
157 if (client->xml)
158 gcry_free(client->xml);
160 if (client->filename)
161 g_free(client->filename);
163 if (client->crypto)
164 cleanup_crypto(&client->crypto);
166 if (client->xml_error)
167 xmlResetError(client->xml_error);
170 void cleanup_client(struct client_s *client)
172 assuan_context_t ctx = client->ctx;
173 struct client_thread_s *thd = client->thd;
174 gboolean has_lock = client->has_lock;
175 #ifdef WITH_PINENTRY
176 struct pinentry_s *pin = client->pinentry;
177 #endif
179 unlock_file_mutex(client);
180 CACHE_LOCK(client->ctx);
181 cache_decr_refcount(client->md5file);
184 * This may be a new file so don't use a cache slot. save_command() will
185 * set this to FALSE on success.
187 if (client->new == TRUE)
188 cache_clear(client->md5file, 1);
190 CACHE_UNLOCK;
191 free_client(client);
192 memset(client, 0, sizeof(struct client_s));
193 client->state = STATE_CONNECTED;
194 client->ctx = ctx;
195 client->thd = thd;
196 client->freed = TRUE;
197 #ifdef WITH_PINENTRY
198 client->pinentry = pin;
199 #endif
200 client->has_lock = has_lock;
203 static void gz_cleanup(void *arg)
205 struct gz_s **gz = (struct gz_s **)arg;
207 if (!gz)
208 return;
210 if (!(*gz)->done && (*gz)->out)
211 gcry_free((*gz)->out);
213 if ((*gz)->which == STATUS_COMPRESS) {
214 if ((*gz)->z.zalloc)
215 deflateEnd(&(*gz)->z);
217 else {
218 if ((*gz)->z.zalloc)
219 inflateEnd(&(*gz)->z);
222 g_free(*gz);
223 *gz = NULL;
226 gboolean do_decompress(assuan_context_t ctx, gpointer in, gulong insize,
227 gpointer *out, gulong *outsize, gint *rc)
229 struct gz_s *gz;
230 gz_header h;
231 gchar buf[17];
233 gz = g_malloc0(sizeof(struct gz_s));
235 if (!gz) {
236 *rc = gpg_error_from_errno(ENOMEM);
237 return FALSE;
240 pth_cleanup_push(gz_cleanup, &gz);
241 gz->which = STATUS_DECOMPRESS;
242 gz->z.zalloc = z_alloc;
243 gz->z.zfree = z_free;
244 gz->z.next_in = in;
245 gz->z.avail_in = (uInt)insize;
246 gz->z.avail_out = zlib_bufsize;
247 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
249 if (!gz->out) {
250 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
251 *rc = Z_MEM_ERROR;
252 pth_cleanup_pop(1);
253 return FALSE;
256 *rc = inflateInit2(&gz->z, 47);
258 if (*rc != Z_OK) {
259 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
260 pth_cleanup_pop(1);
261 return FALSE;
264 memset(&h, 0, sizeof(gz_header));
265 h.comment = (guchar *)buf;
266 h.comm_max = sizeof(buf);
267 *rc = inflateGetHeader(&gz->z, &h);
269 if (*rc != Z_OK) {
270 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
271 pth_cleanup_pop(1);
272 return FALSE;
275 *rc = inflate(&gz->z, Z_BLOCK);
277 if (*rc != Z_OK) {
278 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
279 pth_cleanup_pop(1);
280 return FALSE;
283 if (h.comment)
284 insize = (gulong)strtol((gchar *)h.comment, NULL, 10);
286 do {
287 gpointer p;
289 *rc = inflate(&gz->z, Z_FINISH);
291 switch (*rc) {
292 case Z_OK:
293 break;
294 case Z_BUF_ERROR:
295 if (!gz->z.avail_out) {
296 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
298 if (!p) {
299 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
300 *rc = Z_MEM_ERROR;
301 goto fail;
304 gz->out = p;
305 gz->z.next_out = (guchar *)gz->out + gz->z.total_out;
306 gz->z.avail_out = zlib_bufsize;
307 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li",
308 gz->z.total_out, insize);
310 if (*rc)
311 goto fail;
313 break;
314 case Z_STREAM_END:
315 break;
316 default:
317 goto fail;
318 break;
320 } while (*rc != Z_STREAM_END);
322 *rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li", gz->z.total_out,
323 insize);
325 if (*rc)
326 goto fail;
328 *out = gz->out;
329 *outsize = gz->z.total_out;
330 gz->done = TRUE;
331 pth_cleanup_pop(1);
332 *rc = 0;
333 return TRUE;
335 fail:
336 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
337 pth_cleanup_pop(1);
338 return FALSE;
341 file_header_internal_t *read_file_header(const gchar *filename, gboolean v1,
342 gpg_error_t *rc)
344 gint fd;
345 gsize len;
346 file_header_internal_t *fh = g_malloc0(sizeof(file_header_internal_t));
347 gsize fh_size;
348 gpointer p;
350 *rc = 0;
352 if (!fh) {
353 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
354 *rc = gpg_error_from_errno(ENOMEM);
355 return NULL;
358 pth_cleanup_push(g_free, fh);
359 fh_size = v1 ? sizeof(fh->ver.fh1) : sizeof(fh->ver.fh2);
361 if (lstat(filename, &fh->st) == -1) {
362 *rc = gpg_error_from_syserror();
363 pth_cleanup_pop(1);
364 return NULL;
367 if (!S_ISREG(fh->st.st_mode)) {
368 *rc = GPG_ERR_ENOANO;
369 pth_cleanup_pop(1);
370 return NULL;
373 fd = open(filename, O_RDONLY);
375 if (fd == -1) {
376 *rc = gpg_error_from_errno(errno);
377 pth_cleanup_pop(1);
378 return NULL;
381 pth_cleanup_push(cleanup_fd_cb, &fd);
382 p = v1 ? (void *)&fh->ver.fh1 : (void *)&fh->ver.fh2;
383 len = pth_read(fd, p, fh_size);
385 if (len != fh_size) {
386 gint n = errno;
387 pth_cleanup_pop(1);
388 pth_cleanup_pop(1);
389 *rc = gpg_error_from_errno(n);
390 return NULL;
393 fh->v1 = v1;
394 fh->fd = fd;
395 pth_cleanup_pop(0);
396 pth_cleanup_pop(0);
397 return fh;
401 * This is called before every Assuan command.
403 gpg_error_t command_startup(assuan_context_t ctx)
405 struct client_s *cl = assuan_get_pointer(ctx);
406 gpg_error_t rc;
407 const gchar *name = assuan_get_command_name(ctx);
409 if (!name)
410 return 0;
412 log_write1("%s", name);
414 if (!g_ascii_strcasecmp(name, "ISCACHED") ||
415 !g_ascii_strcasecmp(name, "CLEARCACHE") ||
416 !g_ascii_strcasecmp(name, "CACHETIMEOUT") ||
417 !g_ascii_strcasecmp(name, "GETCONFIG") ||
418 !g_ascii_strcasecmp(name, "GETPID") ||
419 !g_ascii_strcasecmp(name, "VERSION") ||
420 !g_ascii_strcasecmp(name, "SET") ||
421 !g_ascii_strcasecmp(name, "BYE") ||
422 !g_ascii_strcasecmp(name, "NOP") ||
423 !g_ascii_strcasecmp(name, "CANCEL") ||
424 !g_ascii_strcasecmp(name, "RESET") ||
425 !g_ascii_strcasecmp(name, "END") ||
426 !g_ascii_strcasecmp(name, "HELP") ||
427 !g_ascii_strcasecmp(name, "OPTION") ||
428 !g_ascii_strcasecmp(name, "INPUT") ||
429 !g_ascii_strcasecmp(name, "OUTPUT") ||
430 !g_ascii_strcasecmp(name, "UNSET"))
431 return 0;
433 cl->last_rc = rc = file_modified(cl);
435 if (rc) {
436 if ((rc == EPWMD_NO_FILE || rc == EPWMD_FILE_MODIFIED) &&
437 !g_ascii_strcasecmp(name, "OPEN"))
438 rc = 0;
441 return rc;
444 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar *key,
445 gboolean cached)
447 struct client_s *client = assuan_get_pointer(ctx);
448 gpg_error_t rc;
449 gint timeout;
451 /* New file. */
452 if (!client->crypto->fh) {
453 if (key[0])
454 goto update_cache;
456 goto done;
459 rc = init_client_crypto2(client->filename, client->crypto);
461 if (rc) {
462 cleanup_client(client);
463 return send_error(ctx, rc);
466 rc = try_xml_decrypt(ctx, key, client->crypto, NULL, NULL);
468 if (rc) {
469 cleanup_client(client);
470 return send_error(ctx, rc);
473 update_cache:
474 CACHE_LOCK(client->ctx);
476 if (cached == FALSE) {
477 if (cache_update_key(client->md5file, key) == FALSE) {
478 cleanup_client(client);
479 CACHE_UNLOCK;
480 return send_syserror(ctx, ENOMEM);
483 timeout = get_key_file_integer(client->filename, "cache_timeout");
484 cache_reset_timeout(client->md5file, timeout);
486 else
487 cache_set_timeout(client->md5file, -2);
489 CACHE_UNLOCK;
491 done:
492 rc = parse_xml(ctx);
494 if (client->xml) {
495 gcry_free(client->xml);
496 client->xml = NULL;
499 if (!rc) {
500 if (client->new == FALSE)
501 send_status_all(STATUS_CACHE);
503 client->state = STATE_OPEN;
506 if (!rc && client->new == FALSE &&
507 client->crypto->fh->ver.fh2.iter != (guint64)get_key_file_double(client->filename, "iterations")) {
508 MUTEX_LOCK(&rcfile_mutex);
509 g_key_file_set_double(keyfileh, client->filename, "iterations",
510 client->crypto->fh->ver.fh2.iter);
511 MUTEX_UNLOCK(&rcfile_mutex);
512 send_status_all(STATUS_CONFIG);
515 cleanup_crypto(&client->crypto);
516 return send_error(ctx, rc);
519 static void req_cleanup(void *arg)
521 if (!arg)
522 return;
524 g_strfreev((gchar **)arg);
527 static gpg_error_t open_command(assuan_context_t ctx, gchar *line)
529 gboolean cached = FALSE;
530 gpg_error_t rc;
531 struct client_s *client = assuan_get_pointer(ctx);
532 gchar **req;
533 gchar *filename = NULL;
534 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
536 rc = command_startup(ctx);
538 if (rc)
539 return send_error(ctx, rc);
541 if ((req = split_input_line(line, " ", 2)) != NULL)
542 filename = req[0];
544 pth_cleanup_push(req_cleanup, req);
546 if (!filename || !*filename) {
547 pth_cleanup_pop(1);
548 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
551 log_write2("ARGS=\"%s\" %s", filename, req[1] ? "<passphrase>" : "");
553 if (valid_filename(filename) == FALSE) {
554 pth_cleanup_pop(1);
555 return send_error(ctx, EPWMD_INVALID_FILENAME);
558 if (client->state == STATE_OPEN)
559 cleanup_client(client);
561 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
562 CACHE_LOCK(client->ctx);
564 if (cache_has_file(client->md5file) == FALSE) {
565 if (cache_add_file(client->md5file, NULL) == FALSE) {
566 pth_cleanup_pop(1);
567 CACHE_UNLOCK;
568 return send_syserror(ctx, ENOMEM);
572 cache_incr_refcount(client->md5file);
573 CACHE_UNLOCK;
574 rc = lock_file_mutex(client);
576 if (rc) {
577 pth_cleanup_pop(1);
578 return send_error(ctx, rc);
581 client->freed = FALSE;
582 client->crypto = init_client_crypto();
584 if (!client->crypto) {
585 pth_cleanup_pop(1);
586 cleanup_client(client);
587 return send_syserror(ctx, ENOMEM);
590 client->crypto->key = gcry_malloc(hashlen);
592 if (!client->crypto->key) {
593 pth_cleanup_pop(1);
594 log_write("%s(%i): %s", __FUNCTION__, __LINE__,
595 gpg_error_from_errno(ENOMEM));
596 cleanup_client(client);
597 return send_syserror(ctx, ENOMEM);
600 memset(client->crypto->key, 0, hashlen);
601 client->crypto->fh = read_file_header(filename, FALSE, &rc);
603 if (!client->crypto->fh) {
604 if (gpg_err_code_to_errno(rc) != ENOENT) {
605 log_write("%s: %s", filename, pwmd_strerror(rc));
606 pth_cleanup_pop(1);
607 cleanup_client(client);
608 return send_error(ctx, rc);
612 * New files don't need a key.
614 if ((client->xml = new_document()) == NULL) {
615 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
616 pth_cleanup_pop(1);
617 cleanup_client(client);
618 return send_syserror(ctx, ENOMEM);
621 client->len = xmlStrlen(client->xml);
622 client->new = TRUE;
623 client->filename = g_strdup(filename);
625 if (!client->filename) {
626 pth_cleanup_pop(1);
627 cleanup_client(client);
628 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
629 return send_syserror(ctx, ENOMEM);
632 if (req[1] && *req[1])
633 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
634 strlen(req[1]));
636 pth_cleanup_pop(1);
637 #ifdef WITH_PINENTRY
638 client->pinentry->filename = g_strdup(client->filename);
640 if (!client->pinentry->filename) {
641 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
642 cleanup_client(client);
643 return send_syserror(ctx, ENOMEM);
645 #endif
646 return open_command_finalize(ctx, client->crypto->key, cached);
648 else
649 client->mtime = client->crypto->fh->st.st_mtime;
651 client->filename = g_strdup(filename);
653 if (!client->filename) {
654 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
655 pth_cleanup_pop(1);
656 cleanup_client(client);
657 return send_syserror(ctx, ENOMEM);
660 #ifdef WITH_PINENTRY
661 client->pinentry->filename = g_strdup(client->filename);
663 if (!client->pinentry->filename) {
664 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
665 pth_cleanup_pop(1);
666 cleanup_client(client);
667 return send_syserror(ctx, ENOMEM);
669 #endif
671 if (client->crypto->fh->ver.fh2.iter <= 0ULL)
672 goto done;
674 CACHE_LOCK(client->ctx);
675 cached = cache_get_key(client->md5file, client->crypto->key);
676 CACHE_UNLOCK;
678 if (cached == FALSE) {
679 gchar *tmp = get_key_file_string(filename, "key_file");
681 if (tmp) {
682 g_free(tmp);
683 pth_cleanup_pop(1);
684 cleanup_client(client);
685 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
689 * No key specified and no matching filename found in the cache. Use
690 * pinentry to retrieve the key. Cannot return assuan_process_done()
691 * here otherwise the command will be interrupted. The event loop in
692 * client_thread() will poll the file descriptor waiting for it to
693 * become ready to read a pinentry_key_s which will contain the
694 * entered key or an error code. It will then call
695 * open_command_finalize() to to finish the command.
697 if (!req[1] || !*req[1]) {
698 #ifdef WITH_PINENTRY
699 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
701 /* From set_pinentry_defaults(). */
702 if (client->pinentry->enable == FALSE ||
703 (client->pinentry->enable == -1 && b == FALSE)) {
704 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
705 goto done;
708 pth_cleanup_pop(1);
709 rc = lock_pin_mutex(client);
711 if (rc) {
712 unlock_pin_mutex(client->pinentry);
713 cleanup_client(client);
714 return send_error(ctx, rc);
717 client->pinentry->which = PINENTRY_OPEN;
718 rc = pinentry_fork(ctx);
720 if (rc) {
721 unlock_pin_mutex(client->pinentry);
722 cleanup_client(client);
723 return send_error(ctx, rc);
726 // Called from pinentry iterate.
727 client->pinentry->cb = open_command_finalize;
728 client->pinentry->status = PINENTRY_INIT;
729 return 0;
730 #else
731 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
732 goto done;
733 #endif
736 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
737 strlen(req[1]));
739 else if (req && req[1] && *req[1]) {
740 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, req[1],
741 strlen(req[1]) ? strlen(req[1]) : 1);
744 done:
745 pth_cleanup_pop(1);
746 return open_command_finalize(ctx, client->crypto->key, cached);
749 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
750 guint size, gpointer *out, gulong *outsize, gint *rc)
752 struct gz_s *gz;
753 gz_header h;
754 gchar buf[17];
755 gint cmd = Z_NO_FLUSH;
757 gz = g_malloc0(sizeof(struct gz_s));
759 if (!gz) {
760 *rc = gpg_error_from_errno(ENOMEM);
761 return FALSE;
764 pth_cleanup_push(gz_cleanup, &gz);
765 gz->which = STATUS_COMPRESS;
766 gz->z.zalloc = z_alloc;
767 gz->z.zfree = z_free;
768 gz->z.next_in = data;
769 gz->z.avail_in = size < zlib_bufsize ? (uInt)size : (uInt)zlib_bufsize;
770 gz->z.avail_out = (uInt)zlib_bufsize;
771 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
773 if (!gz->out) {
774 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
775 *rc = Z_MEM_ERROR;
776 pth_cleanup_pop(1);
777 return FALSE;
780 *rc = deflateInit2(&gz->z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
782 if (*rc != Z_OK) {
783 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
784 pth_cleanup_pop(1);
785 return FALSE;
788 /* Rather than store the size of the uncompressed data in the file header,
789 * store it in the comment field of the gzip header. Don't give anyone too
790 * much information. Not sure why really, but it seems the right way. :)
792 memset(&h, 0, sizeof(gz_header));
793 g_snprintf(buf, sizeof(buf), "%u", size);
794 h.comment = (guchar *)buf;
795 *rc = deflateSetHeader(&gz->z, &h);
797 if (*rc != Z_OK) {
798 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
799 pth_cleanup_pop(1);
800 return FALSE;
803 do {
804 gpointer p;
806 *rc = deflate(&gz->z, cmd);
808 switch (*rc) {
809 case Z_OK:
810 break;
811 case Z_BUF_ERROR:
812 if (!gz->z.avail_out) {
813 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
815 if (!p) {
816 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
817 *rc = Z_MEM_ERROR;
818 goto fail;
821 gz->out = p;
822 gz->z.next_out = (guchar *)gz->out + gz->z.total_out;
823 gz->z.avail_out = zlib_bufsize;
826 if (!gz->z.avail_in && gz->z.total_in < size) {
827 if (gz->z.total_in + zlib_bufsize > size)
828 gz->z.avail_in = size - gz->z.total_in;
829 else
830 gz->z.avail_in = zlib_bufsize;
832 *rc = send_status(ctx, STATUS_COMPRESS, "%li %u",
833 gz->z.total_in, size);
835 if (*rc)
836 goto fail;
839 if (gz->z.total_in >= size)
840 cmd = Z_FINISH;
842 break;
843 case Z_STREAM_END:
844 break;
845 default:
846 goto fail;
848 } while (*rc != Z_STREAM_END);
850 *rc = send_status(ctx, STATUS_COMPRESS, "%li %u", gz->z.total_in, size);
852 if (*rc)
853 goto fail;
855 *out = gz->out;
856 *outsize = gz->z.total_out;
857 *rc = 0;
858 gz->done = TRUE;
859 pth_cleanup_pop(1);
860 return TRUE;
862 fail:
863 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
864 pth_cleanup_pop(1);
865 return FALSE;
868 #define CRYPTO_BLOCKSIZE(c) (c->blocksize * 1024)
870 static gpg_error_t iterate_crypto_once(struct client_s *client,
871 struct client_crypto_s *crypto, status_msg_t which)
873 gpg_error_t rc = 0;
874 goffset len = CRYPTO_BLOCKSIZE(crypto);
875 gpointer p = gcry_malloc(len);
876 goffset total = 0;
877 gpointer inbuf;
879 if (!p)
880 return gpg_err_code_from_errno(ENOMEM);
882 if (crypto->insize < CRYPTO_BLOCKSIZE(crypto))
883 len = crypto->insize;
885 pth_cleanup_push(gcry_free, p);
887 for (;;) {
888 inbuf = (guchar *)crypto->inbuf + total;
889 guchar *tmp;
891 if (len + total > crypto->insize)
892 len = crypto->blocksize;
894 if (which == STATUS_ENCRYPT)
895 rc = gcry_cipher_encrypt(crypto->gh, p, len, inbuf, len);
896 else
897 rc = gcry_cipher_decrypt(crypto->gh, p, len, inbuf, len);
899 if (rc)
900 goto done;
902 tmp = (guchar *)crypto->inbuf + total;
903 memmove(tmp, p, len);
904 total += len;
906 if (total >= crypto->insize)
907 break;
909 pth_cancel_point();
912 done:
913 pth_cleanup_pop(1);
914 return rc;
917 /* The crypto struct must be setup for iterations and .key. */
918 gpg_error_t do_xml_encrypt(struct client_s *client,
919 struct client_crypto_s *crypto, const gchar *filename)
921 goffset len = crypto->insize;
922 gpointer inbuf;
923 gchar *p;
924 gpg_error_t rc;
925 guint64 iter_progress = 0ULL, n_iter = 0ULL, xiter = 0ULL;
926 gchar tmp[FILENAME_MAX];
927 struct stat st;
928 mode_t mode = 0;
929 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
931 if (!crypto->fh->ver.fh2.iter) {
933 * cache_file_count() needs both .used == TRUE and a valid key in
934 * order for it to count as a used cache entry. Fixes CACHE status
935 * messages.
937 memset(crypto->key, '!', hashlen);
938 goto write_file;
942 * Resize the existing xml buffer to the block size required by gcrypt
943 * rather than duplicating it and wasting memory.
945 len = (crypto->insize / crypto->blocksize) * crypto->blocksize;
947 if (crypto->insize % crypto->blocksize)
948 len += crypto->blocksize;
950 inbuf = gcry_realloc(crypto->inbuf, len);
952 if (!inbuf) {
953 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
954 return gpg_error_from_errno(ENOMEM);
957 crypto->inbuf = inbuf;
958 crypto->insize = len;
959 gcry_create_nonce(crypto->fh->ver.fh2.iv, crypto->blocksize);
960 crypto->tkey = gcry_malloc(hashlen);
962 if (!crypto->tkey) {
963 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
964 return gpg_error_from_errno(ENOMEM);
967 memcpy(crypto->tkey, crypto->key, hashlen);
968 guchar *tkey = crypto->tkey;
969 tkey[0] ^= 1;
971 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, crypto->keysize))) {
972 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
973 return rc;
976 iter_progress = (guint64)get_key_file_double(
977 client ? client->filename : "global", "iteration_progress");
979 if (iter_progress && crypto->fh->ver.fh2.iter >= iter_progress) {
980 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
981 "0 %llu", crypto->fh->ver.fh2.iter);
983 if (rc)
984 return rc;
987 while (xiter < crypto->fh->ver.fh2.iter-1) {
988 if (iter_progress > 0ULL && xiter >= iter_progress) {
989 if (!(xiter % iter_progress)) {
990 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
991 "%llu %llu", ++n_iter * iter_progress,
992 crypto->fh->ver.fh2.iter);
994 if (rc)
995 return rc;
999 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->ver.fh2.iv,
1000 crypto->blocksize))) {
1001 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
1002 return rc;
1005 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1007 if (rc) {
1008 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
1009 return rc;
1012 xiter++;
1015 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->ver.fh2.iv,
1016 crypto->blocksize))) {
1017 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
1018 return rc;
1021 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, crypto->keysize))) {
1022 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
1023 return rc;
1026 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1028 if (rc)
1029 return rc;
1031 if (iter_progress && crypto->fh->ver.fh2.iter >= iter_progress) {
1032 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1033 "%llu %llu", crypto->fh->ver.fh2.iter, crypto->fh->ver.fh2.iter);
1035 if (rc)
1036 return rc;
1039 write_file:
1040 tmp[0] = 0;
1042 if (filename) {
1043 if (!client && !g_ascii_strcasecmp(filename, "-")) {
1044 crypto->fh->fd = STDOUT_FILENO;
1045 goto do_write_file;
1048 if (lstat(filename, &st) == 0) {
1049 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
1051 if (!(mode & S_IWUSR))
1052 return gpg_error_from_errno(EACCES);
1054 else if (errno != ENOENT)
1055 return gpg_error_from_errno(errno);
1057 g_snprintf(tmp, sizeof(tmp), "%s.XXXXXX", filename);
1058 crypto->fh->fd = mkstemp(tmp);
1060 if (crypto->fh->fd == -1) {
1061 rc = errno;
1062 p = strrchr(tmp, '/');
1063 p++;
1064 log_write("%s: %s", p, strerror(rc));
1065 return gpg_error_from_errno(rc);
1068 pth_cleanup_push(cleanup_unlink_cb, tmp);
1070 else
1072 * xml_import() or convert_file() from command line.
1074 crypto->fh->fd = STDOUT_FILENO;
1076 do_write_file:
1077 crypto->fh->ver.fh2.magic[0] = '\177';
1078 crypto->fh->ver.fh2.magic[1] = 'P';
1079 crypto->fh->ver.fh2.magic[2] = 'W';
1080 crypto->fh->ver.fh2.magic[3] = 'M';
1081 crypto->fh->ver.fh2.magic[4] = 'D';
1082 crypto->fh->ver.fh2.version = VERSION_HEX;
1083 len = pth_write(crypto->fh->fd, &crypto->fh->ver.fh2, sizeof(crypto->fh->ver.fh2));
1085 if (len != sizeof(crypto->fh->ver.fh2)) {
1086 len = errno;
1088 if (tmp[0])
1089 pth_cleanup_pop(1);
1091 return gpg_error_from_errno(len);
1094 len = pth_write(crypto->fh->fd, crypto->inbuf, crypto->insize);
1096 if (len != crypto->insize) {
1097 len = errno;
1099 if (tmp[0])
1100 pth_cleanup_pop(1);
1102 return gpg_error_from_errno(len);
1105 if (fsync(crypto->fh->fd) == -1) {
1106 len = errno;
1108 if (tmp[0])
1109 pth_cleanup_pop(1);
1111 return gpg_error_from_errno(len);
1114 if (tmp[0]) {
1115 #ifdef WITH_LIBACL
1116 acl_t acl;
1117 #endif
1118 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
1119 gchar tmp2[FILENAME_MAX];
1121 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
1122 #ifdef WITH_LIBACL
1123 acl = acl_get_file(filename, ACL_TYPE_ACCESS);
1125 if (!acl)
1126 log_write("ACL: %s: %s", filename, strerror(errno));
1127 #endif
1129 if (rename(filename, tmp2) == -1) {
1130 len = errno;
1131 pth_cleanup_pop(1);
1132 #ifdef WITH_LIBACL
1133 if (acl)
1134 acl_free(acl);
1135 #endif
1136 return gpg_error_from_errno(len);
1139 #ifdef WITH_LIBACL
1140 else {
1141 acl = acl_get_file(".", ACL_TYPE_DEFAULT);
1143 if (!acl)
1144 log_write("ACL: %s: %s", filename, strerror(errno));
1146 #endif
1148 if (rename(tmp, filename) == -1) {
1149 len = errno;
1150 pth_cleanup_pop(1);
1151 #ifdef WITH_LIBACL
1152 if (acl)
1153 acl_free(acl);
1154 #endif
1155 return gpg_error_from_errno(len);
1158 pth_cleanup_pop(0);
1160 if (mode)
1161 chmod(filename, mode);
1163 #ifdef WITH_LIBACL
1164 if (acl && acl_set_file(filename, ACL_TYPE_ACCESS, acl))
1165 log_write("ACL: %s: %s", filename, strerror(errno));
1167 if (acl)
1168 acl_free(acl);
1169 #endif
1172 if (client && lstat(filename, &st) == 0)
1173 client->mtime = st.st_mtime;
1175 return 0;
1178 gpg_error_t update_save_flags(const gchar *filename,
1179 struct client_crypto_s *crypto)
1181 gpg_error_t rc;
1182 guint64 iter;
1184 /* New file? */
1185 if (!crypto->fh) {
1186 crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1188 if (!crypto->fh)
1189 return GPG_ERR_ENOMEM;
1192 rc = init_client_crypto2(filename, crypto);
1194 if (rc)
1195 return rc;
1197 if (filename && !crypto->fh->v1) {
1198 iter = (guint64)get_key_file_double(filename, "iterations");
1199 crypto->fh->ver.fh2.iter = iter < 0L ? 0UL : iter;
1202 return 0;
1205 static gpg_error_t save_command_finalize(assuan_context_t ctx, guchar *key,
1206 gboolean cached)
1208 struct client_s *client = assuan_get_pointer(ctx);
1209 gpointer xmlbuf;
1210 gulong outsize = 0;
1211 guint len;
1212 gint clevel;
1213 gint timeout;
1214 gpointer outbuf;
1215 gint zrc;
1216 gpg_error_t rc;
1218 if (client->crypto->key && client->crypto->key != key)
1219 gcry_free(client->crypto->key);
1221 client->crypto->key = key;
1222 rc = update_timestamp(client->doc);
1224 if (rc)
1225 return send_error(ctx, rc);
1227 xmlDocDumpFormatMemory(client->doc, (xmlChar **)&xmlbuf, (gint *)&len, 0);
1228 pth_cleanup_push(xmlFree, xmlbuf);
1229 clevel = get_key_file_integer(client->filename, "compression_level");
1231 if (clevel < 0)
1232 clevel = 0;
1234 if (do_compress(ctx, clevel, xmlbuf, len, &outbuf, &outsize, &zrc) == FALSE) {
1235 pth_cleanup_pop(1);
1236 cleanup_crypto(&client->crypto);
1238 if (zrc == Z_MEM_ERROR)
1239 return send_syserror(ctx, ENOMEM);
1240 else
1241 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1243 else {
1244 pth_cleanup_pop(1);
1245 xmlbuf = outbuf;
1246 len = outsize;
1249 client->crypto->inbuf = xmlbuf;
1250 client->crypto->insize = len;
1251 rc = update_save_flags(client->filename, client->crypto);
1253 if (rc) {
1254 cleanup_crypto(&client->crypto);
1255 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(rc));
1256 return send_error(ctx, rc);
1259 rc = do_xml_encrypt(client, client->crypto, client->filename);
1261 if (rc) {
1262 cleanup_crypto(&client->crypto);
1263 return send_error(ctx, rc);
1266 timeout = get_key_file_integer(client->filename, "cache_timeout");
1267 CACHE_LOCK(client->ctx);
1269 if (cached) {
1270 cache_reset_timeout(client->md5file, timeout);
1271 CACHE_UNLOCK;
1273 if (client->new == TRUE)
1274 send_status_all(STATUS_CACHE);
1276 client->new = FALSE;
1277 cleanup_crypto(&client->crypto);
1278 return send_error(ctx, 0);
1281 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1282 CACHE_UNLOCK;
1283 cleanup_crypto(&client->crypto);
1284 return send_syserror(ctx, ENOMEM);
1287 client->new = FALSE;
1288 cache_reset_timeout(client->md5file, timeout);
1289 CACHE_UNLOCK;
1290 send_status_all(STATUS_CACHE);
1291 cleanup_crypto(&client->crypto);
1292 return send_error(ctx, 0);
1295 static gpg_error_t save_command(assuan_context_t ctx, gchar *line)
1297 gboolean cached = FALSE;
1298 struct stat st;
1299 struct client_s *client = assuan_get_pointer(ctx);
1300 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
1301 gpg_error_t rc;
1303 rc = command_startup(ctx);
1305 if (rc)
1306 return send_error(ctx, rc);
1308 if (line && *line)
1309 log_write2("ARGS=%s", "<passphrase>");
1311 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1312 return send_syserror(ctx, errno);
1314 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1315 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1316 return send_error(ctx, GPG_ERR_ENOANO);
1319 CACHE_LOCK(ctx);
1320 cached = cache_iscached(client->md5file);
1321 CACHE_UNLOCK;
1324 * If a cache entry doesn't exist for this file and the file has a
1325 * "key_file" or "key" parameter, then it's an error. The reason is that
1326 * cache expiration would be useless.
1328 if (cached == FALSE) {
1329 gchar *tmp = get_key_file_string(client->filename, "key_file");
1331 if (tmp) {
1332 g_free(tmp);
1333 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1337 cached = FALSE;
1339 /* New file? */
1340 if (!client->crypto) {
1341 client->crypto = init_client_crypto();
1343 if (!client->crypto) {
1344 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1345 return send_syserror(ctx, ENOMEM);
1349 client->crypto->key = gcry_malloc(hashlen);
1351 if (!client->crypto->key) {
1352 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1353 cleanup_crypto(&client->crypto);
1354 return send_syserror(ctx, ENOMEM);
1357 memset(client->crypto->key, '!', hashlen);
1359 if (get_key_file_double(client->filename, "iterations") <= 0L)
1360 goto done;
1362 if (!line || !*line) {
1363 client->crypto->tkey = gcry_malloc(hashlen);
1365 if (!client->crypto->tkey) {
1366 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1367 cleanup_crypto(&client->crypto);
1368 return send_syserror(ctx, ENOMEM);
1371 memset(client->crypto->tkey, '!', hashlen);
1372 CACHE_LOCK(ctx);
1374 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1375 !memcmp(client->crypto->key, client->crypto->tkey, hashlen)) {
1376 CACHE_UNLOCK;
1378 #ifdef WITH_PINENTRY
1379 gpg_error_t rc;
1381 if (client->pinentry->enable == FALSE ||
1382 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1383 /* Empty keys are allowed. */
1384 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1385 goto done;
1388 lock_pin_mutex(client);
1389 client->pinentry->which = PINENTRY_SAVE;
1390 rc = pinentry_fork(ctx);
1392 if (rc) {
1393 unlock_pin_mutex(client->pinentry);
1394 return send_error(ctx, rc);
1397 client->pinentry->cb = save_command_finalize;
1398 client->pinentry->status = PINENTRY_INIT;
1399 return 0;
1400 #else
1401 /* Empty keys are allowed. */
1402 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1403 goto done;
1404 #endif
1406 else {
1407 CACHE_UNLOCK;
1408 cached = TRUE;
1411 else
1412 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, line,
1413 strlen(line));
1415 done:
1416 return save_command_finalize(ctx, client->crypto->key, cached);
1419 static gpg_error_t delete_command(assuan_context_t ctx, gchar *line)
1421 struct client_s *client = assuan_get_pointer(ctx);
1422 gchar **req;
1423 gpg_error_t rc;
1424 xmlNodePtr n;
1426 rc = command_startup(ctx);
1428 if (rc)
1429 return send_error(ctx, rc);
1431 log_write2("ARGS=\"%s\"", line);
1433 if (strchr(line, '\t'))
1434 req = split_input_line(line, "\t", -1);
1435 else
1436 req = split_input_line(line, " ", -1);
1438 if (!req || !*req)
1439 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1441 n = find_account(client->doc, &req, &rc, NULL, 0, FALSE);
1443 if (!n) {
1444 g_strfreev(req);
1445 return send_error(ctx, rc);
1449 * No sub-node defined. Remove the entire node (account).
1451 if (!req[1]) {
1452 if (n) {
1453 xmlUnlinkNode(n);
1454 xmlFreeNode(n);
1457 g_strfreev(req);
1458 return send_error(ctx, 0);
1461 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1462 g_strfreev(req);
1464 if (!n)
1465 return send_error(ctx, rc);
1467 if (n) {
1468 xmlUnlinkNode(n);
1469 xmlFreeNode(n);
1472 return send_error(ctx, 0);
1476 * Don't return with assuan_process_done() here. This has been called from
1477 * assuan_process_next() and the command should be finished in
1478 * client_thread().
1480 static gpg_error_t store_command_finalize(gpointer data, gpg_error_t assuan_rc,
1481 guchar *line, gsize len)
1483 assuan_context_t ctx = data;
1484 struct client_s *client = assuan_get_pointer(ctx);
1485 gchar **req;
1486 xmlNodePtr n;
1487 gpg_error_t rc = file_modified(client);
1489 if (assuan_rc || rc) {
1490 if (line)
1491 xfree(line);
1492 return assuan_rc ? assuan_rc : rc;
1495 req = split_input_line((gchar *)line, "\t", 0);
1496 xfree(line);
1498 if (!req || !*req)
1499 return EPWMD_COMMAND_SYNTAX;
1501 again:
1502 n = find_account(client->doc, &req, &rc, NULL, 0, FALSE);
1504 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1505 rc = new_account(client->doc, *req);
1507 if (rc) {
1508 g_strfreev(req);
1509 return rc;
1512 goto again;
1515 if (!n) {
1516 g_strfreev(req);
1517 return rc;
1520 if (req[1]) {
1521 if (!n->children)
1522 create_elements_cb(n, req+1, &rc, NULL);
1523 else
1524 find_elements(client->doc, n->children, req+1, &rc,
1525 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1528 g_strfreev(req);
1529 client->inquire_status = INQUIRE_DONE;
1530 return rc;
1533 static gpg_error_t store_command(assuan_context_t ctx, gchar *line)
1535 struct client_s *client = assuan_get_pointer(ctx);
1536 gpg_error_t rc;
1538 rc = command_startup(ctx);
1540 if (rc)
1541 return send_error(ctx, rc);
1543 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1545 if (rc)
1546 return send_error(ctx, rc);
1548 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1549 client->inquire_status = INQUIRE_BUSY;
1550 return 0;
1553 static void *send_data_cb(void *arg)
1555 struct assuan_cmd_s *data = arg;
1556 gint old;
1557 gpg_error_t rc;
1559 pth_cancel_state(PTH_CANCEL_ENABLE|PTH_CANCEL_ASYNCHRONOUS, &old);
1560 rc = assuan_send_data(data->ctx, data->line, data->line_len);
1561 pth_cancel_state(old, NULL);
1562 pth_exit((void *)rc);
1563 return NULL;
1566 /* For every assuan command that needs to be sent to the client, a timeout is
1567 * needed to determine if the client lost the connection. The timeout is the
1568 * same as the "keepalive" configuration parameter or a default if unset.
1570 gpg_error_t do_assuan_command(assuan_context_t ctx,
1571 void *(*cb)(void *data), void *data)
1573 pth_attr_t attr = pth_attr_new();
1574 pth_t tid;
1575 gint n;
1576 gint to = get_key_file_integer("global", "keepalive");
1577 pth_event_t ev, tev;
1578 pth_status_t st;
1579 gpg_error_t rc;
1581 pth_attr_init(attr);
1582 pth_attr_set(attr, PTH_ATTR_JOINABLE, TRUE);
1583 tid = pth_spawn(attr, cb, data);
1584 n = errno;
1585 pth_attr_destroy(attr);
1587 if (!tid) {
1588 log_write("%s(%i): pth_spawn(): %s", __FILE__, __LINE__,
1589 _gpg_strerror(gpg_error_from_errno(n)));
1590 return gpg_error_from_errno(n);
1593 pth_cleanup_push(cleanup_cancel_cb, tid);
1594 to = to <= 0 ? DEFAULT_KEEPALIVE_TO : to;
1595 ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD, tid);
1596 tev = pth_event(PTH_EVENT_TIME, pth_timeout(to, 0));
1597 ev = pth_event_concat(ev, tev, NULL);
1598 pth_cleanup_push(cleanup_ev_cb, ev);
1599 pth_yield(tid);
1600 pth_wait(ev);
1601 st = pth_event_status(ev);
1603 if (st == PTH_STATUS_FAILED) {
1604 pth_cancel(tid);
1605 rc = GPG_ERR_ASS_WRITE_ERROR;
1607 else if (st == PTH_STATUS_OCCURRED)
1608 pth_join(tid, (void **)&rc);
1609 else {
1610 st = pth_event_status(tev);
1612 if (st == PTH_STATUS_OCCURRED) {
1613 pth_cancel(tid);
1614 rc = GPG_ERR_ASS_WRITE_ERROR;
1618 pth_cleanup_pop(1);
1619 pth_cleanup_pop(0);
1620 return rc;
1623 static gpg_error_t xfer_data(assuan_context_t ctx, const gchar *line,
1624 gint total)
1626 gint to_send;
1627 gint sent = 0;
1628 gpg_error_t rc;
1629 struct assuan_cmd_s data;
1630 gint progress = get_key_file_integer("global", "xfer_progress");
1631 gint flush = 0;
1633 progress = progress>0 ? (progress/ASSUAN_LINELENGTH)*ASSUAN_LINELENGTH : 0;
1634 to_send = total < ASSUAN_LINELENGTH ? total : ASSUAN_LINELENGTH;
1635 data.ctx = ctx;
1636 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1638 if (rc)
1639 return rc;
1641 again:
1642 do {
1643 if (sent + to_send > total)
1644 to_send = total - sent;
1646 data.line = flush ? NULL : (gchar *)line+sent;
1647 data.line_len = flush ? 0 : to_send;
1648 rc = do_assuan_command(ctx, send_data_cb, &data);
1650 if (!rc) {
1651 sent += flush ? 0 : to_send;
1653 if ((progress && !(sent % progress) && sent != total) ||
1654 (sent == total && flush))
1655 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1657 if (!flush && !rc && sent == total) {
1658 flush = 1;
1659 goto again;
1662 } while (!rc && sent < total);
1664 return rc;
1667 static gpg_error_t get_command(assuan_context_t ctx, gchar *line)
1669 struct client_s *client = assuan_get_pointer(ctx);
1670 gchar **req;
1671 gpg_error_t rc;
1672 xmlNodePtr n;
1674 rc = command_startup(ctx);
1676 if (rc)
1677 return send_error(ctx, rc);
1679 log_write2("ARGS=\"%s\"", line);
1680 req = split_input_line(line, "\t", -1);
1682 if (!req || !*req) {
1683 g_strfreev(req);
1684 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1687 n = find_account(client->doc, &req, &rc, NULL, 0, FALSE);
1689 if (!n) {
1690 g_strfreev(req);
1691 return send_error(ctx, rc);
1694 if (req[1])
1695 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1697 g_strfreev(req);
1699 if (rc)
1700 return send_error(ctx, rc);
1702 if (!n || !n->children)
1703 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1705 n = find_text_node(n->children);
1707 if (!n || !n->content || !*n->content)
1708 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1710 rc = xfer_data(ctx, (gchar *)n->content, xmlStrlen(n->content));
1711 return send_error(ctx, rc);
1714 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1715 gpg_error_t *rc, gchar **req_orig, void *data)
1717 gchar *path = *(gchar **)data;
1718 gchar *tmp = NULL, *result;
1720 if (path) {
1721 g_free(path);
1722 *(gchar **)data = NULL;
1725 path = g_strjoinv("\t", target);
1727 if (!path) {
1728 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1729 *rc = gpg_error_from_errno(ENOMEM);
1730 return NULL;
1733 if (req_orig) {
1734 tmp = g_strjoinv("\t", req_orig);
1736 if (!tmp) {
1737 g_free(path);
1738 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1739 *rc = gpg_error_from_errno(ENOMEM);
1740 return NULL;
1744 if (tmp && *tmp)
1745 result = g_strdup_printf("%s\t%s", path, tmp);
1746 else
1747 result = g_strdup(path);
1749 if (!result) {
1750 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1751 *rc = gpg_error_from_errno(ENOMEM);
1752 g_free(path);
1753 g_free(tmp);
1754 return NULL;
1757 g_free(path);
1758 g_free(tmp);
1759 *(gchar **)data = result;
1760 return node;
1763 static void list_command_cleanup1(void *arg);
1764 static gpg_error_t realpath_command(assuan_context_t ctx, gchar *line)
1766 gpg_error_t rc;
1767 struct client_s *client = assuan_get_pointer(ctx);
1768 gchar **req;
1769 gchar *t;
1770 gint i;
1771 xmlNodePtr n;
1772 GString *string;
1773 gchar *rp = NULL;
1775 rc = command_startup(ctx);
1777 if (rc)
1778 return send_error(ctx, rc);
1780 log_write2("ARGS=\"%s\"", line);
1782 if (strchr(line, '\t') != NULL) {
1783 if ((req = split_input_line(line, "\t", 0)) == NULL)
1784 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1786 else {
1787 if ((req = split_input_line(line, " ", 0)) == NULL)
1788 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1791 n = find_account(client->doc, &req, &rc, NULL, 0, FALSE);
1793 if (!n) {
1794 g_strfreev(req);
1795 return send_error(ctx, rc);
1798 rp = g_strjoinv("\t", req);
1800 if (!rp) {
1801 g_strfreev(req);
1802 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1803 return send_syserror(ctx, ENOMEM);
1806 if (req[1]) {
1807 n = find_elements(client->doc, n->children, req+1, &rc,
1808 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1810 if (!n) {
1811 g_free(rp);
1812 g_strfreev(req);
1813 return send_error(ctx, rc);
1817 string = g_string_new(rp);
1818 g_free(rp);
1819 g_strfreev(req);
1821 if (!string) {
1822 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1823 return send_syserror(ctx, ENOMEM);
1826 again:
1827 for (i = 0, t = string->str + i; *t; t++, i++) {
1828 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1829 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1830 goto again;
1834 pth_cleanup_push(list_command_cleanup1, string);
1835 rc = xfer_data(ctx, string->str, string->len);
1836 pth_cleanup_pop(1);
1837 return send_error(ctx, rc);
1840 static void list_command_cleanup1(void *arg)
1842 g_string_free((GString *)arg, TRUE);
1845 static void list_command_cleanup2(void *arg)
1847 struct element_list_s *elements = arg;
1849 if (elements) {
1850 gint total = g_slist_length(elements->list);
1851 gint i;
1853 for (i = 0; i < total; i++) {
1854 gchar *tmp = g_slist_nth_data(elements->list, i);
1855 g_free(tmp);
1858 g_slist_free(elements->list);
1860 if (elements->prefix)
1861 g_free(elements->prefix);
1863 g_free(elements);
1867 static gpg_error_t list_command(assuan_context_t ctx, gchar *line)
1869 struct client_s *client = assuan_get_pointer(ctx);
1870 gpg_error_t rc;
1871 struct element_list_s *elements = NULL;
1872 gchar *tmp;
1874 rc = command_startup(ctx);
1876 if (rc)
1877 return send_error(ctx, rc);
1879 if (disable_list_and_dump == TRUE)
1880 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1882 log_write2("ARGS=\"%s\"", line);
1884 if (!*line) {
1885 GString *str;
1887 rc = list_accounts(client->doc, &str);
1889 if (rc)
1890 return send_error(ctx, rc);
1892 pth_cleanup_push(list_command_cleanup1, str);
1893 rc = xfer_data(ctx, str->str, str->len);
1894 pth_cleanup_pop(1);
1895 return send_error(ctx, rc);
1898 elements = g_malloc0(sizeof(struct element_list_s));
1900 if (!elements) {
1901 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1902 rc = gpg_err_code_from_errno(ENOMEM);
1903 goto fail;
1906 pth_cleanup_push(list_command_cleanup2, elements);
1907 rc = create_path_list(client->doc, elements, line);
1909 if (rc)
1910 goto fail;
1912 if (elements) {
1913 gint total = g_slist_length(elements->list);
1914 gint i;
1915 GString *str;
1917 if (!total) {
1918 rc = EPWMD_EMPTY_ELEMENT;
1919 goto fail;
1922 str = g_string_new(NULL);
1924 if (!str) {
1925 rc = gpg_err_code_from_errno(ENOMEM);
1926 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1927 goto fail;
1930 for (i = 0; i < total; i++) {
1931 tmp = g_slist_nth_data(elements->list, i);
1932 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1935 pth_cleanup_push(list_command_cleanup1, str);
1936 rc = xfer_data(ctx, str->str, str->len);
1937 pth_cleanup_pop(1);
1939 else
1940 rc = EPWMD_EMPTY_ELEMENT;
1942 fail:
1943 pth_cleanup_pop(1);
1944 return send_error(ctx, rc);
1948 * req[0] - element path
1950 static gint attribute_list(assuan_context_t ctx, gchar **req)
1952 struct client_s *client = assuan_get_pointer(ctx);
1953 gchar **attrlist = NULL;
1954 gint i = 0;
1955 gchar **path = NULL;
1956 xmlAttrPtr a;
1957 xmlNodePtr n, an;
1958 gchar *line;
1959 gpg_error_t rc;
1961 if (!req || !req[0])
1962 return EPWMD_COMMAND_SYNTAX;
1964 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1966 * The first argument may be only an account.
1968 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1969 return EPWMD_COMMAND_SYNTAX;
1972 n = find_account(client->doc, &path, &rc, NULL, 0, FALSE);
1974 if (!n) {
1975 g_strfreev(path);
1976 return rc;
1979 if (path[1]) {
1980 n = find_elements(client->doc, n->children, path+1, &rc,
1981 NULL, NULL, NULL, FALSE, 0, NULL);
1983 if (!n) {
1984 g_strfreev(path);
1985 return rc;
1989 g_strfreev(path);
1991 for (a = n->properties; a; a = a->next) {
1992 gchar **pa;
1994 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1995 if (attrlist)
1996 g_strfreev(attrlist);
1998 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1999 return gpg_error_from_errno(ENOMEM);
2002 attrlist = pa;
2003 an = a->children;
2004 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
2006 if (!attrlist[i]) {
2007 g_strfreev(attrlist);
2008 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2009 return gpg_error_from_errno(ENOMEM);
2012 attrlist[++i] = NULL;
2015 if (!attrlist)
2016 return EPWMD_EMPTY_ELEMENT;
2018 line = g_strjoinv("\n", attrlist);
2020 if (!line) {
2021 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2022 g_strfreev(attrlist);
2023 return gpg_error_from_errno(ENOMEM);
2026 pth_cleanup_push(g_free, line);
2027 pth_cleanup_push(req_cleanup, attrlist);
2028 rc = xfer_data(ctx, line, strlen(line));
2029 pth_cleanup_pop(1);
2030 pth_cleanup_pop(1);
2031 return rc;
2035 * req[0] - attribute
2036 * req[1] - element path
2038 static gint attribute_delete(struct client_s *client, gchar **req)
2040 xmlAttrPtr a;
2041 xmlNodePtr n;
2042 gchar **path = NULL;
2043 gpg_error_t rc;
2045 if (!req || !req[0] || !req[1])
2046 return EPWMD_COMMAND_SYNTAX;
2048 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2050 * The first argument may be only an account.
2052 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2053 return EPWMD_COMMAND_SYNTAX;
2057 * Don't remove the "name" attribute for the account element. To remove an
2058 * account use DELETE <account>.
2060 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
2061 rc = EPWMD_ATTR_SYNTAX;
2062 goto fail;
2065 n = find_account(client->doc, &path, &rc, NULL, 0, FALSE);
2067 if (!n)
2068 goto fail;
2070 if (path[1]) {
2071 n = find_elements(client->doc, n->children, path+1, &rc,
2072 NULL, NULL, NULL, FALSE, 0, NULL);
2074 if (!n)
2075 goto fail;
2078 g_strfreev(path);
2080 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
2081 return EPWMD_ATTR_NOT_FOUND;
2083 if (xmlRemoveProp(a) == -1)
2084 return EPWMD_LIBXML_ERROR;
2086 return 0;
2088 fail:
2089 g_strfreev(path);
2090 return rc;
2093 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
2094 gpg_error_t *rc)
2096 gchar **src = *path;
2097 gchar **src_orig = g_strdupv(src);
2098 xmlNodePtr n = NULL;
2100 *rc = 0;
2102 if (!src_orig) {
2103 *rc = gpg_error_from_errno(ENOMEM);
2104 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2105 goto fail;
2108 again:
2109 n = find_account(client->doc, &src, rc, NULL, 0, FALSE);
2111 if (!n) {
2112 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
2113 *rc = new_account(client->doc, src[0]);
2115 if (*rc)
2116 goto fail;
2118 goto again;
2120 else
2121 goto fail;
2124 if (src[1]) {
2125 if (!n->children)
2126 n = create_target_elements_cb(n, src+1, rc, NULL);
2127 else
2128 n = find_elements(client->doc, n->children, src+1, rc,
2129 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
2131 if (!n)
2132 goto fail;
2135 * Reset the position of the element tree now that the elements
2136 * have been created.
2138 g_strfreev(src);
2139 src = src_orig;
2140 src_orig = NULL;
2141 n = find_account(client->doc, &src, rc, NULL, 0, FALSE);
2143 if (!n)
2144 goto fail;
2146 n = find_elements(client->doc, n->children, src+1, rc,
2147 NULL, NULL, NULL, FALSE, 0, NULL);
2149 if (!n)
2150 goto fail;
2153 fail:
2154 if (src_orig)
2155 g_strfreev(src_orig);
2157 *path = src;
2158 return n;
2162 * Creates a "target" attribute. When other commands encounter an element with
2163 * this attribute, the element path is modified to the target value. If the
2164 * source element path doesn't exist when using 'ATTR SET target', it is
2165 * created, but the destination element path must exist.
2167 * req[0] - source element path
2168 * req[1] - destination element path
2170 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2172 gchar **src, **dst, *line = NULL, **odst = NULL;
2173 gpg_error_t rc;
2174 xmlNodePtr n;
2176 if (!req || !req[0] || !req[1])
2177 return EPWMD_COMMAND_SYNTAX;
2179 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2181 * The first argument may be only an account.
2183 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2184 return EPWMD_COMMAND_SYNTAX;
2187 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2189 * The first argument may be only an account.
2191 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2192 rc = EPWMD_COMMAND_SYNTAX;
2193 goto fail;
2197 odst = g_strdupv(dst);
2199 if (!odst) {
2200 rc = gpg_error_from_errno(ENOMEM);
2201 goto fail;
2204 n = find_account(client->doc, &dst, &rc, NULL, 0, FALSE);
2207 * Make sure the destination element path exists.
2209 if (!n)
2210 goto fail;
2212 if (dst[1]) {
2213 n = find_elements(client->doc, n->children, dst+1, &rc,
2214 NULL, NULL, NULL, FALSE, 0, NULL);
2216 if (!n)
2217 goto fail;
2220 n = create_element_path(client, &src, &rc);
2222 if (rc)
2223 goto fail;
2225 line = g_strjoinv("\t", odst);
2227 if (!line) {
2228 rc = gpg_error_from_errno(ENOMEM);
2229 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2230 goto fail;
2233 rc = add_attribute(n, "target", line);
2235 fail:
2236 g_free(line);
2237 g_strfreev(src);
2238 g_strfreev(dst);
2239 g_strfreev(odst);
2240 return rc;
2244 * req[0] - account name
2245 * req[1] - new name
2247 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2249 gpg_error_t rc;
2250 gchar **tmp;
2251 xmlNodePtr n;
2253 tmp = g_strdupv(req);
2255 if (!tmp) {
2256 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2257 return gpg_error_from_errno(ENOMEM);
2260 n = find_account(client->doc, &tmp, &rc, NULL, 0, FALSE);
2261 g_strfreev(tmp);
2263 if (!n)
2264 return rc;
2266 if (g_utf8_collate(req[0], req[1]) == 0)
2267 return 0;
2270 * Will not overwrite an existing account.
2272 tmp = g_strdupv(req+1);
2274 if (!tmp) {
2275 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2276 return gpg_error_from_errno(ENOMEM);
2279 n = find_account(client->doc, &tmp, &rc, NULL, 0, FALSE);
2280 g_strfreev(tmp);
2282 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
2283 return rc;
2285 if (n)
2286 return EPWMD_ACCOUNT_EXISTS;
2289 * Whitespace not allowed in account names.
2291 if (contains_whitespace(req[1]) == TRUE)
2292 return EPWMD_ATTR_SYNTAX;
2294 tmp = g_strdupv(req);
2296 if (!tmp) {
2297 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2298 return gpg_error_from_errno(ENOMEM);
2301 n = find_account(client->doc, &tmp, &rc, NULL, 0, FALSE);
2302 g_strfreev(tmp);
2304 if (!n)
2305 return EPWMD_ELEMENT_NOT_FOUND;
2307 return add_attribute(n, "name", req[1]);
2311 * req[0] - attribute
2312 * req[1] - element path
2314 static gint attribute_get(assuan_context_t ctx, gchar **req)
2316 struct client_s *client = assuan_get_pointer(ctx);
2317 xmlNodePtr n;
2318 xmlChar *a;
2319 gchar **path= NULL;
2320 gpg_error_t rc;
2322 if (!req || !req[0] || !req[1])
2323 return EPWMD_COMMAND_SYNTAX;
2325 if (strchr(req[1], '\t')) {
2326 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2327 return EPWMD_COMMAND_SYNTAX;
2329 else {
2330 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2331 return EPWMD_COMMAND_SYNTAX;
2334 n = find_account(client->doc, &path, &rc, NULL, 0, FALSE);
2336 if (!n)
2337 goto fail;
2339 if (path[1]) {
2340 n = find_elements(client->doc, n->children, path+1, &rc,
2341 NULL, NULL, NULL, FALSE, 0, NULL);
2343 if (!n)
2344 goto fail;
2347 g_strfreev(path);
2349 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2350 return EPWMD_ATTR_NOT_FOUND;
2352 pth_cleanup_push(xmlFree, a);
2353 rc = xfer_data(ctx, (gchar *)a, xmlStrlen(a));
2354 pth_cleanup_pop(1);
2355 return rc;
2357 fail:
2358 g_strfreev(path);
2359 return rc;
2363 * req[0] - attribute
2364 * req[1] - element path
2365 * req[2] - value
2367 static gint attribute_set(struct client_s *client, gchar **req)
2369 gchar **path = NULL;
2370 gpg_error_t rc;
2371 xmlNodePtr n;
2373 if (!req || !req[0] || !req[1] || !req[2])
2374 return EPWMD_COMMAND_SYNTAX;
2377 * Reserved attribute names.
2379 if (g_utf8_collate(req[0], "name") == 0) {
2381 * Only reserved for the account element. Not the rest of the
2382 * document.
2384 if (strchr(req[1], '\t') == NULL)
2385 return name_attribute(client, req + 1);
2387 else if (g_utf8_collate(req[0], "target") == 0)
2388 return target_attribute(client, req + 1);
2390 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2392 * The first argument may be only an account.
2394 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2395 return EPWMD_COMMAND_SYNTAX;
2398 n = find_account(client->doc, &path, &rc, NULL, 0, FALSE);
2400 if (!n)
2401 goto fail;
2403 if (path[1]) {
2404 n = find_elements(client->doc, n->children, path+1, &rc,
2405 NULL, NULL, NULL, FALSE, 0, NULL);
2407 if (!n)
2408 goto fail;
2411 g_strfreev(path);
2412 return add_attribute(n, req[0], req[2]);
2414 fail:
2415 g_strfreev(path);
2416 return rc;
2420 * req[0] - command
2421 * req[1] - attribute name or element path if command is LIST
2422 * req[2] - element path
2423 * req[2] - element path or value
2425 static gpg_error_t attr_command(assuan_context_t ctx, gchar *line)
2427 struct client_s *client = assuan_get_pointer(ctx);
2428 gchar **req;
2429 gpg_error_t rc = 0;
2431 rc = command_startup(ctx);
2433 if (rc)
2434 return send_error(ctx, rc);
2436 log_write2("ARGS=\"%s\"", line);
2437 req = split_input_line(line, " ", 4);
2439 if (!req || !req[0] || !req[1]) {
2440 g_strfreev(req);
2441 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2444 pth_cleanup_push(req_cleanup, req);
2446 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2447 rc = attribute_set(client, req+1);
2448 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2449 rc = attribute_get(ctx, req+1);
2450 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2451 rc = attribute_delete(client, req+1);
2452 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2453 rc = attribute_list(ctx, req+1);
2454 else
2455 rc = EPWMD_COMMAND_SYNTAX;
2457 pth_cleanup_pop(1);
2458 return send_error(ctx, rc);
2461 static gpg_error_t iscached_command(assuan_context_t ctx, gchar *line)
2463 gpg_error_t rc = command_startup(ctx);
2465 if (rc)
2466 return send_error(ctx, rc);
2468 gchar **req = split_input_line(line, " ", 0);
2469 guchar md5file[16];
2470 gchar *path, *tmp;
2472 if (!req || !*req) {
2473 g_strfreev(req);
2474 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2477 log_write2("ARGS=\"%s\"", line);
2479 if (!valid_filename(req[0])) {
2480 g_strfreev(req);
2481 return EPWMD_INVALID_FILENAME;
2484 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2485 CACHE_LOCK(ctx);
2487 if (cache_iscached(md5file)) {
2488 g_strfreev(req);
2489 CACHE_UNLOCK;
2490 return send_error(ctx, 0);
2493 CACHE_UNLOCK;
2494 tmp = get_key_file_string("global", "data_directory");
2496 if (!tmp) {
2497 g_strfreev(req);
2498 return gpg_error_from_errno(ENOMEM);
2501 path = expand_homedir(tmp);
2503 if (!path) {
2504 g_strfreev(req);
2505 g_free(tmp);
2506 return gpg_error_from_errno(ENOMEM);
2509 g_free(tmp);
2510 tmp = path;
2511 path = g_strdup_printf("%s/%s", tmp, req[0]);
2512 g_free(tmp);
2514 if (!path) {
2515 g_strfreev(req);
2516 return gpg_error_from_errno(ENOMEM);
2519 if (access(path, R_OK) == -1) {
2520 gpg_error_t rc = gpg_error_from_syserror();
2522 g_free(path);
2523 g_strfreev(req);
2524 return send_error(ctx, rc);
2527 g_free(path);
2528 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2531 static gpg_error_t clearcache_command(assuan_context_t ctx, gchar *line)
2533 gpg_error_t rc = command_startup(ctx);
2535 if (rc)
2536 return send_error(ctx, rc);
2538 struct client_s *client = assuan_get_pointer(ctx);
2539 gchar **req = split_input_line(line, " ", 0);
2540 guchar md5file[16];
2542 log_write2("ARGS=\"%s\"", line);
2543 CACHE_LOCK(ctx);
2545 if (!req || !*req) {
2546 g_strfreev(req);
2547 cache_clear(client->md5file, 2);
2548 CACHE_UNLOCK;
2549 return send_error(ctx, 0);
2552 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2553 g_strfreev(req);
2555 if (cache_clear(md5file, 1) == FALSE) {
2556 CACHE_UNLOCK;
2557 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2560 CACHE_UNLOCK;
2561 return send_error(ctx, 0);
2564 static gpg_error_t cachetimeout_command(assuan_context_t ctx, gchar *line)
2566 gpg_error_t rc = command_startup(ctx);
2568 if (rc)
2569 return send_error(ctx, rc);
2571 guchar md5file[16];
2572 glong timeout;
2573 gchar **req = split_input_line(line, " ", 0);
2574 gchar *p;
2576 if (!req || !*req || !req[1]) {
2577 g_strfreev(req);
2578 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2581 errno = 0;
2582 timeout = strtol(req[1], &p, 10);
2584 if (errno != 0 || *p != 0 || timeout < -1) {
2585 g_strfreev(req);
2586 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2589 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2590 CACHE_LOCK(client->ctx);
2592 if (cache_set_timeout(md5file, timeout) == FALSE) {
2593 CACHE_UNLOCK;
2594 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2597 CACHE_UNLOCK;
2598 return send_error(ctx, 0);
2601 static gpg_error_t dump_command(assuan_context_t ctx, gchar *line)
2603 xmlChar *xml;
2604 gint len;
2605 struct client_s *client = assuan_get_pointer(ctx);
2606 gpg_error_t rc;
2608 rc = command_startup(ctx);
2610 if (rc)
2611 return send_error(ctx, rc);
2613 if (disable_list_and_dump == TRUE)
2614 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2616 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2618 if (!xml) {
2619 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2620 return send_syserror(ctx, ENOMEM);
2623 pth_cleanup_push(xmlFree, xml);
2624 rc = xfer_data(ctx, (gchar *)xml, len);
2625 pth_cleanup_pop(1);
2626 return send_error(ctx, rc);
2629 static gpg_error_t getconfig_command(assuan_context_t ctx, gchar *line)
2631 struct client_s *client = assuan_get_pointer(ctx);
2632 gpg_error_t rc = 0;
2633 gchar filename[255]={0}, param[747]={0};
2634 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2636 rc = command_startup(ctx);
2638 if (rc)
2639 return send_error(ctx, rc);
2641 log_write2("ARGS=\"%s\"", line);
2643 if (strchr(line, ' ')) {
2644 sscanf(line, " %254[^ ] %746c", filename, param);
2645 paramp = param;
2646 fp = filename;
2649 if (fp && !valid_filename(fp))
2650 return send_error(ctx, EPWMD_INVALID_FILENAME);
2652 paramp = g_ascii_strdown(paramp, -1);
2654 if (!paramp) {
2655 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2656 return send_syserror(ctx, ENOMEM);
2659 if (fp && !g_ascii_strcasecmp(paramp, "iterations")) {
2660 if (!(client->opts & OPT_ITERATIONS) || fp != client->filename) {
2661 file_header_internal_t *fh = read_file_header(fp, FALSE, &rc);
2663 if (!fh && rc != GPG_ERR_ENOENT)
2664 return send_error(ctx, rc);
2666 if (!rc) {
2667 g_free(paramp);
2668 p = g_strdup_printf("%lu", (unsigned long)fh->ver.fh2.iter);
2669 close_file_header(fh);
2671 if (!p) {
2672 log_write("%s(%i): %s", __FILE__, __LINE__,
2673 strerror(ENOMEM));
2674 return send_syserror(ctx, ENOMEM);
2677 goto done;
2681 else if (!g_ascii_strcasecmp(paramp, "enable_pinentry")) {
2682 #ifdef WITH_PINENTRY
2683 gboolean n;
2685 if (fp == client->filename && (client->opts & OPT_PINENTRY))
2686 n = client->pinentry->enable;
2687 else
2688 n = get_key_file_boolean(fp, "enable_pinentry");
2690 p = g_strdup_printf("%s", n ? "true" : "false");
2692 if (!p) {
2693 log_write("%s(%i): %s", __FILE__, __LINE__,
2694 strerror(ENOMEM));
2695 return send_syserror(ctx, ENOMEM);
2698 goto done;
2699 #else
2700 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2701 #endif
2703 else if (!g_ascii_strcasecmp(paramp, "pinentry_timeout")) {
2704 #ifdef WITH_PINENTRY
2705 if (fp == client->filename && (client->opts & OPT_PINENTRY_TO))
2706 p = g_strdup_printf("%i", client->pinentry->timeout);
2707 else
2708 p = g_strdup_printf("%i",
2709 get_key_file_integer(fp, "pinentry_timeout"));
2711 if (!p) {
2712 log_write("%s(%i): %s", __FILE__, __LINE__,
2713 strerror(ENOMEM));
2714 return send_syserror(ctx, ENOMEM);
2717 goto done;
2718 #else
2719 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2720 #endif
2723 p = get_key_file_string(fp ? fp : "global", paramp);
2724 g_free(paramp);
2726 if (!p)
2727 return send_error(ctx, GPG_ERR_NO_VALUE);
2729 tmp = expand_homedir(p);
2730 g_free(p);
2732 if (!tmp) {
2733 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2734 return send_syserror(ctx, ENOMEM);
2737 p = tmp;
2738 done:
2739 pth_cleanup_push(g_free, p);
2740 rc = xfer_data(ctx, p, strlen(p));
2741 pth_cleanup_pop(1);
2742 return send_error(ctx, rc);
2745 struct xpath_s {
2746 xmlXPathContextPtr xp;
2747 xmlXPathObjectPtr result;
2748 xmlBufferPtr buf;
2749 gchar **req;
2752 static void xpath_command_cleanup(void *arg)
2754 struct xpath_s *xpath = arg;
2756 req_cleanup(xpath->req);
2758 if (xpath->buf)
2759 xmlBufferFree(xpath->buf);
2761 if (xpath->result)
2762 xmlXPathFreeObject(xpath->result);
2764 if (xpath->xp)
2765 xmlXPathFreeContext(xpath->xp);
2768 static gpg_error_t xpath_command(assuan_context_t ctx, gchar *line)
2770 struct client_s *client = assuan_get_pointer(ctx);
2771 gpg_error_t rc;
2772 struct xpath_s xpath;
2773 rc = command_startup(ctx);
2775 if (rc)
2776 return send_error(ctx, rc);
2779 log_write2("ARGS=\"%s\"", line);
2781 if (disable_list_and_dump == TRUE)
2782 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2784 if (!line || !*line)
2785 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2787 memset(&xpath, 0, sizeof(struct xpath_s));
2789 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
2790 if (strv_printf(&xpath.req, "%s", line) == FALSE)
2791 return send_syserror(ctx, ENOMEM);
2794 xpath.xp = xmlXPathNewContext(client->doc);
2796 if (!xpath.xp) {
2797 xpath_command_cleanup(&xpath);
2798 return send_error(ctx, EPWMD_LIBXML_ERROR);
2801 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
2803 if (!xpath.result) {
2804 xpath_command_cleanup(&xpath);
2805 return send_error(ctx, EPWMD_LIBXML_ERROR);
2808 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
2809 rc = EPWMD_EMPTY_ELEMENT;
2810 goto fail;
2813 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
2814 (xmlChar *)xpath.req[1], &xpath.buf);
2816 if (rc)
2817 goto fail;
2818 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
2819 rc = EPWMD_EMPTY_ELEMENT;
2820 goto fail;
2822 else if (xpath.req[1])
2823 goto fail;
2825 pth_cleanup_push(xpath_command_cleanup, &xpath);
2826 rc = xfer_data(ctx, (gchar *)xmlBufferContent(xpath.buf),
2827 xmlBufferLength(xpath.buf));
2828 pth_cleanup_pop(0);
2830 fail:
2831 xpath_command_cleanup(&xpath);
2832 return send_error(ctx, rc);
2835 static gpg_error_t import_command_finalize(gpointer data,
2836 gpg_error_t assuan_rc, guchar *line, gsize len)
2838 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2839 gpg_error_t rc = file_modified(client);
2840 gchar **req, **path = NULL, **path_orig = NULL, *content;
2841 xmlDocPtr doc = NULL;
2842 xmlNodePtr n, root, copy;
2844 if (assuan_rc || rc) {
2845 if (line)
2846 xfree(line);
2847 return assuan_rc ? assuan_rc : rc;
2850 req = split_input_line((gchar *)line, "\t", 2);
2851 xfree(line);
2853 if (!req || !*req)
2854 return EPWMD_COMMAND_SYNTAX;
2856 content = req[0];
2857 path = split_input_line(req[1], "\t", 0);
2859 if (!content || !*content) {
2860 rc = EPWMD_COMMAND_SYNTAX;
2861 goto fail;
2864 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2866 if (!doc) {
2867 rc = EPWMD_LIBXML_ERROR;
2868 goto fail;
2871 root = xmlDocGetRootElement(doc);
2873 if (path) {
2874 path_orig = g_strdupv(path);
2876 if (!path_orig) {
2877 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
2878 rc = gpg_error_from_errno(ENOMEM);
2879 goto fail;
2882 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2883 g_strfreev(path_orig);
2884 rc = gpg_error_from_errno(ENOMEM);
2885 goto fail;
2888 n = find_account(client->doc, &path, &rc, NULL, 0, FALSE);
2890 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2891 g_strfreev(path_orig);
2892 goto fail;
2895 if (!rc) {
2896 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2898 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2899 g_strfreev(path_orig);
2900 goto fail;
2902 else if (!rc) {
2903 xmlNodePtr parent = n->parent;
2905 xmlUnlinkNode(n);
2906 xmlFreeNode(n);
2907 n = parent;
2911 g_strfreev(path);
2912 path = path_orig;
2914 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2915 n = create_element_path(client, &path, &rc);
2917 if (rc)
2918 goto fail;
2921 copy = xmlCopyNode(root, 1);
2922 n = xmlAddChild(n, copy);
2924 if (!n)
2925 rc = EPWMD_LIBXML_ERROR;
2927 else {
2928 /* Check if the content root element can create a DTD root element. */
2929 if (xmlStrcmp((xmlChar *)"root", root->name)) {
2930 rc = EPWMD_COMMAND_SYNTAX;
2931 goto fail;
2934 xmlChar *a;
2936 if ((a = xmlGetProp(root, (xmlChar *)"name")) == NULL) {
2937 rc = EPWMD_COMMAND_SYNTAX;
2938 goto fail;
2941 gchar *tmp = g_strdup((gchar *)a);
2942 xmlFree(a);
2943 gboolean literal = is_literal_element(&tmp);
2945 if (!valid_xml_element((xmlChar *)tmp) || literal) {
2946 g_free(tmp);
2947 rc = EPWMD_INVALID_ELEMENT;
2948 goto fail;
2951 if (strv_printf(&path, "%s", tmp) == FALSE) {
2952 g_free(tmp);
2953 rc = gpg_error_from_errno(ENOMEM);
2954 goto fail;
2957 g_free(tmp);
2958 n = find_account(client->doc, &path, &rc, NULL, 0, TRUE);
2960 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2961 rc = EPWMD_LIBXML_ERROR;
2962 goto fail;
2965 /* Overwriting the existing tree. */
2966 if (!rc) {
2967 xmlUnlinkNode(n);
2968 xmlFreeNodeList(n);
2971 rc = 0;
2972 xmlSetProp(root, (xmlChar *)"name", (xmlChar *)path[0]);
2973 n = xmlCopyNode(root, 1);
2974 n = xmlAddChild(xmlDocGetRootElement(client->doc), n);
2977 fail:
2978 if (doc)
2979 xmlFreeDoc(doc);
2981 if (path)
2982 g_strfreev(path);
2984 g_strfreev(req);
2985 client->inquire_status = INQUIRE_DONE;
2986 return rc;
2989 static gpg_error_t import_command(assuan_context_t ctx, gchar *line)
2991 gpg_error_t rc;
2992 struct client_s *client = assuan_get_pointer(ctx);
2994 rc = command_startup(ctx);
2996 if (rc)
2997 return send_error(ctx, rc);
2999 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
3001 if (rc)
3002 return send_error(ctx, rc);
3004 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
3005 client->inquire_status = INQUIRE_BUSY;
3006 return 0;
3009 static gpg_error_t lock_command(assuan_context_t ctx, gchar *line)
3011 gpg_error_t rc;
3012 struct client_s *client = assuan_get_pointer(ctx);
3014 rc = command_startup(ctx);
3016 if (rc)
3017 return send_error(ctx, rc);
3019 rc = lock_file_mutex(client);
3021 if (!rc)
3022 client->is_lock_cmd = TRUE;
3024 return send_error(ctx, rc);
3027 static gpg_error_t unlock_command(assuan_context_t ctx, gchar *line)
3029 struct client_s *client = assuan_get_pointer(ctx);
3031 gpg_error_t rc = command_startup(ctx);
3033 if (rc)
3034 return send_error(ctx, rc);
3036 unlock_file_mutex(client);
3037 return send_error(ctx, 0);
3040 static gpg_error_t getpid_command(assuan_context_t ctx, gchar *line)
3042 gpg_error_t rc;
3043 gchar buf[32];
3044 pid_t pid = getpid();
3046 rc = command_startup(ctx);
3048 if (rc)
3049 return send_error(ctx, rc);
3051 print_fmt(buf, sizeof(buf), "%i", pid);
3052 rc = xfer_data(ctx, buf, strlen(buf));
3053 return send_error(ctx, rc);
3056 static gpg_error_t version_command(assuan_context_t ctx, gchar *line)
3058 gpg_error_t rc;
3059 gchar buf[32];
3061 rc = command_startup(ctx);
3063 if (rc)
3064 return send_error(ctx, rc);
3066 print_fmt(buf, sizeof(buf), "0x%X", VERSION_HEX);
3067 rc = xfer_data(ctx, buf, strlen(buf));
3068 return send_error(ctx, rc);
3071 #ifdef WITH_PINENTRY
3072 static void set_option_value(gchar **opt, const gchar *value)
3074 if (opt)
3075 g_free(*opt);
3077 *opt = NULL;
3079 if (value)
3080 *opt = g_strdup(value);
3082 #endif
3084 static gint set_unset_common(assuan_context_t ctx, const gchar *name,
3085 const gchar *value)
3087 struct client_s *client = assuan_get_pointer(ctx);
3089 if (g_ascii_strcasecmp(name, (gchar *)"log_level") == 0) {
3090 glong l = 0;
3092 if (value) {
3093 l = strtol(value, NULL, 10);
3095 if (l < 0 || l > 2)
3096 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3099 log_write1("log_level=%li", l);
3100 MUTEX_LOCK(&rcfile_mutex);
3101 g_key_file_set_integer(keyfileh, "global", "log_level", (gint)l);
3102 MUTEX_UNLOCK(&rcfile_mutex);
3103 goto done;
3105 else if (g_ascii_strcasecmp(name, (gchar *)"cipher") == 0) {
3106 guint64 flags;
3107 const gchar *p = value;
3109 if (!client->filename)
3110 return EPWMD_NO_FILE;
3112 if (value) {
3113 flags = pwmd_cipher_str_to_cipher(value);
3115 if (!flags)
3116 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3118 else if (!value)
3119 p = get_key_file_string("global", "cipher");
3121 MUTEX_LOCK(&rcfile_mutex);
3122 g_key_file_set_string(keyfileh, client->filename, "cipher", p);
3123 MUTEX_UNLOCK(&rcfile_mutex);
3124 log_write1("cipher=%s", p);
3126 if (!value)
3127 g_free((gchar *)p);
3129 goto done;
3131 else if (g_ascii_strcasecmp(name, (gchar *)"iterations") == 0) {
3132 guint64 n;
3133 gchar *p = NULL;
3135 if (!client->filename)
3136 return EPWMD_NO_FILE;
3138 if (!value) {
3139 MUTEX_LOCK(&rcfile_mutex);
3140 g_key_file_set_double(keyfileh, client->filename, "iterations",
3141 get_key_file_double("global", "iterations"));
3142 MUTEX_UNLOCK(&rcfile_mutex);
3143 log_write1("iterations=%lu",
3144 (guint64)get_key_file_double(client->filename, "iterations"));
3145 goto done;
3148 errno = 0;
3149 n = strtoul(value, &p, 10);
3151 if (errno || (p && *p) || n == G_MAXULONG)
3152 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3154 MUTEX_LOCK(&rcfile_mutex);
3155 g_key_file_set_double(keyfileh,
3156 client->filename ? client->filename : "global", "iterations", n);
3157 MUTEX_UNLOCK(&rcfile_mutex);
3159 if (client->filename)
3160 client->opts |= OPT_ITERATIONS;
3162 log_write1("iterations=%lu", n);
3163 goto done;
3165 else if (g_ascii_strcasecmp(name, (gchar *)"NAME") == 0) {
3166 pth_attr_t attr = pth_attr_of(pth_self());
3167 gchar buf[41];
3169 if (!value) {
3170 pth_attr_destroy(attr);
3171 goto done;
3174 print_fmt(buf, sizeof(buf), "%s", value);
3175 pth_attr_set(attr, PTH_ATTR_NAME, buf);
3176 pth_attr_destroy(attr);
3177 log_write1("name=%s", buf);
3178 #ifdef WITH_PINENTRY
3179 if (client->pinentry->name)
3180 g_free(client->pinentry->name);
3182 client->pinentry->name = g_strdup(buf);
3184 if (!client->pinentry->name)
3185 return gpg_error_from_errno(ENOMEM);
3186 #endif
3188 goto done;
3190 #ifdef WITH_PINENTRY
3191 else if (g_ascii_strcasecmp(name, (gchar *)"lc_messages") == 0)
3192 set_option_value(&client->pinentry->lcmessages, value);
3193 else if (g_ascii_strcasecmp(name, (gchar *)"lc_ctype") == 0)
3194 set_option_value(&client->pinentry->lcctype, value);
3195 else if (g_ascii_strcasecmp(name, (gchar *)"ttyname") == 0)
3196 set_option_value(&client->pinentry->ttyname, value);
3197 else if (g_ascii_strcasecmp(name, (gchar *)"ttytype") == 0)
3198 set_option_value(&client->pinentry->ttytype, value);
3199 else if (g_ascii_strcasecmp(name, (gchar *)"display") == 0)
3200 set_option_value(&client->pinentry->display, value);
3201 else if (g_ascii_strcasecmp(name, (gchar *)"pinentry_path") == 0)
3202 set_option_value(&client->pinentry->path, value);
3203 else if (g_ascii_strcasecmp(name, (gchar *)"title") == 0)
3204 set_option_value(&client->pinentry->title, value);
3205 else if (g_ascii_strcasecmp(name, (gchar *)"prompt") == 0)
3206 set_option_value(&client->pinentry->prompt, value);
3207 else if (g_ascii_strcasecmp(name, (gchar *)"desc") == 0)
3208 set_option_value(&client->pinentry->desc, value);
3209 else if (g_ascii_strcasecmp(name, "pinentry_timeout") == 0) {
3210 gchar *p = NULL;
3211 gint n;
3213 if (!value) {
3214 client->pinentry->timeout =
3215 get_key_file_integer(client->filename, "pinentry_timeout");
3216 client->opts &= ~(OPT_PINENTRY_TO);
3217 log_write1("pinentry_timeout=%i",
3218 get_key_file_integer(client->filename, "pinentry_timeout"));
3219 goto done;
3222 n = strtol(value, &p, 10);
3224 if (*p || n < 0)
3225 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3227 client->pinentry->timeout = n;
3228 client->opts |= OPT_PINENTRY_TO;
3229 log_write1("pinentry_timeout=%i", n);
3230 goto done;
3232 else if (g_ascii_strcasecmp(name, "enable_pinentry") == 0) {
3233 gchar *p = NULL;
3234 gint n;
3236 if (!value) {
3237 client->pinentry->enable = -1;
3238 client->opts &= ~(OPT_PINENTRY);
3239 goto done;
3242 n = strtol(value, &p, 10);
3244 if (*p || n < 0 || n > 1)
3245 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3247 client->pinentry->enable = n == 0 ? FALSE : TRUE;
3248 client->opts |= OPT_PINENTRY;
3249 log_write1("enable_pinentry=%i", n);
3250 goto done;
3252 #endif
3253 else
3254 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
3256 log_write1("%s=%s", name, value ? value : "");
3258 done:
3259 return 0;
3262 static gpg_error_t unset_command(assuan_context_t ctx, gchar *line)
3264 gpg_error_t rc = command_startup(ctx);
3266 if (rc)
3267 return send_error(ctx, rc);
3269 log_write2("ARGS=\"%s\"", line);
3270 return send_error(ctx, set_unset_common(ctx, line, NULL));
3273 static gpg_error_t set_command(assuan_context_t ctx, gchar *line)
3275 gchar name[64] = {0}, value[256] = {0};
3277 gpg_error_t rc = command_startup(ctx);
3279 if (rc)
3280 return send_error(ctx, rc);
3282 log_write2("ARGS=\"%s\"", line);
3284 if (sscanf(line, " %63[_a-zA-Z] = %255c", name, value) != 2)
3285 return send_error(ctx, gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX));
3287 return send_error(ctx, set_unset_common(ctx, name, value));
3290 static gpg_error_t rename_command(assuan_context_t ctx, gchar *line)
3292 struct client_s *client = assuan_get_pointer(ctx);
3293 gpg_error_t rc;
3294 gchar **req, **src, *dst;
3295 xmlNodePtr n;
3297 rc = command_startup(ctx);
3299 if (rc)
3300 return send_error(ctx, rc);
3302 log_write2("ARGS=\"%s\"", line);
3303 req = split_input_line(line, " ", -1);
3305 if (!req || !req[0] || !req[1]) {
3306 g_strfreev(req);
3307 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
3310 dst = req[1];
3312 if (!valid_xml_element((xmlChar *)dst)) {
3313 g_strfreev(req);
3314 return EPWMD_INVALID_ELEMENT;
3317 if (strchr(req[0], '\t'))
3318 src = split_input_line(req[0], "\t", -1);
3319 else
3320 src = split_input_line(req[0], " ", -1);
3322 if (!src || !*src) {
3323 rc = EPWMD_COMMAND_SYNTAX;
3324 goto fail;
3327 n = find_account(client->doc, &src, &rc, NULL, 0, FALSE);
3329 if (src[1] && n)
3330 n = find_elements(client->doc, n->children, src+1, &rc, NULL, NULL,
3331 NULL, FALSE, 0, NULL);
3333 if (!n)
3334 goto fail;
3336 if (src[1])
3337 xmlNodeSetName(n, (xmlChar *)dst);
3338 else
3339 rc = add_attribute(n, "name", dst);
3341 fail:
3342 g_strfreev(req);
3343 g_strfreev(src);
3344 return send_error(ctx, rc);
3347 static gpg_error_t copy_command(assuan_context_t ctx, gchar *line)
3349 struct client_s *client = assuan_get_pointer(ctx);
3350 gpg_error_t rc;
3351 gchar **req, **src = NULL, **dst = NULL;
3352 xmlNodePtr nsrc, ndst, new, n;
3354 rc = command_startup(ctx);
3356 if (rc)
3357 return send_error(ctx, rc);
3359 log_write2("ARGS=\"%s\"", line);
3360 req = split_input_line(line, " ", -1);
3362 if (!req || !req[0] || !req[1]) {
3363 g_strfreev(req);
3364 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
3367 if (strchr(req[0], '\t'))
3368 src = split_input_line(req[0], "\t", -1);
3369 else
3370 src = split_input_line(req[0], " ", -1);
3372 if (!src || !*src) {
3373 rc = EPWMD_COMMAND_SYNTAX;
3374 goto fail;
3377 if (strchr(req[1], '\t'))
3378 dst = split_input_line(req[1], "\t", -1);
3379 else
3380 dst = split_input_line(req[1], " ", -1);
3382 if (!dst || !*dst) {
3383 rc = EPWMD_COMMAND_SYNTAX;
3384 goto fail;
3387 nsrc = find_account(client->doc, &src, &rc, NULL, 0, FALSE);
3389 if (nsrc && src[1])
3390 nsrc = find_elements(client->doc, nsrc->children, src+1, &rc, NULL,
3391 NULL, NULL, FALSE, 0, NULL);
3393 if (!nsrc)
3394 goto fail;
3396 ndst = find_account(client->doc, &dst, &rc, NULL, 0, FALSE);
3398 if (ndst && dst[1])
3399 ndst = find_elements(client->doc, ndst->children, dst+1, &rc, NULL,
3400 NULL, NULL, FALSE, 0, NULL);
3402 if (!ndst && rc != EPWMD_ELEMENT_NOT_FOUND)
3403 goto fail;
3405 new = xmlCopyNodeList(nsrc->children);
3407 if (!new) {
3408 rc = GPG_ERR_ENOMEM;
3409 goto fail;
3412 if (!ndst) {
3413 ndst = create_element_path(client, &dst, &rc);
3415 if (!ndst)
3416 goto fail;
3419 n = ndst->children;
3420 xmlUnlinkNode(ndst->children);
3421 xmlFreeNodeList(n);
3422 xmlAddChild(ndst, new);
3424 fail:
3425 if (req)
3426 g_strfreev(req);
3428 if (src)
3429 g_strfreev(src);
3431 if (dst)
3432 g_strfreev(dst);
3434 return send_error(ctx, rc);
3437 static gpg_error_t bye_notify(assuan_context_t ctx, gchar *data)
3439 struct client_s *cl = assuan_get_pointer(ctx);
3441 /* This will let assuan_process_next() return. */
3442 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
3443 return 0;
3446 static gpg_error_t reset_notify(assuan_context_t ctx, gchar *data)
3448 struct client_s *cl = assuan_get_pointer(ctx);
3450 if (cl)
3451 cleanup_client(cl);
3453 return 0;
3457 * This is called after every Assuan command.
3459 void command_finalize(assuan_context_t ctx, gpg_error_t rc)
3461 struct client_s *client = assuan_get_pointer(ctx);
3463 if (!client->is_lock_cmd)
3464 unlock_file_mutex(client);
3466 log_write1(N_("command completed (rc=%u)"), client->last_rc);
3469 gpg_error_t register_commands(assuan_context_t ctx)
3471 static struct {
3472 const gchar *name;
3473 gpg_error_t (*handler)(assuan_context_t, gchar *line);
3474 } table[] = {
3475 { "OPEN", open_command },
3476 { "SAVE", save_command },
3477 { "LIST", list_command },
3478 { "REALPATH", realpath_command },
3479 { "STORE", store_command },
3480 { "DELETE", delete_command },
3481 { "GET", get_command },
3482 { "ATTR", attr_command },
3483 { "ISCACHED", iscached_command },
3484 { "CLEARCACHE", clearcache_command },
3485 { "CACHETIMEOUT", cachetimeout_command },
3486 { "GETCONFIG", getconfig_command },
3487 { "DUMP", dump_command },
3488 { "XPATH", xpath_command },
3489 { "IMPORT", import_command },
3490 { "LOCK", lock_command },
3491 { "UNLOCK", unlock_command },
3492 { "GETPID", getpid_command },
3493 { "VERSION", version_command },
3494 { "SET", set_command },
3495 { "UNSET", unset_command },
3496 { "RENAME", rename_command },
3497 { "COPY", copy_command },
3498 { "INPUT", NULL },
3499 { "OUTPUT", NULL },
3500 { NULL, NULL }
3502 gint i, rc;
3504 for (i=0; table[i].name; i++) {
3505 rc = assuan_register_command (ctx, table[i].name, table[i].handler, NULL);
3507 if (rc)
3508 return rc;
3511 rc = assuan_register_bye_notify(ctx, bye_notify);
3513 if (rc)
3514 return rc;
3516 rc = assuan_register_reset_notify(ctx, reset_notify);
3518 if (rc)
3519 return rc;
3521 return assuan_register_post_cmd_notify(ctx, command_finalize);
3524 gpg_error_t try_xml_decrypt(assuan_context_t ctx, guchar *key,
3525 struct client_crypto_s *crypto, gpointer *dst, goffset *dst_len)
3527 goffset insize, len;
3528 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
3529 guint64 iter = 0ULL, n_iter = 0ULL, iter_progress = 0ULL;
3530 gint zrc = 0;
3531 gulong outsize = 0;
3532 gpg_error_t rc;
3533 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->ver.fh1) : sizeof(crypto->fh->ver.fh2);
3534 guint64 fh_iter = crypto->fh->v1 ? crypto->fh->ver.fh1.iter : crypto->fh->ver.fh2.iter;
3535 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
3537 lseek(crypto->fh->fd, fh_size, SEEK_SET);
3538 insize = crypto->fh->st.st_size - fh_size;
3539 crypto->iv = gcry_malloc(crypto->blocksize);
3541 if (!crypto->iv) {
3542 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3543 return gpg_error_from_errno(ENOMEM);
3546 if (crypto->fh->v1)
3547 memcpy(crypto->iv, crypto->fh->ver.fh1.iv, crypto->blocksize);
3548 else
3549 memcpy(crypto->iv, crypto->fh->ver.fh2.iv, crypto->blocksize);
3551 crypto->inbuf = gcry_malloc(insize);
3553 if (!crypto->inbuf) {
3554 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
3555 return gpg_error_from_errno(ENOMEM);
3558 crypto->insize = insize;
3559 len = pth_read(crypto->fh->fd, crypto->inbuf, crypto->insize);
3561 if (len != crypto->insize)
3562 return GPG_ERR_INV_LENGTH;
3564 /* No encryption iterations. This is a plain (gzipped) file. */
3565 if ((crypto->fh->v1 && (long)fh_iter < 0L) ||
3566 (!crypto->fh->v1 && fh_iter <= 0L)) {
3568 * cache_file_count() needs both .used == TRUE and a valid key in
3569 * order for it to count as a used cache entry. Fixes CACHE status
3570 * messages.
3572 memset(key, '!', hashlen);
3573 goto decompress;
3576 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, crypto->blocksize))) {
3577 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3578 return rc;
3581 if ((rc = gcry_cipher_setkey(crypto->gh, key, crypto->keysize))) {
3582 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3583 return rc;
3586 iter_progress = (guint64)get_key_file_double(client && client->filename ?
3587 client->filename : "global", "iteration_progress");
3589 if (iter_progress > 0ULL && fh_iter >= iter_progress) {
3590 rc = send_status(ctx, STATUS_DECRYPT, "0 %llu", fh_iter);
3592 if (rc)
3593 return rc;
3596 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3598 if (rc)
3599 return rc;
3601 crypto->tkey = gcry_malloc(hashlen);
3603 if (!crypto->tkey) {
3604 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
3605 return gpg_error_from_errno(ENOMEM);
3608 memcpy(crypto->tkey, key, hashlen);
3609 guchar *tkey = crypto->tkey;
3610 tkey[0] ^= 1;
3612 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, crypto->keysize))) {
3613 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3614 return rc;
3617 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
3618 if (iter_progress > 0ULL && iter >= iter_progress) {
3619 if (!(iter % iter_progress)) {
3620 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu",
3621 ++n_iter * iter_progress, fh_iter);
3623 if (rc)
3624 return rc;
3628 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, crypto->blocksize))) {
3629 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3630 return rc;
3633 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
3635 if (rc) {
3636 log_write("%s(%i): %s", __FUNCTION__, __LINE__, _gpg_strerror(rc));
3637 return rc;
3640 iter++;
3643 if (iter_progress && fh_iter >= iter_progress) {
3644 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu", fh_iter, fh_iter);
3646 if (rc)
3647 return rc;
3650 decompress:
3651 if (do_decompress(ctx, crypto->inbuf, crypto->insize,
3652 (gpointer *)&crypto->outbuf, &outsize, &zrc) == FALSE) {
3653 if (zrc == Z_MEM_ERROR)
3654 return gpg_error_from_errno(ENOMEM);
3655 else
3656 return EPWMD_BADKEY; // Not a valid gzip header. Must be a bad key.
3659 if (g_strncasecmp(crypto->outbuf, "<?xml ", 6) != 0) {
3660 gcry_free(crypto->outbuf);
3661 crypto->outbuf = NULL;
3662 return EPWMD_BADKEY;
3665 if (ctx) {
3666 client->xml = crypto->outbuf;
3667 client->len = outsize;
3668 crypto->outbuf = NULL;
3670 else if (dst) {
3671 *dst = crypto->outbuf;
3672 *dst_len = outsize;
3673 crypto->outbuf = NULL;
3676 /* The calling function should free the crypto struct. */
3677 return 0;