Sort the command names for the help text.
[pwmd.git] / src / commands.c
blobf352a81bf8ac9fbe24f0517d12d94c022966f80b
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 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include <ctype.h>
32 #include <glib.h>
33 #include <gcrypt.h>
34 #include <zlib.h>
35 #include <dirent.h>
37 #ifdef WITH_LIBACL
38 #include <sys/acl.h>
39 #endif
41 #include "mem.h"
42 #include "xml.h"
43 #include "common.h"
45 #ifdef WITH_PINENTRY
46 #include "pinentry.h"
47 #endif
49 #include "pwmd_error.h"
50 #include "cache.h"
51 #include "misc.h"
52 #include "commands.h"
53 #include "mutex.h"
55 struct gz_s {
56 z_stream z;
57 gpointer out;
58 gboolean done;
59 status_msg_t which;
62 struct command_table_s {
63 const gchar *name;
64 gint (*handler)(assuan_context_t, gchar *line);
65 const gchar *help;
66 gboolean ignore_startup;
69 static struct command_table_s **command_table;
70 static guchar crypto_magic[5] = "\177PWMD";
72 static gpg_error_t do_lock_command(struct client_s *client);
74 static void *z_alloc(void *data, unsigned items, unsigned size)
76 return gcry_calloc(items, size);
79 static void z_free(void *data, void *p)
81 gcry_free(p);
84 static gpg_error_t file_modified(struct client_s *client)
86 struct stat st;
87 gpg_error_t rc;
89 if (client->state != STATE_OPEN)
90 return EPWMD_NO_FILE;
92 rc = lock_file_mutex(client);
94 if (rc)
95 return rc;
97 if (lstat(client->filename, &st) == 0 && client->mtime) {
98 if (client->mtime != st.st_mtime)
99 return EPWMD_FILE_MODIFIED;
102 pth_cancel_point();
103 return 0;
106 static gpg_error_t parse_xml(assuan_context_t ctx)
108 struct client_s *client = assuan_get_pointer(ctx);
110 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
112 if (!client->doc)
113 return EPWMD_LIBXML_ERROR;
115 if (!client->crypto->fh || client->crypto->fh->ver.fh2.version >= 0x212)
116 return 0;
118 return convert_elements(client->doc);
121 void unlock_file_mutex(struct client_s *client)
123 pth_mutex_t *m;
125 if (client->has_lock == FALSE)
126 return;
128 CACHE_LOCK(client->ctx);
130 if (cache_get_mutex(client->md5file, &m) == FALSE) {
131 CACHE_UNLOCK;
132 return;
135 CACHE_UNLOCK;
136 MUTEX_UNLOCK(m);
137 client->has_lock = client->is_lock_cmd = FALSE;
140 gpg_error_t lock_file_mutex(struct client_s *client)
142 pth_mutex_t *m;
143 gpg_error_t rc = 0;
145 if (client->has_lock == TRUE)
146 return 0;
148 CACHE_LOCK(client->ctx);
150 if (cache_get_mutex(client->md5file, &m) == FALSE) {
151 CACHE_UNLOCK;
152 return 0;
155 CACHE_UNLOCK;
157 if (client->rc_on_locked) {
158 if (!pth_mutex_acquire(m, TRUE, NULL))
159 return GPG_ERR_LOCKED;
160 #ifdef MUTEX_DEBUG
161 MUTEX_LOCK_DEBUG(m);
162 #endif
164 else
165 MUTEX_TRYLOCK(client, m, rc);
167 if (!rc)
168 client->has_lock = TRUE;
170 return rc;
173 void free_client(struct client_s *client)
175 if (client->doc)
176 xmlFreeDoc(client->doc);
178 if (client->xml)
179 gcry_free(client->xml);
181 if (client->filename)
182 g_free(client->filename);
184 if (client->crypto)
185 cleanup_crypto(&client->crypto);
187 if (client->xml_error)
188 xmlResetError(client->xml_error);
191 void cleanup_client(struct client_s *client)
193 assuan_context_t ctx = client->ctx;
194 struct client_thread_s *thd = client->thd;
195 gboolean rc_on_locked = client->rc_on_locked;
196 gboolean lock_on_open = client->lock_on_open;
197 #ifdef WITH_PINENTRY
198 struct pinentry_s *pin = client->pinentry;
199 #endif
201 unlock_file_mutex(client);
202 CACHE_LOCK(client->ctx);
203 cache_decr_refcount(client->md5file);
206 * This may be a new file so don't use a cache slot. save_command() will
207 * set this to FALSE on success.
209 if (client->new == TRUE)
210 cache_clear(client->md5file, 1);
212 CACHE_UNLOCK;
213 free_client(client);
214 memset(client, 0, sizeof(struct client_s));
215 client->state = STATE_CONNECTED;
216 client->ctx = ctx;
217 client->thd = thd;
218 client->freed = TRUE;
219 #ifdef WITH_PINENTRY
220 client->pinentry = pin;
221 #endif
222 client->rc_on_locked = rc_on_locked;
223 client->lock_on_open = lock_on_open;
226 static void gz_cleanup(void *arg)
228 struct gz_s **gz = (struct gz_s **)arg;
230 if (!gz)
231 return;
233 if (!(*gz)->done && (*gz)->out)
234 gcry_free((*gz)->out);
236 if ((*gz)->which == STATUS_COMPRESS) {
237 if ((*gz)->z.zalloc)
238 deflateEnd(&(*gz)->z);
240 else {
241 if ((*gz)->z.zalloc)
242 inflateEnd(&(*gz)->z);
245 g_free(*gz);
246 *gz = NULL;
249 gpg_error_t do_decompress(assuan_context_t ctx, gpointer in, gulong insize,
250 gpointer *out, gulong *outsize)
252 struct gz_s *gz;
253 gz_header h;
254 gchar buf[17];
255 gpg_error_t rc;
256 gint zrc;
258 gz = g_malloc0(sizeof(struct gz_s));
260 if (!gz)
261 return GPG_ERR_ENOMEM;
263 pth_cleanup_push(gz_cleanup, &gz);
264 gz->which = STATUS_DECOMPRESS;
265 gz->z.zalloc = z_alloc;
266 gz->z.zfree = z_free;
267 gz->z.next_in = in;
268 gz->z.avail_in = (uInt)insize;
269 gz->z.avail_out = zlib_bufsize;
270 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
272 if (!gz->out) {
273 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
274 pth_cleanup_pop(1);
275 return GPG_ERR_ENOMEM;
278 zrc = inflateInit2(&gz->z, 47);
280 if (zrc != Z_OK) {
281 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
282 pth_cleanup_pop(1);
283 return zrc == Z_MEM_ERROR ? GPG_ERR_ENOMEM : GPG_ERR_COMPR_ALGO;
286 memset(&h, 0, sizeof(gz_header));
287 h.comment = (guchar *)buf;
288 h.comm_max = sizeof(buf);
289 zrc = inflateGetHeader(&gz->z, &h);
291 if (zrc != Z_OK) {
292 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
293 pth_cleanup_pop(1);
294 return zrc == Z_MEM_ERROR ? GPG_ERR_ENOMEM : GPG_ERR_COMPR_ALGO;
297 zrc = inflate(&gz->z, Z_BLOCK);
299 if (zrc != Z_OK) {
300 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
301 pth_cleanup_pop(1);
302 return zrc == Z_MEM_ERROR ? GPG_ERR_ENOMEM : GPG_ERR_COMPR_ALGO;
305 if (h.comment)
306 insize = (gulong)strtol((gchar *)h.comment, NULL, 10);
308 do {
309 gpointer p;
311 zrc = inflate(&gz->z, Z_FINISH);
313 switch (zrc) {
314 case Z_OK:
315 break;
316 case Z_BUF_ERROR:
317 if (!gz->z.avail_out) {
318 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
320 if (!p) {
321 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
322 rc = GPG_ERR_ENOMEM;
323 goto fail;
326 gz->out = p;
327 gz->z.next_out = (guchar *)gz->out + gz->z.total_out;
328 gz->z.avail_out = zlib_bufsize;
329 rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li",
330 gz->z.total_out, insize);
332 if (rc)
333 goto fail;
335 break;
336 case Z_STREAM_END:
337 break;
338 default:
339 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
340 rc = GPG_ERR_COMPR_ALGO;
341 goto fail;
342 break;
344 } while (zrc != Z_STREAM_END);
346 rc = send_status(ctx, STATUS_DECOMPRESS, "%li %li", gz->z.total_out,
347 insize);
349 if (rc)
350 goto fail;
352 *out = gz->out;
353 *outsize = gz->z.total_out;
354 gz->done = TRUE;
355 pth_cleanup_pop(1);
356 return 0;
358 fail:
359 pth_cleanup_pop(1);
360 return rc;
363 file_header_internal_t *read_file_header(const gchar *filename, gboolean v1,
364 gpg_error_t *rc)
366 gint fd;
367 gsize len;
368 file_header_internal_t *fh = g_malloc0(sizeof(file_header_internal_t));
369 gsize fh_size;
370 gpointer p;
372 *rc = 0;
374 if (!fh) {
375 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
376 *rc = GPG_ERR_ENOMEM;
377 return NULL;
380 pth_cleanup_push(g_free, fh);
381 fh_size = v1 ? sizeof(fh->ver.fh1) : sizeof(fh->ver.fh2);
383 if (lstat(filename, &fh->st) == -1) {
384 *rc = gpg_error_from_syserror();
385 pth_cleanup_pop(1);
386 return NULL;
389 if (!S_ISREG(fh->st.st_mode)) {
390 *rc = GPG_ERR_ENOANO;
391 pth_cleanup_pop(1);
392 return NULL;
395 fd = open(filename, O_RDONLY);
397 if (fd == -1) {
398 *rc = gpg_error_from_syserror();
399 pth_cleanup_pop(1);
400 return NULL;
403 pth_cleanup_push(cleanup_fd_cb, &fd);
404 p = v1 ? (void *)&fh->ver.fh1 : (void *)&fh->ver.fh2;
405 len = pth_read(fd, p, fh_size);
407 if (len != fh_size) {
408 *rc = gpg_error_from_syserror();
409 pth_cleanup_pop(1);
410 pth_cleanup_pop(1);
411 return NULL;
414 fh->v1 = v1;
415 fh->fd = fd;
416 pth_cleanup_pop(0);
417 pth_cleanup_pop(0);
418 return fh;
421 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar *unused,
422 gboolean cached)
424 struct client_s *client = assuan_get_pointer(ctx);
425 gpg_error_t rc;
426 gint timeout;
427 guchar *key = client->crypto->key;
429 /* New file. */
430 if (!client->crypto->fh) {
431 if (key[0])
432 goto update_cache;
434 goto done;
437 rc = init_client_crypto2(client->filename, client->crypto);
439 if (rc) {
440 cleanup_client(client);
441 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
444 rc = try_xml_decrypt(ctx, client->crypto, NULL, NULL);
446 if (rc) {
447 cleanup_client(client);
448 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
451 update_cache:
452 CACHE_LOCK(client->ctx);
454 if (cached == FALSE) {
455 if (cache_update_key(client->md5file, key) == FALSE) {
456 cleanup_client(client);
457 CACHE_UNLOCK;
458 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
461 timeout = get_key_file_integer(client->filename, "cache_timeout");
462 cache_reset_timeout(client->md5file, timeout);
464 else
465 cache_set_timeout(client->md5file, -2);
467 CACHE_UNLOCK;
469 done:
470 rc = parse_xml(ctx);
472 if (client->xml) {
473 gcry_free(client->xml);
474 client->xml = NULL;
477 if (!rc) {
478 if (client->new == FALSE)
479 send_status_all(STATUS_CACHE);
481 client->state = STATE_OPEN;
484 if (!rc && client->new == FALSE &&
485 client->crypto->fh->ver.fh2.iter != (guint64)get_key_file_double(client->filename, "iterations")) {
486 MUTEX_LOCK(&rcfile_mutex);
487 g_key_file_set_double(keyfileh, client->filename, "iterations",
488 client->crypto->fh->ver.fh2.iter);
489 MUTEX_UNLOCK(&rcfile_mutex);
490 send_status_all(STATUS_CONFIG);
493 cleanup_crypto(&client->crypto);
495 if (!rc && client->lock_on_open)
496 return do_lock_command(client);
498 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
501 static void req_cleanup(void *arg)
503 if (!arg)
504 return;
506 g_strfreev((gchar **)arg);
509 static gpg_error_t parse_open_opt_lock(gpointer data, gpointer value)
511 struct client_s *client = data;
512 const gchar *p = value;
514 if (p && *p) {
515 long l = strtol(p, NULL, 10);
517 if (l < 0 || l > 1)
518 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
520 client->lock_on_open = l ? TRUE : FALSE;
522 else if ((!p || !*p) && (client->opts & OPT_LOCK))
523 client->lock_on_open = FALSE;
524 else
525 client->lock_on_open = TRUE;
527 return 0;
530 static gpg_error_t parse_opt_pinentry(gpointer data, gpointer value)
532 #ifdef WITH_PINENTRY
533 struct client_s *client = data;
534 gchar *p = NULL;
535 gchar *str = value;
536 gint n;
538 if (!str || !*str) {
539 client->pinentry->enable = -1;
540 client->opts &= ~(OPT_PINENTRY);
541 return 0;
544 n = strtol(str, &p, 10);
546 if (*p || n < 0 || n > 1)
547 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
549 client->pinentry->enable = n ? TRUE : FALSE;
550 client->opts |= OPT_PINENTRY;
551 return 0;
552 #else
553 return GPG_ERR_NOT_IMPLEMENTED;
554 #endif
557 static gpg_error_t parse_opt_inquire(gpointer data, gpointer value)
559 struct client_s *client = data;
561 (void)value;
562 client->opts |= OPT_INQUIRE;
563 return 0;
566 static gpg_error_t parse_opt_base64(gpointer data, gpointer value)
568 struct client_s *client = data;
570 (void)value;
571 client->opts |= OPT_BASE64;
572 return 0;
575 static gpg_error_t hash_key(struct client_s *client, const gchar *key)
577 guchar *tmp;
578 gsize len;
580 if (client->opts & OPT_BASE64)
581 tmp = g_base64_decode(key, &len);
582 else {
583 tmp = (guchar *)g_strdup(key);
584 len = strlen(key);
587 if (!tmp)
588 return GPG_ERR_ENOMEM;
590 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, tmp, len);
591 g_free(tmp);
592 return 0;
595 static gpg_error_t open_command_common(assuan_context_t ctx, gchar *line)
597 gboolean cached = FALSE;
598 gpg_error_t rc;
599 struct client_s *client = assuan_get_pointer(ctx);
600 gchar **req;
601 gchar *filename = NULL;
602 gsize hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
604 if ((req = split_input_line(line, " ", 2)) != NULL)
605 filename = req[0];
607 pth_cleanup_push(req_cleanup, req);
609 if (!filename || !*filename) {
610 pth_cleanup_pop(1);
611 return client->opts & OPT_INQUIRE ? GPG_ERR_SYNTAX : send_error(ctx, GPG_ERR_SYNTAX);
614 log_write2("ARGS=\"%s\" %s", filename, req[1] ? "<passphrase>" : "");
616 if (valid_filename(filename) == FALSE) {
617 pth_cleanup_pop(1);
618 return client->opts & OPT_INQUIRE ? GPG_ERR_INV_VALUE : send_error(ctx, GPG_ERR_INV_VALUE);
621 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
622 CACHE_LOCK(client->ctx);
624 if (cache_has_file(client->md5file) == FALSE) {
625 if (cache_add_file(client->md5file, NULL) == FALSE) {
626 pth_cleanup_pop(1);
627 CACHE_UNLOCK;
628 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
632 CACHE_UNLOCK;
633 rc = lock_file_mutex(client);
635 if (rc) {
636 pth_cleanup_pop(1);
637 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
640 client->freed = FALSE;
641 CACHE_LOCK(client->ctx);
642 cache_incr_refcount(client->md5file);
643 CACHE_UNLOCK;
644 client->crypto = init_client_crypto();
646 if (!client->crypto) {
647 pth_cleanup_pop(1);
648 cleanup_client(client);
649 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
652 client->crypto->key = gcry_malloc(hashlen);
654 if (!client->crypto->key) {
655 pth_cleanup_pop(1);
656 log_write("%s(%i): %s", __FUNCTION__, __LINE__,
657 GPG_ERR_ENOMEM);
658 cleanup_client(client);
659 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
662 memset(client->crypto->key, 0, hashlen);
663 client->crypto->fh = read_file_header(filename, FALSE, &rc);
665 if (!client->crypto->fh) {
666 if (gpg_err_code_to_errno(rc) != ENOENT) {
667 log_write("%s: %s", filename, pwmd_strerror(rc));
668 pth_cleanup_pop(1);
669 cleanup_client(client);
670 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
674 * New files don't need a key.
676 if ((client->xml = new_document()) == NULL) {
677 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
678 pth_cleanup_pop(1);
679 cleanup_client(client);
680 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
683 client->len = xmlStrlen(client->xml);
684 client->new = TRUE;
685 client->filename = g_strdup(filename);
687 if (!client->filename) {
688 pth_cleanup_pop(1);
689 cleanup_client(client);
690 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
691 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
694 if (req[1] && *req[1]) {
695 rc = hash_key(client, req[1]);
697 if (rc) {
698 pth_cleanup_pop(1);
699 cleanup_client(client);
700 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
704 pth_cleanup_pop(1);
705 #ifdef WITH_PINENTRY
706 client->pinentry->filename = g_strdup(client->filename);
708 if (!client->pinentry->filename) {
709 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
710 cleanup_client(client);
711 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
713 #endif
714 return open_command_finalize(ctx, NULL, cached);
716 else {
717 if (!(client->opts & OPT_CIPHER))
718 g_key_file_set_string(keyfileh, filename, "cipher",
719 pwmd_cipher_to_str(client->crypto->fh->ver.fh2.flags));
721 client->mtime = client->crypto->fh->st.st_mtime;
724 client->filename = g_strdup(filename);
726 if (!client->filename) {
727 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
728 pth_cleanup_pop(1);
729 cleanup_client(client);
730 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
733 #ifdef WITH_PINENTRY
734 if (client->pinentry->filename)
735 g_free(client->pinentry->filename);
737 client->pinentry->filename = g_strdup(client->filename);
739 if (!client->pinentry->filename) {
740 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
741 pth_cleanup_pop(1);
742 cleanup_client(client);
743 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
745 #endif
747 if (client->crypto->fh->ver.fh2.iter <= 0ULL)
748 goto done;
750 CACHE_LOCK(client->ctx);
751 cached = cache_get_key(client->md5file, client->crypto->key);
752 CACHE_UNLOCK;
754 if (cached == FALSE) {
755 gchar *tmp = get_key_file_string(filename, "key_file");
757 if (tmp && !(client->opts & OPT_INQUIRE)) {
758 g_free(tmp);
759 pth_cleanup_pop(1);
760 cleanup_client(client);
761 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
764 if (tmp)
765 g_free(tmp);
768 * No key specified and no matching filename found in the cache. Use
769 * pinentry to retrieve the key. Cannot return assuan_process_done()
770 * here otherwise the command will be interrupted. The event loop in
771 * client_thread() will poll the file descriptor waiting for it to
772 * become ready to read a pinentry_key_s which will contain the
773 * entered key or an error code. It will then call
774 * open_command_finalize() to to finish the command.
776 if (!req[1] || !*req[1]) {
777 #ifdef WITH_PINENTRY
778 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
780 /* From set_pinentry_defaults(). */
781 if (client->opts & OPT_INQUIRE ||
782 client->pinentry->enable == FALSE ||
783 (client->pinentry->enable == -1 && b == FALSE)) {
784 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
785 goto done;
788 pth_cleanup_pop(1);
789 rc = lock_pin_mutex(client);
791 if (rc) {
792 unlock_pin_mutex(client->pinentry);
793 cleanup_client(client);
794 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
797 client->pinentry->which = PINENTRY_OPEN;
798 rc = pinentry_fork(ctx);
800 if (rc) {
801 unlock_pin_mutex(client->pinentry);
802 cleanup_client(client);
803 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
806 // Called from pinentry iterate.
807 client->pinentry->cb = open_command_finalize;
808 client->pinentry->status = PINENTRY_INIT;
809 return 0;
810 #else
811 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
812 goto done;
813 #endif
816 rc = hash_key(client, req[1]);
818 if (rc) {
819 pth_cleanup_pop(1);
820 cleanup_client(client);
821 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
824 else if (req && req[1] && *req[1]) {
825 rc = hash_key(client, req[1]);
827 if (rc) {
828 pth_cleanup_pop(1);
829 cleanup_client(client);
830 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
834 done:
835 pth_cleanup_pop(1);
836 return open_command_finalize(ctx, NULL, cached);
839 static gint open_command_inquire_finalize(gpointer data, gint assuan_rc,
840 guchar *line, gsize len)
842 assuan_context_t ctx = data;
843 struct client_s *client = assuan_get_pointer(ctx);
844 gpg_error_t rc = file_modified(client);
846 if (assuan_rc || (rc && rc != EPWMD_NO_FILE)) {
847 if (line)
848 xfree(line);
850 return assuan_rc ? assuan_rc : rc;
853 rc = open_command_common(ctx, (gchar *)line);
855 if (line)
856 xfree(line);
858 client->inquire_status = INQUIRE_DONE;
859 return rc;
862 static gint open_command(assuan_context_t ctx, gchar *line)
864 gpg_error_t rc;
865 struct client_s *client = assuan_get_pointer(ctx);
866 struct argv_s *args[] = {
867 &(struct argv_s) { "lock", OPTION_TYPE_NOARG, parse_open_opt_lock },
868 &(struct argv_s) { "pinentry", OPTION_TYPE_OPTARG, parse_opt_pinentry },
869 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
870 &(struct argv_s) { "base64", OPTION_TYPE_NOARG, parse_opt_base64 },
871 NULL
874 if (client->state == STATE_OPEN)
875 cleanup_client(client);
877 if (!(client->opts & OPT_LOCK))
878 client->lock_on_open = FALSE;
880 rc = parse_options(&line, args, client);
882 if (rc)
883 return send_error(ctx, rc);
885 if ((client->opts & OPT_INQUIRE)) {
886 rc = assuan_inquire_ext(ctx, "OPEN", 0, open_command_inquire_finalize,
887 ctx);
889 if (rc)
890 return send_error(ctx, rc);
892 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
893 client->inquire_status = INQUIRE_BUSY;
894 return 0;
897 return open_command_common(ctx, line);
900 gpg_error_t do_compress(assuan_context_t ctx, gint level, gpointer data,
901 guint size, gpointer *out, gulong *outsize)
903 struct gz_s *gz;
904 gz_header h;
905 gchar buf[17];
906 gint cmd = Z_NO_FLUSH;
907 gint zrc;
908 gpg_error_t rc;
910 gz = g_malloc0(sizeof(struct gz_s));
912 if (!gz)
913 return GPG_ERR_ENOMEM;
915 pth_cleanup_push(gz_cleanup, &gz);
916 gz->which = STATUS_COMPRESS;
917 gz->z.zalloc = z_alloc;
918 gz->z.zfree = z_free;
919 gz->z.next_in = data;
920 gz->z.avail_in = size < zlib_bufsize ? (uInt)size : (uInt)zlib_bufsize;
921 gz->z.avail_out = (uInt)zlib_bufsize;
922 gz->z.next_out = gz->out = gcry_malloc(zlib_bufsize);
924 if (!gz->out) {
925 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
926 pth_cleanup_pop(1);
927 return GPG_ERR_ENOMEM;
930 zrc = deflateInit2(&gz->z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
932 if (zrc != Z_OK) {
933 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
934 pth_cleanup_pop(1);
935 return GPG_ERR_COMPR_ALGO;
938 /* Rather than store the size of the uncompressed data in the file header,
939 * store it in the comment field of the gzip header. Don't give anyone too
940 * much information. Not sure why really, but it seems the right way. :)
942 memset(&h, 0, sizeof(gz_header));
943 g_snprintf(buf, sizeof(buf), "%u", size);
944 h.comment = (guchar *)buf;
945 zrc = deflateSetHeader(&gz->z, &h);
947 if (zrc != Z_OK) {
948 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
949 pth_cleanup_pop(1);
950 return GPG_ERR_COMPR_ALGO;
953 do {
954 gpointer p;
956 zrc = deflate(&gz->z, cmd);
958 switch (zrc) {
959 case Z_OK:
960 break;
961 case Z_BUF_ERROR:
962 if (!gz->z.avail_out) {
963 p = gcry_realloc(gz->out, gz->z.total_out + zlib_bufsize);
965 if (!p) {
966 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
967 rc = GPG_ERR_ENOMEM;
968 goto fail;
971 gz->out = p;
972 gz->z.next_out = (guchar *)gz->out + gz->z.total_out;
973 gz->z.avail_out = zlib_bufsize;
976 if (!gz->z.avail_in && gz->z.total_in < size) {
977 if (gz->z.total_in + zlib_bufsize > size)
978 gz->z.avail_in = size - gz->z.total_in;
979 else
980 gz->z.avail_in = zlib_bufsize;
982 rc = send_status(ctx, STATUS_COMPRESS, "%li %u",
983 gz->z.total_in, size);
985 if (rc)
986 goto fail;
989 if (gz->z.total_in >= size)
990 cmd = Z_FINISH;
992 break;
993 case Z_STREAM_END:
994 break;
995 default:
996 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gz->z.msg);
997 rc = GPG_ERR_COMPR_ALGO;
998 goto fail;
1000 } while (zrc != Z_STREAM_END);
1002 rc = send_status(ctx, STATUS_COMPRESS, "%li %u", gz->z.total_in, size);
1004 if (rc)
1005 goto fail;
1007 *out = gz->out;
1008 *outsize = gz->z.total_out;
1009 gz->done = TRUE;
1010 pth_cleanup_pop(1);
1011 return 0;
1013 fail:
1014 pth_cleanup_pop(1);
1015 return rc;
1018 #define CRYPTO_BLOCKSIZE(c) (c->blocksize * 1024)
1021 * Useful for a large amount of data. Rather than doing all of the data in one
1022 * iteration do it in chunks. This lets the command be cancelable rather than
1023 * waiting for it to complete.
1025 static gpg_error_t iterate_crypto_once(struct client_s *client,
1026 struct crypto_s *crypto, status_msg_t which)
1028 gpg_error_t rc = 0;
1029 goffset len = CRYPTO_BLOCKSIZE(crypto);
1030 gpointer p = gcry_malloc(len);
1031 goffset total = 0;
1032 gpointer inbuf;
1034 if (!p)
1035 return GPG_ERR_ENOMEM;
1037 if (crypto->insize < CRYPTO_BLOCKSIZE(crypto))
1038 len = crypto->insize;
1040 pth_cleanup_push(gcry_free, p);
1042 for (;;) {
1043 inbuf = (guchar *)crypto->inbuf + total;
1044 guchar *tmp;
1046 if (len + total > crypto->insize)
1047 len = crypto->blocksize;
1049 if (which == STATUS_ENCRYPT)
1050 rc = gcry_cipher_encrypt(crypto->gh, p, len, inbuf, len);
1051 else
1052 rc = gcry_cipher_decrypt(crypto->gh, p, len, inbuf, len);
1054 if (rc)
1055 goto done;
1057 tmp = (guchar *)crypto->inbuf + total;
1058 memmove(tmp, p, len);
1059 total += len;
1061 if (total >= crypto->insize)
1062 break;
1064 pth_cancel_point();
1067 done:
1068 pth_cleanup_pop(1);
1069 return rc;
1072 /* The crypto struct must be setup for iterations and .key. */
1073 gpg_error_t do_xml_encrypt(struct client_s *client,
1074 struct crypto_s *crypto, const gchar *filename)
1076 goffset len = crypto->insize;
1077 gpointer inbuf;
1078 gchar *p;
1079 gpg_error_t rc;
1080 guint64 iter_progress = 0ULL, n_iter = 0ULL, xiter = 0ULL;
1081 gchar tmp[FILENAME_MAX];
1082 struct stat st;
1083 mode_t mode = 0;
1084 gsize hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
1086 if (!crypto->fh->ver.fh2.iter) {
1088 * cache_file_count() needs both .used == TRUE and a valid key in
1089 * order for it to count as a used cache entry. Fixes CACHE status
1090 * messages.
1092 memset(crypto->key, '!', hashlen);
1093 goto write_file;
1097 * Resize the existing xml buffer to the block size required by gcrypt
1098 * rather than duplicating it and wasting memory.
1100 crypto->insize += sizeof(crypto_magic);
1101 len = (crypto->insize / crypto->blocksize) * crypto->blocksize;
1103 if (crypto->insize % crypto->blocksize)
1104 len += crypto->blocksize;
1106 inbuf = gcry_realloc(crypto->inbuf, len);
1108 if (!inbuf) {
1109 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1110 return GPG_ERR_ENOMEM;
1113 guchar *tmpbuf = inbuf;
1114 memmove(&tmpbuf[sizeof(crypto_magic)], tmpbuf, len-sizeof(crypto_magic));
1115 memcpy(tmpbuf, crypto_magic, sizeof(crypto_magic));
1116 crypto->inbuf = tmpbuf;
1117 crypto->insize = len;
1118 gcry_create_nonce(crypto->fh->ver.fh2.iv, crypto->blocksize);
1120 if (crypto->tkey)
1121 gcry_free(crypto->tkey);
1123 crypto->tkey = gcry_malloc(hashlen);
1125 if (!crypto->tkey) {
1126 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1127 return GPG_ERR_ENOMEM;
1130 memcpy(crypto->tkey, crypto->key, hashlen);
1131 guchar *tkey = crypto->tkey;
1132 tkey[0] ^= 1;
1134 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, crypto->keysize))) {
1135 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
1136 return rc;
1139 iter_progress = (guint64)get_key_file_double(
1140 client ? client->filename : "global", "iteration_progress");
1142 if (iter_progress && crypto->fh->ver.fh2.iter >= iter_progress) {
1143 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1144 "0 %llu", crypto->fh->ver.fh2.iter);
1146 if (rc)
1147 return rc;
1150 while (xiter < crypto->fh->ver.fh2.iter-1) {
1151 if (iter_progress > 0ULL && xiter >= iter_progress) {
1152 if (!(xiter % iter_progress)) {
1153 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1154 "%llu %llu", ++n_iter * iter_progress,
1155 crypto->fh->ver.fh2.iter);
1157 if (rc)
1158 return rc;
1162 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->ver.fh2.iv,
1163 crypto->blocksize))) {
1164 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
1165 return rc;
1168 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1170 if (rc) {
1171 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
1172 return rc;
1175 xiter++;
1178 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->fh->ver.fh2.iv,
1179 crypto->blocksize))) {
1180 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
1181 return rc;
1184 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, crypto->keysize))) {
1185 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
1186 return rc;
1189 rc = iterate_crypto_once(client, crypto, STATUS_ENCRYPT);
1191 if (rc)
1192 return rc;
1194 if (iter_progress && crypto->fh->ver.fh2.iter >= iter_progress) {
1195 rc = send_status(client ? client->ctx : NULL, STATUS_ENCRYPT,
1196 "%llu %llu", crypto->fh->ver.fh2.iter, crypto->fh->ver.fh2.iter);
1198 if (rc)
1199 return rc;
1202 write_file:
1203 tmp[0] = 0;
1205 if (filename) {
1206 if (!client && !g_ascii_strcasecmp(filename, "-")) {
1207 crypto->fh->fd = STDOUT_FILENO;
1208 goto do_write_file;
1211 if (lstat(filename, &st) == 0) {
1212 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
1214 if (!(mode & S_IWUSR))
1215 return GPG_ERR_EACCES;
1217 else if (errno != ENOENT)
1218 return gpg_error_from_syserror();
1220 g_snprintf(tmp, sizeof(tmp), "%s.XXXXXX", filename);
1221 crypto->fh->fd = mkstemp(tmp);
1223 if (crypto->fh->fd == -1) {
1224 rc = gpg_error_from_syserror();
1225 p = strrchr(tmp, '/');
1226 p++;
1227 log_write("%s: %s", p, pwmd_strerror(rc));
1228 return rc;
1231 pth_cleanup_push(cleanup_unlink_cb, tmp);
1233 else
1235 * xml_import() or convert_file() from command line.
1237 crypto->fh->fd = STDOUT_FILENO;
1239 do_write_file:
1240 crypto->fh->ver.fh2.magic[0] = '\177';
1241 crypto->fh->ver.fh2.magic[1] = 'P';
1242 crypto->fh->ver.fh2.magic[2] = 'W';
1243 crypto->fh->ver.fh2.magic[3] = 'M';
1244 crypto->fh->ver.fh2.magic[4] = 'D';
1245 crypto->fh->ver.fh2.version = VERSION_HEX;
1246 len = pth_write(crypto->fh->fd, &crypto->fh->ver.fh2, sizeof(crypto->fh->ver.fh2));
1248 if (len != sizeof(crypto->fh->ver.fh2)) {
1249 rc = gpg_error_from_syserror();
1251 if (tmp[0])
1252 pth_cleanup_pop(1);
1254 return rc;
1257 len = pth_write(crypto->fh->fd, crypto->inbuf, crypto->insize);
1259 if (len != crypto->insize) {
1260 rc = gpg_error_from_syserror();
1262 if (tmp[0])
1263 pth_cleanup_pop(1);
1265 return rc;
1268 if (fsync(crypto->fh->fd) == -1) {
1269 rc = gpg_error_from_syserror();
1271 if (tmp[0])
1272 pth_cleanup_pop(1);
1274 return rc;
1277 if (tmp[0]) {
1278 #ifdef WITH_LIBACL
1279 acl_t acl;
1280 #endif
1281 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
1282 gchar tmp2[FILENAME_MAX];
1284 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
1285 #ifdef WITH_LIBACL
1286 acl = acl_get_file(filename, ACL_TYPE_ACCESS);
1288 if (!acl)
1289 log_write("ACL: %s: %s", filename, pwmd_strerror(gpg_error_from_syserror()));
1290 #endif
1292 if (rename(filename, tmp2) == -1) {
1293 rc = gpg_error_from_syserror();
1294 pth_cleanup_pop(1);
1295 #ifdef WITH_LIBACL
1296 if (acl)
1297 acl_free(acl);
1298 #endif
1299 return rc;
1302 #ifdef WITH_LIBACL
1303 else {
1304 acl = acl_get_file(".", ACL_TYPE_DEFAULT);
1306 if (!acl)
1307 log_write("ACL: %s: %s", filename, pwmd_strerror(gpg_error_from_syserror()));
1309 #endif
1311 if (rename(tmp, filename) == -1) {
1312 rc = gpg_error_from_syserror();
1313 pth_cleanup_pop(1);
1314 #ifdef WITH_LIBACL
1315 if (acl)
1316 acl_free(acl);
1317 #endif
1318 return rc;
1321 pth_cleanup_pop(0);
1323 if (mode)
1324 chmod(filename, mode);
1326 #ifdef WITH_LIBACL
1327 if (acl && acl_set_file(filename, ACL_TYPE_ACCESS, acl))
1328 log_write("ACL: %s: %s", filename, pwmd_strerror(gpg_error_from_syserror()));
1330 if (acl)
1331 acl_free(acl);
1332 #endif
1335 if (client && lstat(filename, &st) == 0)
1336 client->mtime = st.st_mtime;
1338 return 0;
1341 gpg_error_t update_save_flags(const gchar *filename,
1342 struct crypto_s *crypto)
1344 gpg_error_t rc;
1345 guint64 iter;
1347 /* New file? */
1348 if (!crypto->fh) {
1349 crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1351 if (!crypto->fh)
1352 return GPG_ERR_ENOMEM;
1355 rc = init_client_crypto2(filename, crypto);
1357 if (rc)
1358 return rc;
1360 if (filename && !crypto->fh->v1) {
1361 iter = (guint64)get_key_file_double(filename, "iterations");
1362 crypto->fh->ver.fh2.iter = iter < 0L ? 0UL : iter;
1365 return 0;
1368 static gpg_error_t save_command_finalize(assuan_context_t ctx, guchar *key,
1369 gboolean cached)
1371 struct client_s *client = assuan_get_pointer(ctx);
1372 gpointer xmlbuf;
1373 gulong outsize = 0;
1374 guint len;
1375 gint clevel;
1376 gint timeout;
1377 gpointer outbuf;
1378 gpg_error_t rc;
1380 if (client->crypto->key && client->crypto->key != key)
1381 gcry_free(client->crypto->key);
1383 client->crypto->key = key;
1384 rc = update_element_mtime(xmlDocGetRootElement(client->doc));
1386 if (rc) {
1387 cleanup_crypto(&client->crypto);
1388 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
1391 xmlDocDumpFormatMemory(client->doc, (xmlChar **)&xmlbuf, (gint *)&len, 0);
1392 pth_cleanup_push(xmlFree, xmlbuf);
1393 clevel = get_key_file_integer(client->filename, "compression_level");
1395 if (clevel < 0)
1396 clevel = 0;
1398 rc = do_compress(ctx, clevel, xmlbuf, len, &outbuf, &outsize);
1400 if (rc) {
1401 pth_cleanup_pop(1);
1402 cleanup_crypto(&client->crypto);
1404 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
1406 else {
1407 pth_cleanup_pop(1);
1408 xmlbuf = outbuf;
1409 len = outsize;
1412 client->crypto->inbuf = xmlbuf;
1413 client->crypto->insize = len;
1414 rc = update_save_flags(client->filename, client->crypto);
1416 if (rc) {
1417 cleanup_crypto(&client->crypto);
1418 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(rc));
1419 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
1422 rc = do_xml_encrypt(client, client->crypto, client->filename);
1424 if (rc) {
1425 cleanup_crypto(&client->crypto);
1426 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
1429 timeout = get_key_file_integer(client->filename, "cache_timeout");
1430 CACHE_LOCK(client->ctx);
1432 if (cached) {
1433 cache_reset_timeout(client->md5file, timeout);
1434 CACHE_UNLOCK;
1436 if (client->new == TRUE)
1437 send_status_all(STATUS_CACHE);
1439 client->new = FALSE;
1440 cleanup_crypto(&client->crypto);
1441 return client->opts & OPT_INQUIRE ? 0 : send_error(ctx, 0);
1444 if (cache_update_key(client->md5file, client->crypto->key) == FALSE) {
1445 CACHE_UNLOCK;
1446 cleanup_crypto(&client->crypto);
1447 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
1450 client->new = FALSE;
1451 cache_reset_timeout(client->md5file, timeout);
1452 CACHE_UNLOCK;
1453 send_status_all(STATUS_CACHE);
1454 cleanup_crypto(&client->crypto);
1455 return client->opts & OPT_INQUIRE ? 0 : send_error(ctx, 0);
1458 static gpg_error_t parse_save_opt_iterations(gpointer data, gpointer v)
1460 struct client_s *client = data;
1461 guint64 n;
1462 gchar *value = v;
1463 gchar *p = NULL;
1465 if (!client->filename)
1466 return EPWMD_NO_FILE;
1468 if (!value || !*value)
1469 return 0;
1471 errno = 0;
1472 n = strtoul(value, &p, 10);
1474 if (errno || (p && *p) || n == G_MAXULONG)
1475 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_ERANGE);
1477 MUTEX_LOCK(&rcfile_mutex);
1478 g_key_file_set_double(keyfileh,
1479 client->filename ? client->filename : "global", "iterations", n);
1480 MUTEX_UNLOCK(&rcfile_mutex);
1482 if (client->filename)
1483 client->opts |= OPT_ITERATIONS;
1485 return 0;
1488 static gpg_error_t parse_save_opt_cipher(gpointer data, gpointer value)
1490 struct client_s *client = data;
1491 const gchar *p = value;
1492 guint64 flags;
1494 if (!client->filename)
1495 return EPWMD_NO_FILE;
1497 if (!p || !*p)
1498 return 0;
1500 flags = pwmd_cipher_str_to_cipher(p);
1502 if (!flags)
1503 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
1505 MUTEX_LOCK(&rcfile_mutex);
1506 g_key_file_set_string(keyfileh, client->filename, "cipher", p);
1507 MUTEX_UNLOCK(&rcfile_mutex);
1509 if (!value)
1510 g_free((gchar *)p);
1512 return 0;
1515 static gpg_error_t parse_save_opt_reset(gpointer data, gpointer value)
1517 struct client_s *client = data;
1519 CACHE_LOCK(client->ctx);
1520 cache_clear(client->md5file, 1);
1521 CACHE_UNLOCK;
1522 return 0;
1525 static gpg_error_t save_command_common(assuan_context_t ctx, gchar *line)
1527 struct client_s *client = assuan_get_pointer(ctx);
1528 gboolean cached = FALSE;
1529 guint hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
1531 CACHE_LOCK(ctx);
1532 cached = cache_iscached(client->md5file);
1533 CACHE_UNLOCK;
1536 * If a cache entry doesn't exist for this file and the file has a
1537 * "key_file" or "key" parameter, then it's an error. The reason is that
1538 * cache expiration would be useless. Unless this is an inquire, then its
1539 * fine.
1541 if (cached == FALSE) {
1542 gchar *tmp = get_key_file_string(client->filename, "key_file");
1544 if (tmp && !(client->opts & OPT_INQUIRE)) {
1545 g_free(tmp);
1546 return send_error(ctx, GPG_ERR_WRONG_KEY_USAGE);
1549 if (tmp)
1550 g_free(tmp);
1553 cached = FALSE;
1555 /* New file? */
1556 if (!client->crypto) {
1557 client->crypto = init_client_crypto();
1559 if (!client->crypto) {
1560 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1561 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
1565 client->crypto->key = gcry_malloc(hashlen);
1567 if (!client->crypto->key) {
1568 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1569 cleanup_crypto(&client->crypto);
1570 return client->opts & OPT_INQUIRE ? GPG_ERR_ENOMEM : send_error(ctx, GPG_ERR_ENOMEM);
1573 memset(client->crypto->key, '!', hashlen);
1575 if (get_key_file_double(client->filename, "iterations") <= 0L &&
1576 (!line || !*line))
1577 goto done;
1579 if (!line || !*line) {
1580 /* It doesn't make sense to use an --inquire with an empty
1581 * passphrase. This will prevent a pinentry dialog. */
1582 if (client->opts & OPT_INQUIRE) {
1583 cleanup_crypto(&client->crypto);
1584 return GPG_ERR_WRONG_KEY_USAGE;
1587 client->crypto->tkey = gcry_malloc(hashlen);
1589 if (!client->crypto->tkey) {
1590 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
1591 cleanup_crypto(&client->crypto);
1592 return send_error(ctx, GPG_ERR_ENOMEM);
1595 memset(client->crypto->tkey, '!', hashlen);
1596 CACHE_LOCK(ctx);
1598 if (cache_get_key(client->md5file, client->crypto->key) == FALSE ||
1599 !memcmp(client->crypto->key, client->crypto->tkey, hashlen)) {
1600 CACHE_UNLOCK;
1602 #ifdef WITH_PINENTRY
1603 gpg_error_t rc;
1605 if (client->pinentry->enable == FALSE ||
1606 get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1607 /* Empty keys are allowed. */
1608 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1609 goto done;
1612 lock_pin_mutex(client);
1613 client->pinentry->which = PINENTRY_SAVE;
1614 rc = pinentry_fork(ctx);
1616 if (rc) {
1617 unlock_pin_mutex(client->pinentry);
1618 cleanup_crypto(&client->crypto);
1619 return send_error(ctx, rc);
1622 client->pinentry->cb = save_command_finalize;
1623 client->pinentry->status = PINENTRY_INIT;
1624 return 0;
1625 #else
1626 /* Empty keys are allowed. */
1627 gcry_md_hash_buffer(GCRY_MD_SHA256, client->crypto->key, "", 1);
1628 goto done;
1629 #endif
1631 else {
1632 CACHE_UNLOCK;
1633 cached = TRUE;
1636 else {
1637 gpg_error_t rc;
1639 if (get_key_file_double(client->filename, "iterations") <= 0L) {
1640 guint64 iter = (guint64)get_key_file_double(NULL, "iterations");
1642 if (iter <= 0L)
1643 iter = 1;
1645 MUTEX_LOCK(&rcfile_mutex);
1646 g_key_file_set_double(keyfileh, client->filename, "iterations", iter);
1647 MUTEX_UNLOCK(&rcfile_mutex);
1648 client->opts |= OPT_ITERATIONS;
1649 rc = send_status(ctx, STATUS_CONFIG, NULL);
1651 if (rc) {
1652 cleanup_crypto(&client->crypto);
1653 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
1657 rc = hash_key(client, line);
1659 if (rc) {
1660 cleanup_crypto(&client->crypto);
1661 return client->opts & OPT_INQUIRE ? rc : send_error(ctx, rc);
1665 done:
1666 return save_command_finalize(ctx, client->crypto->key, cached);
1669 static gint save_command_inquire_finalize(gpointer data, gint assuan_rc,
1670 guchar *line, gsize len)
1672 assuan_context_t ctx = data;
1673 struct client_s *client = assuan_get_pointer(ctx);
1674 gpg_error_t rc = file_modified(client);
1676 if (assuan_rc || rc) {
1677 if (line)
1678 xfree(line);
1680 return assuan_rc ? assuan_rc : rc;
1683 rc = save_command_common(ctx, (gchar *)line);
1685 if (line)
1686 xfree(line);
1688 client->inquire_status = INQUIRE_DONE;
1689 return rc;
1692 static gint save_command(assuan_context_t ctx, gchar *line)
1694 struct stat st;
1695 struct client_s *client = assuan_get_pointer(ctx);
1696 gpg_error_t rc;
1697 struct argv_s *args[] = {
1698 &(struct argv_s) { "iterations", OPTION_TYPE_OPTARG, parse_save_opt_iterations },
1699 &(struct argv_s) { "cipher", OPTION_TYPE_OPTARG, parse_save_opt_cipher },
1700 &(struct argv_s) { "pinentry", OPTION_TYPE_OPTARG, parse_opt_pinentry },
1701 &(struct argv_s) { "reset", OPTION_TYPE_NOARG, parse_save_opt_reset },
1702 &(struct argv_s) { "inquire", OPTION_TYPE_NOARG, parse_opt_inquire },
1703 &(struct argv_s) { "base64", OPTION_TYPE_NOARG, parse_opt_base64 },
1704 NULL
1707 rc = parse_options(&line, args, client);
1709 if (rc)
1710 return send_error(ctx, rc);
1712 if (lstat(client->filename, &st) == -1 && errno != ENOENT)
1713 return send_error(ctx, gpg_error_from_syserror());
1715 if (errno != ENOENT && !S_ISREG(st.st_mode)) {
1716 log_write("%s: %s", client->filename, pwmd_strerror(GPG_ERR_ENOANO));
1717 return send_error(ctx, GPG_ERR_ENOANO);
1720 if ((client->opts & OPT_INQUIRE)) {
1721 rc = assuan_inquire_ext(ctx, "SAVE", 0, save_command_inquire_finalize,
1722 ctx);
1724 if (rc)
1725 return send_error(ctx, rc);
1727 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1728 client->inquire_status = INQUIRE_BUSY;
1729 return 0;
1732 if (line && *line)
1733 log_write2("ARGS=%s", "<passphrase>");
1735 return save_command_common(ctx, line);
1738 static gint delete_command(assuan_context_t ctx, gchar *line)
1740 struct client_s *client = assuan_get_pointer(ctx);
1741 gchar **req;
1742 gpg_error_t rc;
1743 xmlNodePtr n;
1745 log_write2("ARGS=\"%s\"", line);
1747 if (strchr(line, '\t'))
1748 req = split_input_line(line, "\t", -1);
1749 else
1750 req = split_input_line(line, " ", -1);
1752 if (!req || !*req)
1753 return send_error(ctx, GPG_ERR_SYNTAX);
1755 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
1757 if (!n) {
1758 g_strfreev(req);
1759 return send_error(ctx, rc);
1763 * No sub-node defined. Remove the entire node (root element).
1765 if (!req[1]) {
1766 if (n) {
1767 rc = unlink_node(n);
1768 xmlFreeNode(n);
1771 g_strfreev(req);
1772 return send_error(ctx, rc);
1775 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
1776 g_strfreev(req);
1778 if (!n)
1779 return send_error(ctx, rc);
1781 if (n) {
1782 rc = unlink_node(n);
1783 xmlFreeNode(n);
1786 return send_error(ctx, rc);
1790 * Don't return with assuan_process_done() here. This has been called from
1791 * assuan_process_next() and the command should be finished in
1792 * client_thread().
1794 static gint store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1795 gsize len)
1797 assuan_context_t ctx = data;
1798 struct client_s *client = assuan_get_pointer(ctx);
1799 gchar **req;
1800 xmlNodePtr n;
1801 gpg_error_t rc = file_modified(client);
1803 if (assuan_rc || rc) {
1804 if (line)
1805 xfree(line);
1806 return assuan_rc ? assuan_rc : rc;
1809 req = split_input_line((gchar *)line, "\t", 0);
1810 xfree(line);
1812 if (!req || !*req)
1813 return GPG_ERR_SYNTAX;
1815 again:
1816 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
1818 if (rc && rc == GPG_ERR_ELEMENT_NOT_FOUND) {
1819 rc = new_root_element(client->doc, *req);
1821 if (rc) {
1822 g_strfreev(req);
1823 return rc;
1826 goto again;
1829 if (!n) {
1830 g_strfreev(req);
1831 return rc;
1834 if (req[1]) {
1835 if (!n->children)
1836 n = create_elements_cb(n, req+1, &rc, NULL);
1837 else
1838 n = find_elements(client->doc, n->children, req+1, &rc,
1839 NULL, NULL, create_elements_cb, FALSE, 0, NULL, FALSE);
1842 g_strfreev(req);
1843 client->inquire_status = INQUIRE_DONE;
1845 if (!rc)
1846 rc = update_element_mtime(n);
1848 return rc;
1851 static gint store_command(assuan_context_t ctx, gchar *line)
1853 struct client_s *client = assuan_get_pointer(ctx);
1854 gpg_error_t rc;
1856 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1858 if (rc)
1859 return send_error(ctx, rc);
1861 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1862 client->inquire_status = INQUIRE_BUSY;
1863 return 0;
1866 static void *send_data_cb(void *arg)
1868 struct assuan_cmd_s *data = arg;
1869 gint old;
1870 gpg_error_t *rc;
1872 pth_cancel_state(PTH_CANCEL_ENABLE|PTH_CANCEL_ASYNCHRONOUS, &old);
1873 rc = g_malloc(sizeof(gpg_error_t));
1874 *rc = assuan_send_data(data->ctx, data->line, data->line_len);
1875 pth_cancel_state(old, NULL);
1876 pth_exit(rc);
1877 return NULL;
1880 /* For every assuan command that needs to be sent to the client, a timeout is
1881 * needed to determine if the client lost the connection. The timeout is the
1882 * same as the "keepalive" configuration parameter or a default if unset.
1884 gpg_error_t do_assuan_command(assuan_context_t ctx,
1885 void *(*cb)(void *data), void *data)
1887 pth_attr_t attr = pth_attr_new();
1888 pth_t tid;
1889 gint to = get_key_file_integer("global", "keepalive");
1890 pth_event_t ev, tev;
1891 pth_status_t st;
1892 gpg_error_t rc = 0;
1893 void *p;
1895 pth_attr_init(attr);
1896 pth_attr_set(attr, PTH_ATTR_JOINABLE, TRUE);
1897 tid = pth_spawn(attr, cb, data);
1898 rc = gpg_error_from_syserror();
1899 pth_attr_destroy(attr);
1901 if (!tid) {
1902 log_write("%s(%i): pth_spawn(): %s", __FILE__, __LINE__, pwmd_strerror(rc));
1903 return rc;
1906 pth_cleanup_push(cleanup_cancel_cb, tid);
1907 to = to <= 0 ? DEFAULT_KEEPALIVE_TO : to;
1908 ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD, tid);
1909 tev = to ? pth_event(PTH_EVENT_TIME, pth_timeout(to, 0)) : NULL;
1910 ev = pth_event_concat(ev, tev, NULL);
1911 pth_cleanup_push(cleanup_ev_cb, ev);
1912 pth_yield(tid);
1913 pth_wait(ev);
1915 if (tev) {
1916 st = pth_event_status(tev);
1918 if (st == PTH_STATUS_OCCURRED) {
1919 pth_cleanup_pop(1);
1920 pth_cleanup_pop(1);
1921 return GPG_ERR_TIMEOUT;
1925 st = pth_event_status(ev);
1927 if (st == PTH_STATUS_FAILED) {
1928 pth_cancel(tid);
1929 pth_join(tid, &p);
1930 g_free(p);
1931 rc = GPG_ERR_ASS_WRITE_ERROR;
1933 else if (st == PTH_STATUS_OCCURRED) {
1934 pth_join(tid, &p);
1935 rc = *(gpg_error_t *)p;
1936 g_free(p);
1939 pth_cleanup_pop(1);
1940 pth_cleanup_pop(0);
1941 return rc;
1944 static gpg_error_t xfer_data(assuan_context_t ctx, const gchar *line,
1945 gint total)
1947 gint to_send;
1948 gint sent = 0;
1949 gpg_error_t rc;
1950 struct assuan_cmd_s data;
1951 gint progress = get_key_file_integer("global", "xfer_progress");
1952 gint flush = 0;
1954 progress = progress>0 ? (progress/ASSUAN_LINELENGTH)*ASSUAN_LINELENGTH : 0;
1955 to_send = total < ASSUAN_LINELENGTH ? total : ASSUAN_LINELENGTH;
1956 data.ctx = ctx;
1957 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1959 if (rc)
1960 return rc;
1962 again:
1963 do {
1964 if (sent + to_send > total)
1965 to_send = total - sent;
1967 data.line = flush ? NULL : (gchar *)line+sent;
1968 data.line_len = flush ? 0 : to_send;
1969 rc = do_assuan_command(ctx, send_data_cb, &data);
1971 if (!rc) {
1972 sent += flush ? 0 : to_send;
1974 if ((progress && !(sent % progress) && sent != total) ||
1975 (sent == total && flush))
1976 rc = send_status(ctx, STATUS_XFER, "%li %li", sent, total);
1978 if (!flush && !rc && sent == total) {
1979 flush = 1;
1980 goto again;
1983 } while (!rc && sent < total);
1985 return rc;
1988 static gint get_command(assuan_context_t ctx, gchar *line)
1990 struct client_s *client = assuan_get_pointer(ctx);
1991 gchar **req;
1992 gpg_error_t rc;
1993 xmlNodePtr n;
1995 log_write2("ARGS=\"%s\"", line);
1996 req = split_input_line(line, "\t", -1);
1998 if (!req || !*req) {
1999 g_strfreev(req);
2000 return send_error(ctx, GPG_ERR_SYNTAX);
2003 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
2005 if (!n) {
2006 g_strfreev(req);
2007 return send_error(ctx, rc);
2010 if (req[1])
2011 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
2013 g_strfreev(req);
2015 if (rc)
2016 return send_error(ctx, rc);
2018 if (!n || !n->children)
2019 return send_error(ctx, GPG_ERR_NO_VALUE);
2021 n = find_text_node(n->children);
2023 if (!n || !n->content || !*n->content)
2024 return send_error(ctx, GPG_ERR_NO_VALUE);
2026 rc = xfer_data(ctx, (gchar *)n->content, xmlStrlen(n->content));
2027 return send_error(ctx, rc);
2030 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
2031 gpg_error_t *rc, gchar **req_orig, void *data)
2033 gchar *path = *(gchar **)data;
2034 gchar *tmp = NULL, *result;
2036 if (path) {
2037 g_free(path);
2038 *(gchar **)data = NULL;
2041 path = g_strjoinv("\t", target);
2043 if (!path) {
2044 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2045 *rc = GPG_ERR_ENOMEM;
2046 return NULL;
2049 if (req_orig) {
2050 tmp = g_strjoinv("\t", req_orig);
2052 if (!tmp) {
2053 g_free(path);
2054 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2055 *rc = GPG_ERR_ENOMEM;
2056 return NULL;
2060 if (tmp && *tmp)
2061 result = g_strdup_printf("%s\t%s", path, tmp);
2062 else
2063 result = g_strdup(path);
2065 if (!result) {
2066 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2067 *rc = GPG_ERR_ENOMEM;
2068 g_free(path);
2069 g_free(tmp);
2070 return NULL;
2073 g_free(path);
2074 g_free(tmp);
2075 *(gchar **)data = result;
2076 return node;
2079 static void list_command_cleanup1(void *arg);
2080 static gint realpath_command(assuan_context_t ctx, gchar *line)
2082 gpg_error_t rc;
2083 struct client_s *client = assuan_get_pointer(ctx);
2084 gchar **req;
2085 gchar *t;
2086 gint i;
2087 xmlNodePtr n;
2088 GString *string;
2089 gchar *rp = NULL;
2091 log_write2("ARGS=\"%s\"", line);
2093 if (strchr(line, '\t') != NULL) {
2094 if ((req = split_input_line(line, "\t", 0)) == NULL)
2095 return send_error(ctx, GPG_ERR_SYNTAX);
2097 else {
2098 if ((req = split_input_line(line, " ", 0)) == NULL)
2099 return send_error(ctx, GPG_ERR_SYNTAX);
2102 n = find_root_element(client->doc, &req, &rc, NULL, 0, FALSE);
2104 if (!n) {
2105 g_strfreev(req);
2106 return send_error(ctx, rc);
2109 rp = g_strjoinv("\t", req);
2111 if (!rp) {
2112 g_strfreev(req);
2113 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2114 return send_error(ctx, GPG_ERR_ENOMEM);
2117 if (req[1]) {
2118 n = find_elements(client->doc, n->children, req+1, &rc,
2119 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp, FALSE);
2121 if (!n) {
2122 g_free(rp);
2123 g_strfreev(req);
2124 return send_error(ctx, rc);
2128 string = g_string_new(rp);
2129 g_free(rp);
2130 g_strfreev(req);
2132 if (!string) {
2133 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2134 return send_error(ctx, GPG_ERR_ENOMEM);
2137 again:
2138 for (i = 0, t = string->str + i; *t; t++, i++) {
2139 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
2140 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
2141 goto again;
2145 pth_cleanup_push(list_command_cleanup1, string);
2146 rc = xfer_data(ctx, string->str, string->len);
2147 pth_cleanup_pop(1);
2148 return send_error(ctx, rc);
2151 static void list_command_cleanup1(void *arg)
2153 g_string_free((GString *)arg, TRUE);
2156 static void list_command_cleanup2(void *arg)
2158 struct element_list_s *elements = arg;
2160 if (elements) {
2161 if (elements->list) {
2162 gint total = g_slist_length(elements->list);
2163 gint i;
2165 for (i = 0; i < total; i++) {
2166 gchar *tmp = g_slist_nth_data(elements->list, i);
2167 g_free(tmp);
2170 g_slist_free(elements->list);
2173 if (elements->prefix)
2174 g_free(elements->prefix);
2176 if (elements->req)
2177 g_strfreev(elements->req);
2179 g_free(elements);
2183 static gpg_error_t parse_list_opt_norecurse(gpointer data, gpointer value)
2185 struct element_list_s *elements = data;
2187 elements->recurse = FALSE;
2188 return 0;
2191 static gpg_error_t parse_list_opt_verbose(gpointer data, gpointer value)
2193 struct element_list_s *elements = data;
2195 elements->verbose = TRUE;
2196 return 0;
2199 static gint list_command(assuan_context_t ctx, gchar *line)
2201 struct client_s *client = assuan_get_pointer(ctx);
2202 gpg_error_t rc;
2203 struct element_list_s *elements = NULL;
2204 gchar *tmp;
2205 struct argv_s *args[] = {
2206 &(struct argv_s) { "no-recurse", OPTION_TYPE_NOARG, parse_list_opt_norecurse },
2207 &(struct argv_s) { "verbose", OPTION_TYPE_NOARG, parse_list_opt_verbose },
2208 NULL
2211 if (disable_list_and_dump == TRUE)
2212 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2214 elements = g_malloc0(sizeof(struct element_list_s));
2216 if (!elements) {
2217 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2218 return GPG_ERR_ENOMEM;
2221 elements->recurse = TRUE; // default
2222 pth_cleanup_push(list_command_cleanup2, elements);
2223 rc = parse_options(&line, args, elements);
2225 if (rc)
2226 goto fail;
2228 if (!*line) {
2229 GString *str;
2231 rc = list_root_elements(client->doc, &str, elements->verbose);
2233 if (rc) {
2234 pth_cleanup_pop(1);
2235 return send_error(ctx, rc);
2238 pth_cleanup_push(list_command_cleanup1, str);
2239 rc = xfer_data(ctx, str->str, str->len);
2240 pth_cleanup_pop(1);
2241 pth_cleanup_pop(1);
2242 return send_error(ctx, rc);
2245 elements->req = split_input_line(line, " ", 0);
2247 if (!elements->req)
2248 strv_printf(&elements->req, "%s", line);
2250 rc = create_path_list(client->doc, elements, *elements->req);
2252 if (rc)
2253 goto fail;
2255 if (elements) {
2256 gint total = g_slist_length(elements->list);
2257 gint i;
2258 GString *str;
2260 if (!total) {
2261 rc = GPG_ERR_NO_VALUE;
2262 goto fail;
2265 str = g_string_new(NULL);
2267 if (!str) {
2268 rc = GPG_ERR_ENOMEM;
2269 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2270 goto fail;
2273 for (i = 0; i < total; i++) {
2274 tmp = g_slist_nth_data(elements->list, i);
2275 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
2278 pth_cleanup_push(list_command_cleanup1, str);
2279 rc = xfer_data(ctx, str->str, str->len);
2280 pth_cleanup_pop(1);
2282 else
2283 rc = GPG_ERR_NO_VALUE;
2285 fail:
2286 pth_cleanup_pop(1);
2287 return send_error(ctx, rc);
2291 * req[0] - element path
2293 static gpg_error_t attribute_list(assuan_context_t ctx, gchar **req)
2295 struct client_s *client = assuan_get_pointer(ctx);
2296 gchar **attrlist = NULL;
2297 gint i = 0;
2298 gchar **path = NULL;
2299 xmlAttrPtr a;
2300 xmlNodePtr n, an;
2301 gchar *line;
2302 gpg_error_t rc;
2304 if (!req || !req[0])
2305 return GPG_ERR_SYNTAX;
2307 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2309 * The first argument may be only a root element.
2311 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2312 return GPG_ERR_SYNTAX;
2315 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
2317 if (!n) {
2318 g_strfreev(path);
2319 return rc;
2322 if (path[1]) {
2323 n = find_elements(client->doc, n->children, path+1, &rc,
2324 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
2326 if (!n) {
2327 g_strfreev(path);
2328 return rc;
2332 g_strfreev(path);
2334 for (a = n->properties; a; a = a->next) {
2335 gchar **pa;
2337 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
2338 if (attrlist)
2339 g_strfreev(attrlist);
2341 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2342 return GPG_ERR_ENOMEM;
2345 attrlist = pa;
2346 an = a->children;
2347 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name,
2348 an && an->content ? (gchar *)an->content : "");
2350 if (!attrlist[i]) {
2351 g_strfreev(attrlist);
2352 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2353 return GPG_ERR_ENOMEM;
2356 attrlist[++i] = NULL;
2359 if (!attrlist)
2360 return GPG_ERR_NO_VALUE;
2362 line = g_strjoinv("\n", attrlist);
2364 if (!line) {
2365 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2366 g_strfreev(attrlist);
2367 return GPG_ERR_ENOMEM;
2370 pth_cleanup_push(g_free, line);
2371 pth_cleanup_push(req_cleanup, attrlist);
2372 rc = xfer_data(ctx, line, strlen(line));
2373 pth_cleanup_pop(1);
2374 pth_cleanup_pop(1);
2375 return rc;
2379 * req[0] - attribute
2380 * req[1] - element path
2382 static gpg_error_t attribute_delete(struct client_s *client, gchar **req)
2384 xmlNodePtr n;
2385 gchar **path = NULL;
2386 gpg_error_t rc;
2388 if (!req || !req[0] || !req[1])
2389 return GPG_ERR_SYNTAX;
2391 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2393 * The first argument may be only a root element.
2395 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2396 return GPG_ERR_SYNTAX;
2400 * Don't remove the "_name" attribute for the root element. To remove an
2401 * root element use DELETE <name>.
2403 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"_name")) {
2404 rc = GPG_ERR_SYNTAX;
2405 goto fail;
2408 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
2410 if (!n)
2411 goto fail;
2413 if (path[1]) {
2414 n = find_elements(client->doc, n->children, path+1, &rc,
2415 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
2417 if (!n)
2418 goto fail;
2421 rc = delete_attribute(n, (xmlChar *)req[0]);
2423 fail:
2424 g_strfreev(path);
2425 return rc;
2428 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
2429 gpg_error_t *rc)
2431 gchar **src = *path;
2432 gchar **src_orig = g_strdupv(src);
2433 xmlNodePtr n = NULL;
2435 *rc = 0;
2437 if (!src_orig) {
2438 *rc = GPG_ERR_ENOMEM;
2439 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2440 goto fail;
2443 again:
2444 n = find_root_element(client->doc, &src, rc, NULL, 0, FALSE);
2446 if (!n) {
2447 if (*rc == GPG_ERR_ELEMENT_NOT_FOUND) {
2448 *rc = new_root_element(client->doc, src[0]);
2450 if (*rc)
2451 goto fail;
2453 goto again;
2455 else
2456 goto fail;
2459 if (src[1]) {
2460 if (!n->children)
2461 n = create_target_elements_cb(n, src+1, rc, NULL);
2462 else
2463 n = find_elements(client->doc, n->children, src+1, rc,
2464 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL, FALSE);
2466 if (!n)
2467 goto fail;
2470 * Reset the position of the element tree now that the elements
2471 * have been created.
2473 g_strfreev(src);
2474 src = src_orig;
2475 src_orig = NULL;
2476 n = find_root_element(client->doc, &src, rc, NULL, 0, FALSE);
2478 if (!n)
2479 goto fail;
2481 n = find_elements(client->doc, n->children, src+1, rc,
2482 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
2484 if (!n)
2485 goto fail;
2488 fail:
2489 if (src_orig)
2490 g_strfreev(src_orig);
2492 *path = src;
2493 return n;
2497 * Creates a "target" attribute. When other commands encounter an element with
2498 * this attribute, the element path is modified to the target value. If the
2499 * source element path doesn't exist when using 'ATTR SET target', it is
2500 * created, but the destination element path must exist.
2502 * req[0] - source element path
2503 * req[1] - destination element path
2505 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
2507 gchar **src, **dst, *line = NULL, **odst = NULL;
2508 gpg_error_t rc;
2509 xmlNodePtr n;
2511 if (!req || !req[0] || !req[1])
2512 return GPG_ERR_SYNTAX;
2514 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
2516 * The first argument may be only a root element.
2518 if ((src = split_input_line(req[0], " ", 0)) == NULL)
2519 return GPG_ERR_SYNTAX;
2522 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
2524 * The first argument may be only a root element.
2526 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
2527 rc = GPG_ERR_SYNTAX;
2528 goto fail;
2532 odst = g_strdupv(dst);
2534 if (!odst) {
2535 rc = GPG_ERR_ENOMEM;
2536 goto fail;
2539 n = find_root_element(client->doc, &dst, &rc, NULL, 0, FALSE);
2542 * Make sure the destination element path exists.
2544 if (!n)
2545 goto fail;
2547 if (dst[1]) {
2548 n = find_elements(client->doc, n->children, dst+1, &rc,
2549 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
2551 if (!n)
2552 goto fail;
2555 n = create_element_path(client, &src, &rc);
2557 if (rc)
2558 goto fail;
2560 line = g_strjoinv("\t", odst);
2562 if (!line) {
2563 rc = GPG_ERR_ENOMEM;
2564 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2565 goto fail;
2568 rc = add_attribute(n, "target", line);
2570 fail:
2571 g_free(line);
2572 g_strfreev(src);
2573 g_strfreev(dst);
2574 g_strfreev(odst);
2575 return rc;
2579 * req[0] - name
2580 * req[1] - new name
2582 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
2584 gpg_error_t rc;
2585 gchar **tmp;
2586 xmlNodePtr n;
2588 tmp = g_strdupv(req);
2590 if (!tmp) {
2591 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2592 return GPG_ERR_ENOMEM;
2595 n = find_root_element(client->doc, &tmp, &rc, NULL, 0, FALSE);
2596 g_strfreev(tmp);
2598 if (!n)
2599 return rc;
2601 if (g_utf8_collate(req[0], req[1]) == 0)
2602 return 0;
2605 * Will not overwrite an existing root.
2607 tmp = g_strdupv(req+1);
2609 if (!tmp) {
2610 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2611 return GPG_ERR_ENOMEM;
2614 n = find_root_element(client->doc, &tmp, &rc, NULL, 0, FALSE);
2615 g_strfreev(tmp);
2617 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND)
2618 return rc;
2620 if (n)
2621 return GPG_ERR_AMBIGUOUS_NAME;
2623 if (!valid_xml_element((xmlChar *)req[1]))
2624 return GPG_ERR_SYNTAX;
2626 tmp = g_strdupv(req);
2628 if (!tmp) {
2629 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2630 return GPG_ERR_ENOMEM;
2633 n = find_root_element(client->doc, &tmp, &rc, NULL, 0, FALSE);
2634 g_strfreev(tmp);
2636 if (!n)
2637 return GPG_ERR_ELEMENT_NOT_FOUND;
2639 return add_attribute(n, "_name", req[1]);
2643 * req[0] - attribute
2644 * req[1] - element path
2646 static gpg_error_t attribute_get(assuan_context_t ctx, gchar **req)
2648 struct client_s *client = assuan_get_pointer(ctx);
2649 xmlNodePtr n;
2650 xmlChar *a;
2651 gchar **path= NULL;
2652 gpg_error_t rc;
2654 if (!req || !req[0] || !req[1])
2655 return GPG_ERR_SYNTAX;
2657 if (strchr(req[1], '\t')) {
2658 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
2659 return GPG_ERR_SYNTAX;
2661 else {
2662 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2663 return GPG_ERR_SYNTAX;
2666 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
2668 if (!n)
2669 goto fail;
2671 if (path[1]) {
2672 n = find_elements(client->doc, n->children, path+1, &rc,
2673 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
2675 if (!n)
2676 goto fail;
2679 g_strfreev(path);
2681 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
2682 return GPG_ERR_NOT_FOUND;
2684 pth_cleanup_push(xmlFree, a);
2686 if (*a)
2687 rc = xfer_data(ctx, (gchar *)a, xmlStrlen(a));
2688 else
2689 rc = GPG_ERR_NO_VALUE;
2691 pth_cleanup_pop(1);
2692 return rc;
2694 fail:
2695 g_strfreev(path);
2696 return rc;
2700 * req[0] - attribute
2701 * req[1] - element path
2702 * req[2] - value
2704 static gpg_error_t attribute_set(struct client_s *client, gchar **req)
2706 gchar **path = NULL;
2707 gpg_error_t rc;
2708 xmlNodePtr n;
2710 if (!req || !req[0] || !req[1])
2711 return GPG_ERR_SYNTAX;
2714 * Reserved attribute names.
2716 if (!strcmp(req[0], "_name")) {
2718 * Only reserved for the root element. Not the rest of the
2719 * document.
2721 if (strchr(req[1], '\t') == NULL)
2722 return name_attribute(client, req + 1);
2724 else if (!strcmp(req[0], "target"))
2725 return target_attribute(client, req + 1);
2727 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2729 * The first argument may be only a root element.
2731 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2732 return GPG_ERR_SYNTAX;
2735 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
2737 if (!n)
2738 goto fail;
2740 if (path[1]) {
2741 n = find_elements(client->doc, n->children, path+1, &rc,
2742 NULL, NULL, NULL, FALSE, 0, NULL, FALSE);
2744 if (!n)
2745 goto fail;
2748 rc = add_attribute(n, req[0], req[2]);
2750 fail:
2751 g_strfreev(path);
2752 return rc;
2756 * req[0] - command
2757 * req[1] - attribute name or element path if command is LIST
2758 * req[2] - element path
2759 * req[2] - element path or value
2761 static gint attr_command(assuan_context_t ctx, gchar *line)
2763 struct client_s *client = assuan_get_pointer(ctx);
2764 gchar **req;
2765 gpg_error_t rc = 0;
2767 log_write2("ARGS=\"%s\"", line);
2768 req = split_input_line(line, " ", 4);
2770 if (!req || !req[0] || !req[1]) {
2771 g_strfreev(req);
2772 return send_error(ctx, GPG_ERR_SYNTAX);
2775 pth_cleanup_push(req_cleanup, req);
2777 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2778 rc = attribute_set(client, req+1);
2779 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2780 rc = attribute_get(ctx, req+1);
2781 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2782 rc = attribute_delete(client, req+1);
2783 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2784 rc = attribute_list(ctx, req+1);
2785 else
2786 rc = GPG_ERR_SYNTAX;
2788 pth_cleanup_pop(1);
2789 return send_error(ctx, rc);
2792 static gint iscached_command(assuan_context_t ctx, gchar *line)
2794 gchar **req = split_input_line(line, " ", 0);
2795 guchar md5file[16];
2796 gchar *path, *tmp;
2798 if (!req || !*req) {
2799 g_strfreev(req);
2800 return send_error(ctx, GPG_ERR_SYNTAX);
2803 log_write2("ARGS=\"%s\"", line);
2805 if (!valid_filename(req[0])) {
2806 g_strfreev(req);
2807 return GPG_ERR_INV_VALUE;
2810 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2811 CACHE_LOCK(ctx);
2813 if (cache_iscached(md5file)) {
2814 g_strfreev(req);
2815 CACHE_UNLOCK;
2816 return send_error(ctx, 0);
2819 CACHE_UNLOCK;
2820 tmp = get_key_file_string("global", "data_directory");
2822 if (!tmp) {
2823 g_strfreev(req);
2824 return GPG_ERR_ENOMEM;
2827 path = expand_homedir(tmp);
2829 if (!path) {
2830 g_strfreev(req);
2831 g_free(tmp);
2832 return GPG_ERR_ENOMEM;
2835 g_free(tmp);
2836 tmp = path;
2837 path = g_strdup_printf("%s/%s", tmp, req[0]);
2838 g_free(tmp);
2840 if (!path) {
2841 g_strfreev(req);
2842 return GPG_ERR_ENOMEM;
2845 if (access(path, R_OK) == -1) {
2846 gpg_error_t rc = gpg_error_from_syserror();
2848 g_free(path);
2849 g_strfreev(req);
2850 return send_error(ctx, rc);
2853 g_free(path);
2854 return send_error(ctx, GPG_ERR_NOT_FOUND);
2857 static gint clearcache_command(assuan_context_t ctx, gchar *line)
2859 gchar **req = split_input_line(line, " ", 0);
2860 guchar md5file[16];
2862 log_write2("ARGS=\"%s\"", line);
2863 CACHE_LOCK(ctx);
2865 if (!req || !*req) {
2866 g_strfreev(req);
2867 cache_clear(NULL, 2);
2868 CACHE_UNLOCK;
2869 return send_error(ctx, 0);
2872 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2873 g_strfreev(req);
2874 (void)cache_clear(md5file, 1);
2875 CACHE_UNLOCK;
2876 return send_error(ctx, 0);
2879 static gint cachetimeout_command(assuan_context_t ctx, gchar *line)
2881 guchar md5file[16];
2882 glong timeout;
2883 gchar **req = split_input_line(line, " ", 0);
2884 gchar *p;
2886 if (!req || !*req || !req[1]) {
2887 g_strfreev(req);
2888 return send_error(ctx, GPG_ERR_SYNTAX);
2891 errno = 0;
2892 timeout = strtol(req[1], &p, 10);
2894 if (errno != 0 || *p != 0 || timeout < -1) {
2895 g_strfreev(req);
2896 return send_error(ctx, GPG_ERR_SYNTAX);
2899 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2900 CACHE_LOCK(client->ctx);
2902 if (cache_set_timeout(md5file, timeout) == FALSE) {
2903 CACHE_UNLOCK;
2904 return send_error(ctx, GPG_ERR_NOT_FOUND);
2907 CACHE_UNLOCK;
2908 return send_error(ctx, 0);
2911 static gint dump_command(assuan_context_t ctx, gchar *line)
2913 xmlChar *xml;
2914 gint len;
2915 struct client_s *client = assuan_get_pointer(ctx);
2916 gpg_error_t rc;
2918 if (disable_list_and_dump == TRUE)
2919 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2921 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2923 if (!xml) {
2924 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2925 return send_error(ctx, GPG_ERR_ENOMEM);
2928 pth_cleanup_push(xmlFree, xml);
2929 rc = xfer_data(ctx, (gchar *)xml, len);
2930 pth_cleanup_pop(1);
2931 return send_error(ctx, rc);
2934 static gint getconfig_command(assuan_context_t ctx, gchar *line)
2936 struct client_s *client = assuan_get_pointer(ctx);
2937 gpg_error_t rc = 0;
2938 gchar filename[255]={0}, param[747]={0};
2939 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2941 log_write2("ARGS=\"%s\"", line);
2943 if (!line || !*line)
2944 return send_error(ctx, GPG_ERR_SYNTAX);
2946 if (strchr(line, ' ')) {
2947 sscanf(line, " %254[^ ] %746c", filename, param);
2948 paramp = param;
2949 fp = filename;
2952 if (fp && !valid_filename(fp))
2953 return send_error(ctx, GPG_ERR_INV_VALUE);
2955 paramp = g_ascii_strdown(paramp, -1);
2957 if (!paramp) {
2958 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
2959 return send_error(ctx, GPG_ERR_ENOMEM);
2962 if (fp && !g_ascii_strcasecmp(paramp, "iterations")) {
2963 if (!(client->opts & OPT_ITERATIONS) || fp != client->filename) {
2964 file_header_internal_t *fh = read_file_header(fp, FALSE, &rc);
2966 if (!fh && rc != GPG_ERR_ENOENT)
2967 return send_error(ctx, rc);
2969 if (!rc) {
2970 g_free(paramp);
2971 p = g_strdup_printf("%lu", (unsigned long)fh->ver.fh2.iter);
2972 close_file_header(fh);
2974 if (!p) {
2975 log_write("%s(%i): %s", __FILE__, __LINE__,
2976 pwmd_strerror(GPG_ERR_ENOMEM));
2977 return send_error(ctx, GPG_ERR_ENOMEM);
2980 goto done;
2984 else if (!g_ascii_strcasecmp(paramp, "enable_pinentry")) {
2985 #ifdef WITH_PINENTRY
2986 gboolean n;
2988 if (fp == client->filename && (client->opts & OPT_PINENTRY))
2989 n = client->pinentry->enable;
2990 else
2991 n = get_key_file_boolean(fp, "enable_pinentry");
2993 p = g_strdup_printf("%s", n ? "true" : "false");
2995 if (!p) {
2996 log_write("%s(%i): %s", __FILE__, __LINE__,
2997 pwmd_strerror(GPG_ERR_ENOMEM));
2998 return send_error(ctx, GPG_ERR_ENOMEM);
3001 goto done;
3002 #else
3003 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
3004 #endif
3006 else if (!g_ascii_strcasecmp(paramp, "pinentry_timeout")) {
3007 #ifdef WITH_PINENTRY
3008 p = g_strdup_printf("%i", get_key_file_integer(fp, "pinentry_timeout"));
3010 if (!p) {
3011 log_write("%s(%i): %s", __FILE__, __LINE__,
3012 pwmd_strerror(GPG_ERR_ENOMEM));
3013 return send_error(ctx, GPG_ERR_ENOMEM);
3016 goto done;
3017 #else
3018 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
3019 #endif
3022 p = get_key_file_string(fp ? fp : "global", paramp);
3023 g_free(paramp);
3025 if (!p)
3026 return send_error(ctx, GPG_ERR_UNKNOWN_OPTION);
3028 tmp = expand_homedir(p);
3029 g_free(p);
3031 if (!tmp) {
3032 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
3033 return send_error(ctx, GPG_ERR_ENOMEM);
3036 p = tmp;
3037 done:
3038 pth_cleanup_push(g_free, p);
3039 rc = xfer_data(ctx, p, strlen(p));
3040 pth_cleanup_pop(1);
3041 return send_error(ctx, rc);
3044 struct xpath_s {
3045 xmlXPathContextPtr xp;
3046 xmlXPathObjectPtr result;
3047 xmlBufferPtr buf;
3048 gchar **req;
3051 static void xpath_command_cleanup(void *arg)
3053 struct xpath_s *xpath = arg;
3055 req_cleanup(xpath->req);
3057 if (xpath->buf)
3058 xmlBufferFree(xpath->buf);
3060 if (xpath->result)
3061 xmlXPathFreeObject(xpath->result);
3063 if (xpath->xp)
3064 xmlXPathFreeContext(xpath->xp);
3067 static gint xpath_command(assuan_context_t ctx, gchar *line)
3069 struct client_s *client = assuan_get_pointer(ctx);
3070 gpg_error_t rc;
3071 struct xpath_s xpath;
3073 log_write2("ARGS=\"%s\"", line);
3075 if (disable_list_and_dump == TRUE)
3076 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
3078 if (!line || !*line)
3079 return send_error(ctx, GPG_ERR_SYNTAX);
3081 memset(&xpath, 0, sizeof(struct xpath_s));
3083 if ((xpath.req = split_input_line(line, "\t", 2)) == NULL) {
3084 if (strv_printf(&xpath.req, "%s", line) == FALSE)
3085 return send_error(ctx, GPG_ERR_ENOMEM);
3088 xpath.xp = xmlXPathNewContext(client->doc);
3090 if (!xpath.xp) {
3091 xpath_command_cleanup(&xpath);
3092 return send_error(ctx, EPWMD_LIBXML_ERROR);
3095 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
3097 if (!xpath.result) {
3098 xpath_command_cleanup(&xpath);
3099 return send_error(ctx, EPWMD_LIBXML_ERROR);
3102 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
3103 rc = GPG_ERR_ELEMENT_NOT_FOUND;
3104 goto fail;
3107 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
3108 (xmlChar *)xpath.req[1], &xpath.buf, 0, NULL);
3110 if (rc)
3111 goto fail;
3112 else if (!xpath.req[1] && !xmlBufferLength(xpath.buf)) {
3113 rc = GPG_ERR_NO_VALUE;
3114 goto fail;
3116 else if (xpath.req[1])
3117 goto fail;
3119 pth_cleanup_push(xpath_command_cleanup, &xpath);
3120 rc = xfer_data(ctx, (gchar *)xmlBufferContent(xpath.buf),
3121 xmlBufferLength(xpath.buf));
3122 pth_cleanup_pop(0);
3124 fail:
3125 xpath_command_cleanup(&xpath);
3126 return send_error(ctx, rc);
3129 /* XPATHATTR SET|DELETE <name> <expression>[<TAB>[value]] */
3130 static gint xpathattr_command(assuan_context_t ctx, gchar *line)
3132 struct client_s *client = assuan_get_pointer(ctx);
3133 gpg_error_t rc;
3134 struct xpath_s xpath;
3135 gchar **req = NULL;
3136 gboolean cmd = FALSE; //SET
3138 log_write2("ARGS=\"%s\"", line);
3140 if (disable_list_and_dump == TRUE)
3141 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
3143 if (!line || !*line)
3144 return send_error(ctx, GPG_ERR_SYNTAX);
3146 memset(&xpath, 0, sizeof(struct xpath_s));
3148 if ((req = split_input_line(line, " ", 3)) == NULL)
3149 return send_error(ctx, GPG_ERR_ENOMEM);
3151 if (!req[0]) {
3152 rc = GPG_ERR_SYNTAX;
3153 goto fail;
3156 if (!g_ascii_strcasecmp(req[0], "SET"))
3157 cmd = FALSE;
3158 else if (!g_ascii_strcasecmp(req[0], "DELETE"))
3159 cmd = TRUE;
3160 else {
3161 rc = GPG_ERR_SYNTAX;
3162 goto fail;
3165 if (!req[1] || !req[2]) {
3166 rc = GPG_ERR_SYNTAX;
3167 goto fail;
3170 if ((xpath.req = split_input_line(req[2], "\t", 3)) == NULL) {
3171 rc = GPG_ERR_ENOMEM;
3172 goto fail;
3175 if (!xpath.req[0] || (!xpath.req[1] && !cmd) || (xpath.req[1] && cmd)) {
3176 rc = GPG_ERR_SYNTAX;
3177 goto fail;
3180 xpath.xp = xmlXPathNewContext(client->doc);
3182 if (!xpath.xp) {
3183 rc = EPWMD_LIBXML_ERROR;
3184 goto fail;
3187 xpath.result = xmlXPathEvalExpression((xmlChar *)xpath.req[0], xpath.xp);
3189 if (!xpath.result) {
3190 rc = EPWMD_LIBXML_ERROR;
3191 goto fail;
3194 if (xmlXPathNodeSetIsEmpty(xpath.result->nodesetval)) {
3195 rc = GPG_ERR_ELEMENT_NOT_FOUND;
3196 goto fail;
3199 rc = recurse_xpath_nodeset(client->doc, xpath.result->nodesetval,
3200 (xmlChar *)xpath.req[1], &xpath.buf, cmd, (xmlChar *)req[1]);
3202 fail:
3203 g_strfreev(req);
3204 xpath_command_cleanup(&xpath);
3205 return send_error(ctx, rc);
3208 static gint import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
3209 gsize len)
3211 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
3212 gpg_error_t rc = file_modified(client);
3213 gchar **req, **path = NULL, **path_orig = NULL, *content;
3214 xmlDocPtr doc = NULL;
3215 xmlNodePtr n, root, copy;
3217 if (assuan_rc || rc) {
3218 if (line)
3219 xfree(line);
3220 return assuan_rc ? assuan_rc : rc;
3223 req = split_input_line((gchar *)line, "\t", 2);
3224 xfree(line);
3226 if (!req || !*req)
3227 return GPG_ERR_SYNTAX;
3229 content = req[0];
3230 path = split_input_line(req[1], "\t", 0);
3232 if (!content || !*content) {
3233 rc = GPG_ERR_SYNTAX;
3234 goto fail;
3237 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
3239 if (!doc) {
3240 rc = EPWMD_LIBXML_ERROR;
3241 goto fail;
3244 root = xmlDocGetRootElement(doc);
3245 rc = validate_import(root);
3247 if (rc)
3248 goto fail;
3250 if (path) {
3251 path_orig = g_strdupv(path);
3253 if (!path_orig) {
3254 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
3255 rc = GPG_ERR_ENOMEM;
3256 goto fail;
3259 xmlChar *a = xmlGetProp(root, (xmlChar *)"_name");
3261 if (!a) {
3262 g_strfreev(path_orig);
3263 rc = GPG_ERR_ENOMEM;
3264 goto fail;
3267 if (strv_printf(&path, "%s", (gchar *)a) == FALSE) {
3268 xmlFree(a);
3269 g_strfreev(path_orig);
3270 rc = GPG_ERR_ENOMEM;
3271 goto fail;
3274 xmlFree(a);
3275 n = find_root_element(client->doc, &path, &rc, NULL, 0, FALSE);
3277 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
3278 g_strfreev(path_orig);
3279 goto fail;
3282 if (!rc) {
3283 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL, TRUE);
3285 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
3286 g_strfreev(path_orig);
3287 goto fail;
3289 else if (!rc) {
3290 xmlNodePtr parent = n->parent;
3292 xmlUnlinkNode(n);
3293 xmlFreeNode(n);
3294 n = parent;
3298 g_strfreev(path);
3299 path = path_orig;
3301 if (rc == GPG_ERR_ELEMENT_NOT_FOUND) {
3302 n = create_element_path(client, &path, &rc);
3304 if (rc)
3305 goto fail;
3308 copy = xmlCopyNodeList(root);
3309 n = xmlAddChildList(n, copy);
3311 if (!n)
3312 rc = EPWMD_LIBXML_ERROR;
3314 else {
3315 /* Check if the content root element can create a DTD root element. */
3316 if (!xmlStrEqual((xmlChar *)"element", root->name)) {
3317 rc = GPG_ERR_SYNTAX;
3318 goto fail;
3321 xmlChar *a;
3323 if ((a = xmlGetProp(root, (xmlChar *)"_name")) == NULL) {
3324 rc = GPG_ERR_SYNTAX;
3325 goto fail;
3328 gchar *tmp = g_strdup((gchar *)a);
3329 xmlFree(a);
3330 gboolean literal = is_literal_element(&tmp);
3332 if (!valid_xml_element((xmlChar *)tmp) || literal) {
3333 g_free(tmp);
3334 rc = GPG_ERR_INV_VALUE;
3335 goto fail;
3338 if (strv_printf(&path, "%s", tmp) == FALSE) {
3339 g_free(tmp);
3340 rc = GPG_ERR_ENOMEM;
3341 goto fail;
3344 g_free(tmp);
3345 n = find_root_element(client->doc, &path, &rc, NULL, 0, TRUE);
3347 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
3348 rc = EPWMD_LIBXML_ERROR;
3349 goto fail;
3352 /* Overwriting the existing tree. */
3353 if (!rc) {
3354 xmlUnlinkNode(n);
3355 xmlFreeNodeList(n);
3358 rc = 0;
3359 xmlSetProp(root, (xmlChar *)"_name", (xmlChar *)path[0]);
3360 n = xmlCopyNode(root, 1);
3361 n = xmlAddChildList(xmlDocGetRootElement(client->doc), n);
3364 if (n && !rc)
3365 rc = update_element_mtime(n->parent);
3367 fail:
3368 if (doc)
3369 xmlFreeDoc(doc);
3371 if (path)
3372 g_strfreev(path);
3374 g_strfreev(req);
3375 client->inquire_status = INQUIRE_DONE;
3376 return rc;
3379 static gint import_command(assuan_context_t ctx, gchar *line)
3381 gpg_error_t rc;
3382 struct client_s *client = assuan_get_pointer(ctx);
3384 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
3386 if (rc)
3387 return send_error(ctx, rc);
3389 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
3390 client->inquire_status = INQUIRE_BUSY;
3391 return 0;
3394 static gpg_error_t do_lock_command(struct client_s *client)
3396 gpg_error_t rc = lock_file_mutex(client);
3398 if (!rc)
3399 client->is_lock_cmd = TRUE;
3401 return client->opts & OPT_INQUIRE ? rc : send_error(client->ctx, rc);
3404 static gint lock_command(assuan_context_t ctx, gchar *line)
3406 struct client_s *client = assuan_get_pointer(ctx);
3408 return do_lock_command(client);
3411 static gint unlock_command(assuan_context_t ctx, gchar *line)
3413 struct client_s *client = assuan_get_pointer(ctx);
3415 unlock_file_mutex(client);
3416 return send_error(ctx, 0);
3419 static gint getpid_command(assuan_context_t ctx, gchar *line)
3421 gpg_error_t rc;
3422 gchar buf[32];
3423 pid_t pid = getpid();
3425 print_fmt(buf, sizeof(buf), "%i", pid);
3426 rc = xfer_data(ctx, buf, strlen(buf));
3427 return send_error(ctx, rc);
3430 static gint version_command(assuan_context_t ctx, gchar *line)
3432 gpg_error_t rc;
3433 gchar *buf;
3435 buf = g_strdup_printf("0x%X %s", VERSION_HEX,
3436 #ifdef WITH_PINENTRY
3437 "PINENTRY "
3438 #endif
3439 #ifdef WITH_QUALITY
3440 "QUALITY "
3441 #endif
3442 #ifdef WITH_LIBACL
3443 "ACL "
3444 #endif
3445 "");
3446 rc = xfer_data(ctx, buf, strlen(buf));
3447 g_free(buf);
3448 return send_error(ctx, rc);
3451 #ifdef WITH_PINENTRY
3452 static void set_option_value(gchar **opt, const gchar *value)
3454 if (opt)
3455 g_free(*opt);
3457 *opt = NULL;
3459 if (value)
3460 *opt = g_strdup(value);
3462 #endif
3464 static gint set_unset_common(assuan_context_t ctx, const gchar *name,
3465 const gchar *value)
3467 struct client_s *client = assuan_get_pointer(ctx);
3468 gpg_error_t rc;
3470 if (g_ascii_strcasecmp(name, (gchar *)"log_level") == 0) {
3471 glong l = 0;
3473 if (value) {
3474 l = strtol(value, NULL, 10);
3476 if (l < 0 || l > 2)
3477 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3480 MUTEX_LOCK(&rcfile_mutex);
3481 g_key_file_set_integer(keyfileh, "global", "log_level", (gint)l);
3482 MUTEX_UNLOCK(&rcfile_mutex);
3483 goto done;
3485 else if (g_ascii_strcasecmp(name, (gchar *)"rc_on_locked") == 0) {
3486 glong l = 0;
3488 if (value) {
3489 l = strtol(value, NULL, 10);
3491 if (l < 0 || l > 1)
3492 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3495 client->rc_on_locked = l ? TRUE : FALSE;
3496 goto done;
3498 else if (g_ascii_strcasecmp(name, (gchar *)"lock_on_open") == 0) {
3499 rc = parse_open_opt_lock(client, (gpointer)value);
3501 if (rc)
3502 return rc;
3504 client->opts |= OPT_LOCK;
3506 else if (g_ascii_strcasecmp(name, (gchar *)"cipher") == 0) {
3507 if (!value) {
3508 client->opts &= ~(OPT_CIPHER);
3509 goto done;
3512 rc = parse_save_opt_cipher(client, (gpointer)value);
3514 if (rc)
3515 return rc;
3517 client->opts |= OPT_CIPHER;
3518 goto done;
3520 else if (g_ascii_strcasecmp(name, (gchar *)"iterations") == 0) {
3521 rc = parse_save_opt_iterations(client, (gpointer)value);
3523 if (rc)
3524 return rc;
3526 goto done;
3528 else if (g_ascii_strcasecmp(name, (gchar *)"NAME") == 0) {
3529 pth_attr_t attr = pth_attr_of(pth_self());
3530 gchar buf[41];
3532 if (!value) {
3533 pth_attr_destroy(attr);
3534 goto done;
3537 print_fmt(buf, sizeof(buf), "%s", value);
3538 pth_attr_set(attr, PTH_ATTR_NAME, buf);
3539 pth_attr_destroy(attr);
3540 #ifdef WITH_PINENTRY
3541 if (client->pinentry->name)
3542 g_free(client->pinentry->name);
3544 client->pinentry->name = g_strdup(buf);
3546 if (!client->pinentry->name)
3547 return GPG_ERR_ENOMEM;
3548 #endif
3550 goto done;
3552 #ifdef WITH_PINENTRY
3553 else if (g_ascii_strcasecmp(name, (gchar *)"lc_messages") == 0)
3554 set_option_value(&client->pinentry->lcmessages, value);
3555 else if (g_ascii_strcasecmp(name, (gchar *)"lc_ctype") == 0)
3556 set_option_value(&client->pinentry->lcctype, value);
3557 else if (g_ascii_strcasecmp(name, (gchar *)"ttyname") == 0)
3558 set_option_value(&client->pinentry->ttyname, value);
3559 else if (g_ascii_strcasecmp(name, (gchar *)"ttytype") == 0)
3560 set_option_value(&client->pinentry->ttytype, value);
3561 else if (g_ascii_strcasecmp(name, (gchar *)"display") == 0)
3562 set_option_value(&client->pinentry->display, value);
3563 else if (g_ascii_strcasecmp(name, (gchar *)"pinentry_path") == 0)
3564 set_option_value(&client->pinentry->path, value);
3565 else if (g_ascii_strcasecmp(name, (gchar *)"title") == 0)
3566 set_option_value(&client->pinentry->title, value);
3567 else if (g_ascii_strcasecmp(name, (gchar *)"prompt") == 0)
3568 set_option_value(&client->pinentry->prompt, value);
3569 else if (g_ascii_strcasecmp(name, (gchar *)"desc") == 0)
3570 set_option_value(&client->pinentry->desc, value);
3571 else if (g_ascii_strcasecmp(name, "pinentry_timeout") == 0) {
3572 gchar *p = NULL;
3573 gint n;
3575 if (!value)
3576 goto done;
3578 n = strtol(value, &p, 10);
3580 if (*p || n < 0)
3581 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
3583 MUTEX_LOCK(&rcfile_mutex);
3584 g_key_file_set_integer(keyfileh, client->filename ? client->filename :
3585 "global", "pinentry_timeout", n);
3586 MUTEX_UNLOCK(&rcfile_mutex);
3587 goto done;
3589 else if (g_ascii_strcasecmp(name, "enable_pinentry") == 0) {
3590 rc = parse_opt_pinentry(client, (gpointer)value);
3592 if (rc)
3593 return rc;
3595 goto done;
3597 #else
3598 else if (g_ascii_strcasecmp(name, (gchar *)"lc_messages") == 0)
3599 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3600 else if (g_ascii_strcasecmp(name, (gchar *)"lc_ctype") == 0)
3601 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3602 else if (g_ascii_strcasecmp(name, (gchar *)"ttyname") == 0)
3603 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3604 else if (g_ascii_strcasecmp(name, (gchar *)"ttytype") == 0)
3605 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3606 else if (g_ascii_strcasecmp(name, (gchar *)"display") == 0)
3607 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3608 else if (g_ascii_strcasecmp(name, (gchar *)"pinentry_path") == 0)
3609 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3610 else if (g_ascii_strcasecmp(name, (gchar *)"title") == 0)
3611 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3612 else if (g_ascii_strcasecmp(name, (gchar *)"prompt") == 0)
3613 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3614 else if (g_ascii_strcasecmp(name, (gchar *)"desc") == 0)
3615 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3616 else if (g_ascii_strcasecmp(name, "pinentry_timeout") == 0)
3617 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3618 else if (g_ascii_strcasecmp(name, "enable_pinentry") == 0)
3619 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
3620 #endif
3621 else
3622 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
3624 done:
3625 return 0;
3628 static gint unset_command(assuan_context_t ctx, gchar *line)
3630 log_write2("ARGS=\"%s\"", line);
3631 return send_error(ctx, set_unset_common(ctx, line, NULL));
3634 static gint set_command(assuan_context_t ctx, gchar *line)
3636 gchar name[64] = {0}, value[256] = {0};
3638 log_write2("ARGS=\"%s\"", line);
3640 if (sscanf(line, " %63[_a-zA-Z] = %255c", name, value) != 2)
3641 return send_error(ctx, gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX));
3643 return send_error(ctx, set_unset_common(ctx, name, value));
3646 static gint rename_command(assuan_context_t ctx, gchar *line)
3648 struct client_s *client = assuan_get_pointer(ctx);
3649 gpg_error_t rc;
3650 gchar **req, **src, *dst;
3651 xmlNodePtr n, ndst;
3653 log_write2("ARGS=\"%s\"", line);
3654 req = split_input_line(line, " ", -1);
3656 if (!req || !req[0] || !req[1]) {
3657 g_strfreev(req);
3658 return send_error(ctx, GPG_ERR_SYNTAX);
3661 dst = req[1];
3662 is_literal_element(&dst);
3664 if (!valid_xml_element((xmlChar *)dst)) {
3665 g_strfreev(req);
3666 return GPG_ERR_INV_VALUE;
3669 if (strchr(req[0], '\t'))
3670 src = split_input_line(req[0], "\t", -1);
3671 else
3672 src = split_input_line(req[0], " ", -1);
3674 if (!src || !*src) {
3675 rc = GPG_ERR_SYNTAX;
3676 goto fail;
3679 n = find_root_element(client->doc, &src, &rc, NULL, 0, FALSE);
3681 if (src[1] && n)
3682 n = find_elements(client->doc, n->children, src+1, &rc, NULL, NULL,
3683 NULL, FALSE, 0, NULL, FALSE);
3685 if (!n)
3686 goto fail;
3689 xmlChar *a = xmlGetProp(n, (xmlChar *)"_name");
3691 if (!a) {
3692 rc = GPG_ERR_ENOMEM;
3693 goto fail;
3696 /* To prevent unwanted effects:
3698 * <root name="a"><b/></root>
3700 * RENAME a<TAB>b b
3702 if (xmlStrEqual(a, (xmlChar *)dst)) {
3703 xmlFree(a);
3704 rc = GPG_ERR_AMBIGUOUS_NAME;
3705 goto fail;
3708 xmlFree(a);
3709 gchar **tmp = NULL;
3711 if (src[1]) {
3712 gchar **p;
3714 for (p = src; *p; p++) {
3715 if (!*(p+1))
3716 break;
3718 strv_printf(&tmp, "%s", *p);
3722 strv_printf(&tmp, "!%s", dst);
3723 ndst = find_root_element(client->doc, &tmp, &rc, NULL, 0, FALSE);
3725 if (!ndst && rc && rc != GPG_ERR_ELEMENT_NOT_FOUND) {
3726 g_strfreev(tmp);
3727 goto fail;
3730 if (tmp[1] && ndst)
3731 ndst = find_elements(client->doc, ndst->children, tmp+1, &rc, NULL,
3732 NULL, NULL, FALSE, 0, NULL, FALSE);
3734 g_strfreev(tmp);
3736 if (!ndst && rc && rc != GPG_ERR_ELEMENT_NOT_FOUND)
3737 goto fail;
3739 rc = 0;
3741 /* Target may exist:
3743 * <root name="a"/>
3744 * <root name="b" target="a"/>
3746 * RENAME b a
3748 * Would need to do:
3749 * RENAME !b a
3751 if (ndst == n) {
3752 rc = GPG_ERR_AMBIGUOUS_NAME;
3753 goto fail;
3756 if (ndst) {
3757 unlink_node(ndst);
3758 xmlFreeNodeList(ndst);
3761 rc = add_attribute(n, "_name", dst);
3763 fail:
3764 g_strfreev(req);
3765 g_strfreev(src);
3766 return send_error(ctx, rc);
3769 static gint copy_command(assuan_context_t ctx, gchar *line)
3771 struct client_s *client = assuan_get_pointer(ctx);
3772 gpg_error_t rc;
3773 gchar **req, **src = NULL, **dst = NULL;
3774 xmlNodePtr nsrc, ndst, new;
3776 log_write2("ARGS=\"%s\"", line);
3777 req = split_input_line(line, " ", -1);
3779 if (!req || !req[0] || !req[1]) {
3780 g_strfreev(req);
3781 return send_error(ctx, GPG_ERR_SYNTAX);
3784 if (strchr(req[0], '\t'))
3785 src = split_input_line(req[0], "\t", -1);
3786 else
3787 src = split_input_line(req[0], " ", -1);
3789 if (!src || !*src) {
3790 rc = GPG_ERR_SYNTAX;
3791 goto fail;
3794 if (strchr(req[1], '\t'))
3795 dst = split_input_line(req[1], "\t", -1);
3796 else
3797 dst = split_input_line(req[1], " ", -1);
3799 if (!dst || !*dst) {
3800 rc = GPG_ERR_SYNTAX;
3801 goto fail;
3804 nsrc = find_root_element(client->doc, &src, &rc, NULL, 0, FALSE);
3806 if (nsrc && src[1])
3807 nsrc = find_elements(client->doc, nsrc->children, src+1, &rc, NULL,
3808 NULL, NULL, FALSE, 0, NULL, FALSE);
3810 if (!nsrc)
3811 goto fail;
3813 ndst = find_root_element(client->doc, &dst, &rc, NULL, 0, FALSE);
3815 if (ndst && dst[1])
3816 ndst = find_elements(client->doc, ndst->children, dst+1, &rc, NULL,
3817 NULL, NULL, FALSE, 0, NULL, FALSE);
3819 if (!ndst && rc != GPG_ERR_ELEMENT_NOT_FOUND)
3820 goto fail;
3822 new = xmlCopyNodeList(nsrc);
3824 if (!new) {
3825 rc = GPG_ERR_ENOMEM;
3826 goto fail;
3829 if (!ndst)
3830 ndst = create_element_path(client, &dst, &rc);
3832 if (!ndst) {
3833 xmlUnlinkNode(new);
3834 xmlFreeNodeList(new);
3835 goto fail;
3838 /* Merge any attributes from the src node to the initial dst node. */
3839 for (xmlAttrPtr attr = new->properties; attr; attr = attr->next) {
3840 if (xmlStrEqual(attr->name, (xmlChar *)"_name"))
3841 continue;
3843 xmlAttrPtr a = xmlHasProp(ndst, attr->name);
3845 if (a)
3846 xmlRemoveProp(a);
3848 xmlChar *tmp = xmlNodeGetContent(attr->children);
3849 xmlNewProp(ndst, attr->name, tmp);
3850 xmlFree(tmp);
3851 rc = add_attribute(ndst, NULL, NULL);
3854 xmlNodePtr n = ndst->children;
3855 xmlUnlinkNode(n);
3856 xmlFreeNodeList(n);
3857 ndst->children = NULL;
3859 if (!new->children) {
3860 xmlUnlinkNode(new);
3861 xmlFreeNodeList(new);
3862 goto fail;
3865 n = xmlCopyNodeList(new->children);
3867 if (!n) {
3868 rc = GPG_ERR_ENOMEM;
3869 goto fail;
3872 xmlUnlinkNode(new);
3873 xmlFreeNodeList(new);
3874 n = xmlAddChildList(ndst, n);
3876 if (!n) {
3877 rc = GPG_ERR_ENOMEM;
3878 goto fail;
3881 rc = update_element_mtime(xmlDocGetRootElement(client->doc) == ndst->parent ? ndst : ndst->parent);
3883 fail:
3884 if (req)
3885 g_strfreev(req);
3887 if (src)
3888 g_strfreev(src);
3890 if (dst)
3891 g_strfreev(dst);
3893 return send_error(ctx, rc);
3896 static gint move_command(assuan_context_t ctx, gchar *line)
3898 struct client_s *client = assuan_get_pointer(ctx);
3899 gpg_error_t rc;
3900 gchar **req, **src = NULL, **dst = NULL;
3901 xmlNodePtr nsrc, ndst = NULL;
3903 log_write2("ARGS=\"%s\"", line);
3904 req = split_input_line(line, " ", -1);
3906 if (!req || !req[0] || !req[1]) {
3907 g_strfreev(req);
3908 return send_error(ctx, GPG_ERR_SYNTAX);
3911 if (strchr(req[0], '\t'))
3912 src = split_input_line(req[0], "\t", -1);
3913 else
3914 src = split_input_line(req[0], " ", -1);
3916 if (!src || !*src) {
3917 rc = GPG_ERR_SYNTAX;
3918 goto fail;
3921 if (strchr(req[1], '\t'))
3922 dst = split_input_line(req[1], "\t", -1);
3923 else
3924 dst = split_input_line(req[1], " ", -1);
3926 nsrc = find_root_element(client->doc, &src, &rc, NULL, 0, FALSE);
3928 if (nsrc && src[1])
3929 nsrc = find_elements(client->doc, nsrc->children, src+1, &rc, NULL,
3930 NULL, NULL, FALSE, 0, NULL, FALSE);
3932 if (!nsrc)
3933 goto fail;
3935 if (dst) {
3936 ndst = find_root_element(client->doc, &dst, &rc, NULL, 0, FALSE);
3938 if (ndst && dst[1])
3939 ndst = find_elements(client->doc, ndst->children, dst+1, &rc, NULL,
3940 NULL, NULL, FALSE, 0, NULL, FALSE);
3942 else
3943 ndst = xmlDocGetRootElement(client->doc);
3945 for (xmlNodePtr n = ndst; n; n = n->parent) {
3946 if (n == nsrc) {
3947 rc = GPG_ERR_CONFLICT;
3948 goto fail;
3952 if (rc && rc != GPG_ERR_ELEMENT_NOT_FOUND)
3953 goto fail;
3955 rc = 0;
3957 if (ndst) {
3958 xmlChar *a = node_has_attribute(nsrc, (xmlChar *)"_name");
3959 xmlNodePtr dup = find_element(ndst->children, (gchar *)a, NULL);
3961 xmlFree(a);
3963 if (dup) {
3964 if (dup == nsrc)
3965 goto fail;
3967 if (ndst == xmlDocGetRootElement(client->doc)) {
3968 xmlNodePtr n = nsrc;
3969 gboolean match = FALSE;
3971 while (n->parent && n->parent != ndst)
3972 n = n->parent;
3974 xmlChar *a = node_has_attribute(n, (xmlChar *)"_name");
3975 xmlChar *b = node_has_attribute(nsrc, (xmlChar *)"_name");
3977 if (xmlStrEqual(a, b)) {
3978 match = TRUE;
3979 xmlUnlinkNode(nsrc);
3980 xmlUnlinkNode(n);
3981 xmlFreeNodeList(n);
3984 xmlFree(a);
3985 xmlFree(b);
3987 if (!match) {
3988 xmlUnlinkNode(dup);
3989 xmlFreeNodeList(dup);
3992 else
3993 xmlUnlinkNode(dup);
3997 if (!ndst && dst)
3998 ndst = create_element_path(client, &dst, &rc);
4000 if (!ndst)
4001 goto fail;
4003 update_element_mtime(nsrc->parent);
4004 xmlUnlinkNode(nsrc);
4005 ndst = xmlAddChildList(ndst, nsrc);
4007 if (!ndst)
4008 rc = GPG_ERR_ENOMEM;
4010 update_element_mtime(ndst->parent);
4012 fail:
4013 if (req)
4014 g_strfreev(req);
4016 if (src)
4017 g_strfreev(src);
4019 if (dst)
4020 g_strfreev(dst);
4022 return send_error(ctx, rc);
4025 static int ls_command(assuan_context_t ctx, gchar *line)
4027 log_write2("ARGS=\"%s\"", line);
4028 gpg_error_t rc;
4029 gchar *tmp = g_key_file_get_string(keyfileh, "global", "data_directory", NULL);
4030 gchar *dir = expand_homedir(tmp);
4031 DIR *d = opendir(dir);
4033 rc = gpg_error_from_syserror();
4034 g_free(tmp);
4036 if (!d) {
4037 g_free(dir);
4038 return send_error(ctx, rc);
4041 size_t len = offsetof(struct dirent, d_name)+pathconf(dir, _PC_NAME_MAX)+1;
4042 struct dirent *p = g_malloc(len), *cur = NULL;
4043 gchar *list = NULL;
4045 g_free(dir);
4046 rc = 0;
4048 while (!readdir_r(d, p, &cur) && cur) {
4049 if (cur->d_name[0] == '.' && cur->d_name[1] == '\0')
4050 continue;
4051 else if (cur->d_name[0] == '.' && cur->d_name[1] == '.' && cur->d_name[2] == '\0')
4052 continue;
4054 tmp = g_strdup_printf("%s%s\n", list ? list : "", cur->d_name);
4056 if (!tmp) {
4057 if (list)
4058 g_free(list);
4060 rc = GPG_ERR_ENOMEM;
4061 break;
4064 g_free(list);
4065 list = tmp;
4068 closedir(d);
4069 g_free(p);
4071 if (rc)
4072 return send_error(ctx, rc);
4074 if (!list)
4075 return send_error(ctx, GPG_ERR_NO_VALUE);
4077 list[strlen(list)-1] = 0;
4078 rc = xfer_data(ctx, list, strlen(list));
4079 g_free(list);
4080 return send_error(ctx, rc);
4083 static void bye_notify(assuan_context_t ctx)
4085 struct client_s *cl = assuan_get_pointer(ctx);
4087 /* This will let assuan_process_next() return. */
4088 fcntl(cl->thd->fd, F_SETFL, O_NONBLOCK);
4089 cl->last_rc = 0; // BYE command result
4092 static void reset_notify(assuan_context_t ctx)
4094 struct client_s *cl = assuan_get_pointer(ctx);
4096 if (cl)
4097 cleanup_client(cl);
4101 * This is called before every Assuan command.
4103 gint command_startup(assuan_context_t ctx, const gchar *name)
4105 struct client_s *cl = assuan_get_pointer(ctx);
4106 gpg_error_t rc;
4108 log_write1("%s", name);
4110 for (int i = 0; command_table[i]; i++) {
4111 if (!g_ascii_strcasecmp(name, command_table[i]->name) &&
4112 command_table[i]->ignore_startup)
4113 return 0;
4116 #ifdef WITH_PINENTRY
4117 if (!(cl->opts & OPT_PINENTRY))
4118 reset_pin_defaults(cl->pinentry);
4119 #endif
4121 cl->last_rc = rc = file_modified(cl);
4123 if (rc) {
4124 if ((rc == EPWMD_NO_FILE || rc == EPWMD_FILE_MODIFIED) &&
4125 !g_ascii_strcasecmp(name, "OPEN"))
4126 rc = 0;
4129 return rc;
4133 * This is called after every Assuan command.
4135 void command_finalize(assuan_context_t ctx, gint rc)
4137 struct client_s *client = assuan_get_pointer(ctx);
4139 if (!client->is_lock_cmd)
4140 unlock_file_mutex(client);
4142 log_write1(N_("command completed (rc=%u)"), client->last_rc);
4143 client->opts &= ~(OPT_INQUIRE);
4144 client->opts &= ~(OPT_BASE64);
4147 static gint help_command(assuan_context_t ctx, gchar *line)
4149 gpg_error_t rc;
4150 gint i;
4152 if (!line || !*line) {
4153 gchar *tmp;
4154 gchar *buf = g_strdup(N_(
4155 "Usage: HELP [<COMMAND>]\n"
4156 " For commands that take an element path as an argument, each element is\n"
4157 " separated with an ASCII tab character (ASCII 0x09).\n"
4158 "\n"
4159 " Each element may contain a \"target\" attribute whose value is also an\n"
4160 " element path. Think of a \"target\" attribute like a symbolic link on a\n"
4161 " filesystem. When found, the element path of the \"target\" attribute will\n"
4162 " be used as a prefix for further elements in the current element path. To\n"
4163 " ignore any \"target\" attribute for the current element, prefix the element\n"
4164 " with an ! (ASCII 0x21).\n"
4165 "\n"
4166 " See pwmd(1) for configuration file options which may also set default\n"
4167 " options for specific data files.\n"
4168 "\n"
4169 "COMMANDS:"));
4171 for (i = 0; command_table[i]; i++) {
4172 gchar *p = strrchr(buf, '\n');
4173 gboolean newline = FALSE;
4175 if (!command_table[i]->help)
4176 continue;
4178 if (p && strlen(p)+strlen(command_table[i]->name) > 79) {
4179 tmp = g_strdup_printf("%s\n", buf);
4180 g_free(buf);
4181 buf = tmp;
4182 newline = TRUE;
4185 tmp = g_strdup_printf("%s%s%s", buf, !newline ? " " : "",
4186 command_table[i]->name);
4187 g_free(buf);
4188 buf = tmp;
4191 tmp = g_strdup_printf("%s\n", buf);
4192 g_free(buf);
4193 buf = tmp;
4194 rc = xfer_data(ctx, buf, strlen(buf));
4195 g_free(buf);
4196 return send_error(ctx, rc);
4199 for (i = 0; command_table[i]; i++) {
4200 if (!g_strcasecmp(line, command_table[i]->name)) {
4201 if (!command_table[i]->help)
4202 break;
4204 gchar *tmp = g_strdup_printf(N_("Usage: %s"), command_table[i]->help);
4205 rc = xfer_data(ctx, tmp, strlen(tmp));
4206 g_free(tmp);
4207 return send_error(ctx, rc);
4211 return send_error(ctx, GPG_ERR_INV_NAME);
4214 void new_command(const gchar *name, gboolean ignore,
4215 gint (*handler)(assuan_context_t, gchar *), const gchar *help)
4217 gint i = 0;
4219 if (command_table)
4220 for (i = 0; command_table[i]; i++);
4222 command_table = g_realloc(command_table, (i+2)*sizeof(struct command_table_s *));
4223 command_table[i] = g_malloc0(sizeof(struct command_table_s));
4224 command_table[i]->name = name;
4225 command_table[i]->handler = handler;
4226 command_table[i]->ignore_startup = ignore;
4227 command_table[i++]->help = help;
4228 command_table[i] = NULL;
4231 void deinit_commands()
4233 gint i;
4235 for (i = 0; command_table[i]; i++)
4236 g_free(command_table[i]);
4238 g_free(command_table);
4241 static gint sort_commands(const void *arg1, const void *arg2)
4243 struct command_table_s* const *a = arg1;
4244 struct command_table_s* const *b = arg2;
4246 if (!*a || !*b)
4247 return 0;
4248 else if (*a && !*b)
4249 return 1;
4250 else if (!*a && *b)
4251 return -1;
4253 return strcmp((*a)->name, (*b)->name);
4256 void init_commands()
4258 /* !BEGIN-HELP-TEXT!
4260 * This comment is used as a marker to generate the offline documentation
4261 * for commands found in doc/COMMANDS.
4263 new_command("HELP", TRUE, help_command, N_(
4264 "HELP [<COMMAND>]\n"
4265 " Show available commands or command specific help text.\n"
4268 new_command("OPEN", FALSE, open_command, N_(
4269 "OPEN [--lock] [--inquire | --pinentry=[0|1]] [--base64] <filename> [<key>]\n"
4270 " Opens <filename> using <key>. When the filename is not found on the\n"
4271 " file-system a new document will be created. If the file is found, it is\n"
4272 " looked for in the file cache for an existing key. When found and no key\n"
4273 " was specified, the cached key will be used for decryption (if encrypted).\n"
4274 " When not found, pinentry(1) will be used to retrieve the key (see the\n"
4275 " OPTIONS documentation).\n"
4276 "\n"
4277 " When the --lock option is passed then the file mutex will be locked as if\n"
4278 " the LOCK command had been sent after the file had been opened.\n"
4279 "\n"
4280 " The --inquire option disables pinentry usage and uses a server inquire to\n"
4281 " retrieve the filename and key arguments.\n"
4282 "\n"
4283 " Using pinentry for passphrase retrieval can be enabled or disabled by\n"
4284 " specifying the --pinentry option with the value 1 or 0 respectively. When\n"
4285 " no value is specified then the configuration file value will be used. If\n"
4286 " the passphrase is invalid then it is up to the client whether to retry or\n"
4287 " not. To decrypt an encrypted file with an empty passphrase and avoid the\n"
4288 " pinentry dialog, use --pinentry=0.\n"
4289 "\n"
4290 " When a \"key_file\" configuration parameter has been set for the current\n"
4291 " file and there is no cache entry, then an --inquire must be used to\n"
4292 " retrieve the key.\n"
4293 "\n"
4294 " The --base64 option specifies that the key is Base64 encoded. It will be\n"
4295 " decoded before doing decryption. This allows for binary keys and may also\n"
4296 " be used with --inquire. This option is ignored when a pinentry is used.\n"
4299 new_command("SAVE", FALSE, save_command, N_(
4300 "SAVE [--reset] [--inquire | --pinentry=[0|1]] [--cipher=[<string>]]\n"
4301 " [--iterations=[N]] [--base64] [<key>]\n"
4302 " Writes the XML document to disk. The file written to is the file that was\n"
4303 " opened using the OPEN command. If <key> is not specified then the\n"
4304 " currently cached key will be used. If the file is a new file or the file\n"
4305 " isn't found in the file cache then <key> will be used. If both <key> is\n"
4306 " not specified and the file is not cached then pinentry(1) will be used to\n"
4307 " retrieve the key (see below) unless the configured number of iterations is\n"
4308 " 0 in which case the file will be saved unencrypted.\n"
4309 "\n"
4310 " Note that when both <key> is specified and the configured number of\n"
4311 " iterations is 0 the iterations for the current filename will be reset to\n"
4312 " 1. This is to be on the safe side and prevent misuse.\n"
4313 "\n"
4314 " The --iterations option can be used to change the number of encryption\n"
4315 " iterations for the opened file. When 0 no encryption will be performed.\n"
4316 " When this option is either not passed or is specified without a value then\n"
4317 " previous setting obtained from the file header will be used.\n"
4318 "\n"
4319 " You can specify an alternate cipher to encrypt with by specifying a cipher\n"
4320 " string with the --cipher option. Omitting the string uses the current\n"
4321 " cipher of the opened file or the default if the file is a new one. The\n"
4322 " default is specified in the configuration file. See pwmd(1) for available\n"
4323 " ciphers.\n"
4324 "\n"
4325 " Using pinentry for passphrase retrieval can be enabled or disabled by\n"
4326 " specifying the --pinentry option with the value 1 or 0, respectively. When\n"
4327 " no value is specified then the configuration file value will be used.\n"
4328 " When enabled and the passphrase confirmation fails, the pinentry process\n"
4329 " is started over again until either the passphrases match or until the\n"
4330 " input is canceled by the user. To save with encryption and with an empty\n"
4331 " passphrase, use --pinentry=0.\n"
4332 "\n"
4333 " When --reset is specified then the cached passphrase for the opened file\n"
4334 " will be cleared before doing the actual SAVE as if the CLEARCACHE command\n"
4335 " had been sent.\n"
4336 "\n"
4337 " The --inquire option disables pinentry usage and uses a server inquire to\n"
4338 " retrieve the key.\n"
4339 "\n"
4340 " When a \"key_file\" configuration parameter has been set for the current\n"
4341 " file and there is no cache entry, then an --inquire must be used to\n"
4342 " retrieve the key.\n"
4343 "\n"
4344 " The --base64 option specifies that the key is Base64 encoded. It will be\n"
4345 " decoded before doing encryption. This allows for binary keys and may also\n"
4346 " be used with --inquire. This option is ignored when a pinentry is used.\n"
4349 new_command("ISCACHED", TRUE, iscached_command, N_(
4350 "ISCACHED <filename>\n"
4351 " An OK response is returned if the specified file is found in the file\n"
4352 " cache. If not found in the cache but exists on the filesystem,\n"
4353 " GPG_ERR_NOT_FOUND is returned. Otherwise a filesystem error is returned.\n"
4356 new_command("CLEARCACHE", TRUE, clearcache_command, N_(
4357 "CLEARCACHE [<filename>]\n"
4358 " Clears a file cache entry. This will forget the timeout and key for all or\n"
4359 " the specified file. Always returns an OK response.\n"
4362 new_command("CACHETIMEOUT", TRUE, cachetimeout_command, N_(
4363 "CACHETIMEOUT <filename> <seconds>\n"
4364 " Specify the number of seconds the specified file will be cached. -1 will\n"
4365 " keep the cache entry forever, 0 will require the key each time the OPEN or\n"
4366 " SAVE commands are used. Also see the \"cache_timeout\" configuration\n"
4367 " parameter. Returns ERR if the filename isn't cached or if the timeout is\n"
4368 " invalid. OK otherwise.\n"
4371 new_command("LIST", FALSE, list_command, N_(
4372 "LIST [--no-recurse] [--verbose] [[!]element[<TAB>[!]element[...]]]\n"
4373 " If no element path is given then a newline separated list of root elements\n"
4374 " is returned with the data response. If given, then all reachable elements\n"
4375 " for the specified element path are returned unless the --no-recurse option\n"
4376 " is specified. If specified, only the child elements of the element path\n"
4377 " are returned without recursing into grandchildren. Each element in the\n"
4378 " path is prefixed with the literal '!' character when the element contains\n"
4379 " no \"target\" attribute. Refer to THE TARGET ATTRIBUTE for more information.\n"
4380 "\n"
4381 " When the --verbose option is passed then each element path returned in the\n"
4382 " list will have a single space character followed by either a 0 or 1\n"
4383 " appended to it. When 0, the element path has no children, otherwise it\n"
4384 " does have children. When used with the --no-recurse option this may be\n"
4385 " useful to limit the amount of data transferred to the client.\n"
4388 new_command("REALPATH", FALSE, realpath_command, N_(
4389 "REALPATH [!]element[<TAB>[!]element[...]]\n"
4390 " Resolves all \"target\" attributes of the specified element path and returns\n"
4391 " the result with a data response.\n"
4394 new_command("STORE", FALSE, store_command, N_(
4395 "STORE [!]element[[<TAB>[!]element[...]]<TAB>[content]]\n"
4396 " Creates a new element tree or modifies the content of an existing element\n"
4397 " path. If only a single element is specified, a new root element is\n"
4398 " created. Otherwise, elements are TAB delimited and the content will be set\n"
4399 " to the last TAB delimited argument. If no content is specified after the\n"
4400 " last TAB then the content for the last specified element will be removed\n"
4401 " or empty when creating a new element.\n"
4402 "\n"
4403 " The only restriction of an element name is that it not contain whitespace\n"
4404 " or begin with the literal element character '!' unless specifying a\n"
4405 " literal element. There is no whitespace between the TAB delimited\n"
4406 " elements. It is recommended that the value or content be base 64 encoded\n"
4407 " when it contains control or TAB characters to prevent XML and pwmd parsing\n"
4408 " errors.\n"
4409 "\n"
4410 " PWMD reads the element path from the client via the Assuan INQUIRE\n"
4411 " protocol response: the STORE command is sent by itself without arguments,\n"
4412 " then the server responds with INQUIRE. The client then sends the element\n"
4413 " path prefixed with a \"D \" data response. The element path may extend\n"
4414 " multiple lines but each must be prefixed with the data \"D \" response. When\n"
4415 " finished, the client sends \"END\" on an empty line. This is needed so an\n"
4416 " element path and value can be more than 1000 bytes long, the Assuan\n"
4417 " protocol line limit.\n"
4420 new_command("RENAME", FALSE, rename_command, N_(
4421 "RENAME [!]element[<TAB>[!]element[...]] <value>\n"
4422 " Renames the specified element to the new value. If an element of the same\n"
4423 " name as the value exists then it will be overwritten.\n"
4426 new_command("COPY", FALSE, copy_command, N_(
4427 "COPY [!]element[<TAB>[!]element[...]] [!]element[<TAB>[!]element[...]]\n"
4428 " Copies the entire element tree starting from the child node of the source\n"
4429 " element path, to the destination element path. If the destination element\n"
4430 " path doesn't exist then it is created; otherwise it is overwritten.\n"
4431 "\n"
4432 " Note that attributes from the source element path are merged into the\n"
4433 " destination element path when the destination element path exists. When an\n"
4434 " attribute of the same name exists in both the source and destination\n"
4435 " element paths then the destination attribute will be updated to the source\n"
4436 " attribute value.\n"
4439 new_command("MOVE", FALSE, move_command, N_(
4440 "MOVE [!]element[<TAB>[!]element[...]] [[!]element[<TAB>[!]element[...]]]\n"
4441 " Moves the source element path to the destination element path. If the\n"
4442 " destination is not specified then it will be moved to the root of the\n"
4443 " document. If the destination is specified and exists then it will be\n"
4444 " overwritten; otherwise it will be created.\n"
4447 new_command("DELETE", FALSE, delete_command, N_(
4448 "DELETE [!]element[<TAB>[!]element[...]]\n"
4449 " Removes the specified element path from the XML document.\n"
4452 new_command("GET", FALSE, get_command, N_(
4453 "GET [!]element[<TAB>[!]element[...]]\n"
4454 " Retrieves the content or XML text node of the specified element path. The\n"
4455 " data is returned with a data response.\n"
4458 new_command("ATTR", FALSE, attr_command, N_(
4459 "ATTR SET|GET|DELETE|LIST [<attribute>] [!]<arg1> [!][arg2]\n"
4460 " ATTR SET attribute [!]element[<TAB>[!]element[...]] [attribute_value]\n"
4461 " Stores or updates an attribute name and optional value of an element\n"
4462 " path.\n"
4463 "\n"
4464 " ATTR DELETE attribute [!]element[<TAB>[!]element[...]]\n"
4465 " Removes an attribute from an element path.\n"
4466 "\n"
4467 " ATTR LIST [!]element[<TAB>[!]element[...]]\n"
4468 " Gets a newline separated list of attributes from an element path.\n"
4469 "\n"
4470 " ATTR GET attribute [!]element[<TAB>[!]element[...]]\n"
4471 " Gets the value of an attribute from an element path.\n"
4472 "\n"
4473 " The \"_name\" attribute (case sensitive) cannot be removed with ATTR DELETE\n"
4474 " if the element path is the root element. Although it can be SET to change\n"
4475 " the element name but only if the destination element name doesn't exist.\n"
4476 " Use the RENAME command for that instead.\n"
4477 "\n"
4478 " There is another special attribute \"_mtime\" which is updated each time an\n"
4479 " element is modified: either by storing content, editing attributes or\n"
4480 " deleting a child element.\n"
4481 "\n"
4482 " Also see THE TARGET ATTRIBUTE.\n"
4485 new_command("XPATH", FALSE, xpath_command, N_(
4486 "XPATH <expression>[<TAB>[value]]\n"
4487 " Evaluates an XPath expression. If no value argument is specified, it is\n"
4488 " assumed the expression is a request to return a result. Otherwise, the\n"
4489 " result is set to the value argument and the document is updated. If there\n"
4490 " is no value after the <TAB> character, the value is assumed to be empty\n"
4491 " and the document is updated.\n"
4494 new_command("XPATHATTR", FALSE, xpathattr_command, N_(
4495 "XPATHATTR SET|DELETE <name> <expression>[<TAB>[<value>]]\n"
4496 " Like the XPATH command but operates on element attributes and won't return\n"
4497 " a result. For the SET operation the <value> is optional but the field is\n"
4498 " required in which case the value will be empty.\n"
4501 new_command("IMPORT", FALSE, import_command, N_(
4502 "IMPORT <content>[<TAB>[!]element[<TAB>[!]element[...]]]\n"
4503 " Like the STORE command (an INQUIRE), but the content argument is raw XML\n"
4504 " data. The content is created as a child of the specified element path. If\n"
4505 " an element of the element path does not exist then it is created. If no\n"
4506 " element path is specified then the content must begin with an pwmd DTD\n"
4507 " root element.\n"
4508 "\n"
4509 " Note that the new content must begin with an XML element node. Also note\n"
4510 " that an existing child node of the same element name as the root node of\n"
4511 " the imported content will be overwritten.\n"
4514 new_command("DUMP", FALSE, dump_command, N_(
4515 "DUMP\n"
4516 " Shows the in memory XML document with indenting. To dump a specific\n"
4517 " element tree, use the XPATH command.\n"
4520 new_command("LOCK", FALSE, lock_command, N_(
4521 "LOCK\n"
4522 " Locks the mutex associated with the opened file. This prevents other\n"
4523 " clients from sending commands to the same opened file until the client\n"
4524 " that sent this command either disconnects or sends the UNLOCK command.\n"
4527 new_command("UNLOCK", FALSE, unlock_command, N_(
4528 "UNLOCK\n"
4529 " Unlocks the file mutex which was locked with the LOCK command.\n"
4532 new_command("GETPID", TRUE, getpid_command, N_(
4533 "GETPID\n"
4534 " Retrieves the process id of the server.\n"
4537 new_command("GETCONFIG", TRUE, getconfig_command, N_(
4538 "GETCONFIG [filename] <parameter>\n"
4539 " Returns the value of a pwmd configuration variable with a data response.\n"
4540 " If no file has been opened then the value for the specified file or the\n"
4541 " default from the \"global\" section will be returned. If a file has been\n"
4542 " opened and no filename is specified, the value previously set with the SET\n"
4543 " command, if any, will be returned.\n"
4544 "\n"
4545 " If there is no such configuration parameter defined, GPG_ERR_UNKNOWN_OPTION\n"
4546 " is returned.\n"
4549 new_command("VERSION", TRUE, version_command, N_(
4550 "VERSION\n"
4551 " Returns the server version number and compile-time features with a data\n"
4552 " response. Each feature is space delimited.\n"
4555 new_command("SET", TRUE, set_command, N_(
4556 "SET <NAME>=<VALUE>\n"
4557 " Sets a client option NAME to VALUE. Use the UNSET command to reset an\n"
4558 " option to its default value.\n"
4559 "\n"
4560 " NAME |VALUE |Description\n"
4561 " -----------------|----------|----------------------------------------------\n"
4562 " ENABLE_PINENTRY 0|1 When 0, disable use of pinentry. The default\n"
4563 " is 1.\n"
4564 "\n"
4565 " * Deprecated. Pass --pinentry to the OPEN and\n"
4566 " SAVE commands instead.\n"
4567 "\n"
4568 " PINENTRY_TIMEOUT <integer> The number of seconds before the pinentry\n"
4569 " process will terminate while waiting for a\n"
4570 " passphrase. The default is 20, 0 disables.\n"
4571 "\n"
4572 " PINENTRTY_PATH <string> Full path to the pinentry binary. The default\n"
4573 " is specified at compile time.\n"
4574 "\n"
4575 " TTYNAME <string> Same as the --ttyname option to pinentry(1).\n"
4576 "\n"
4577 " TTYTYPE <string> Same as the --ttytype option to pinentry(1).\n"
4578 "\n"
4579 " DISPLAY <string> Same as the --display option to pinentry(1).\n"
4580 "\n"
4581 " TITLE <string> Sets the title string of the pinentry dialog.\n"
4582 "\n"
4583 " PROMPT <string> Sets the prompt string of the pinentry dialog.\n"
4584 "\n"
4585 " DESC <string> Sets the error or description string of the\n"
4586 " pinentry dialog.\n"
4587 "\n"
4588 " LC_CTYPE <string> Same as the --lc-ctype option to pinentry(1).\n"
4589 "\n"
4590 " LC_MESSAGES <string> Same as the --lc-messages option to\n"
4591 " pinentry(1).\n"
4592 "\n"
4593 " NAME <string> Associates the thread ID of the connection\n"
4594 " with the specified textual representation.\n"
4595 " Useful for debugging log messages.\n"
4596 "\n"
4597 " CIPHER <string> The cipher to use for the next SAVE.\n"
4598 "\n"
4599 " * Deprecated. Use --cipher instead.\n"
4600 "\n"
4601 " ITERATIONS <integer> The number of encryption iterations to do\n"
4602 " when the SAVE command is sent. An opened file\n"
4603 " is needed when setting this option. The\n"
4604 " CONFIG status message is sent after receiving\n"
4605 " this command.\n"
4606 "\n"
4607 " * Deprecated. Use --iterations instead.\n"
4608 "\n"
4609 " LOCK_ON_OPEN 0|1 If enabled then the file mutex will be locked\n"
4610 " after a successful OPEN as if the LOCK\n"
4611 " command had been sent.\n"
4612 "\n"
4613 " * Deprecated. Use --lock instead.\n"
4614 "\n"
4615 " RC_ON_LOCKED 0|1 If enabled then return an error code instead\n"
4616 " of a status message when the file mutex is\n"
4617 " locked by another thread.\n"
4620 new_command("UNSET", TRUE, unset_command, N_(
4621 "UNSET <NAME>\n"
4622 " Resets option NAME to the value specified in the server configuration\n"
4623 " file. Some options have no default and will be reset to NULL or 0\n"
4624 " depending on the value type. See the SET command for available options.\n"
4627 new_command("LS", TRUE, ls_command, N_(
4628 "LS\n"
4629 " Lists the contents of the configured data_directory. The result is a\n"
4630 " newline separated list of filenames.\n"
4633 new_command("RESET", TRUE, NULL, N_(
4634 "RESET\n"
4635 " Closes the currently opened file but keeps any previously set client\n"
4636 " options.\n"
4639 new_command("BYE", TRUE, NULL, N_(
4640 "BYE\n"
4641 " Closes the connection discarding any unsaved changes.\n"
4644 new_command("NOP", TRUE, NULL, N_(
4645 "NOP\n"
4646 " Does nothing. Always returns successfully.\n"
4649 /* !END-HELP-TEXT! */
4650 new_command("CANCEL", TRUE, NULL, NULL);
4651 new_command("END", TRUE, NULL, NULL);
4653 gint i;
4654 for (i = 0; command_table[i]; i++);
4655 qsort(command_table, i-1, sizeof(struct command_table_s *), sort_commands);
4658 gpg_error_t register_commands(assuan_context_t ctx)
4660 gint i = 0, rc;
4662 for (; command_table[i]; i++) {
4663 if (!command_table[i]->handler)
4664 continue;
4666 rc = assuan_register_command (ctx, command_table[i]->name,
4667 command_table[i]->handler);
4669 if (rc)
4670 return rc;
4673 rc = assuan_register_bye_notify(ctx, bye_notify);
4675 if (rc)
4676 return rc;
4678 rc = assuan_register_reset_notify(ctx, reset_notify);
4680 if (rc)
4681 return rc;
4683 rc = assuan_register_pre_cmd_notify(ctx, command_startup);
4685 if (rc)
4686 return rc;
4688 return assuan_register_post_cmd_notify(ctx, command_finalize);
4691 gpg_error_t try_xml_decrypt(assuan_context_t ctx,
4692 struct crypto_s *crypto, gpointer *dst, goffset *dst_len)
4694 goffset insize, len;
4695 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
4696 guint64 iter = 0ULL, n_iter = 0ULL, iter_progress = 0ULL;
4697 gulong outsize = 0;
4698 gpg_error_t rc;
4699 gsize fh_size = crypto->fh->v1 ? sizeof(crypto->fh->ver.fh1) : sizeof(crypto->fh->ver.fh2);
4700 guint64 fh_iter = crypto->fh->v1 ? crypto->fh->ver.fh1.iter : crypto->fh->ver.fh2.iter;
4701 gsize hashlen = gcry_md_get_algo_dlen(GCRY_MD_SHA256);
4703 lseek(crypto->fh->fd, fh_size, SEEK_SET);
4704 insize = crypto->fh->st.st_size - fh_size;
4705 crypto->iv = gcry_malloc(crypto->blocksize);
4707 if (!crypto->iv) {
4708 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
4709 return GPG_ERR_ENOMEM;
4712 if (crypto->fh->v1)
4713 memcpy(crypto->iv, crypto->fh->ver.fh1.iv, crypto->blocksize);
4714 else
4715 memcpy(crypto->iv, crypto->fh->ver.fh2.iv, crypto->blocksize);
4717 crypto->inbuf = gcry_malloc(insize);
4719 if (!crypto->inbuf) {
4720 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
4721 return GPG_ERR_ENOMEM;
4724 crypto->insize = insize;
4725 len = pth_read(crypto->fh->fd, crypto->inbuf, crypto->insize);
4727 if (len != crypto->insize)
4728 return GPG_ERR_INV_LENGTH;
4730 /* No encryption iterations. This is a plain (gzipped) file. */
4731 if ((crypto->fh->v1 && (long)fh_iter < 0L) ||
4732 (!crypto->fh->v1 && fh_iter <= 0L)) {
4734 * cache_file_count() needs both .used == TRUE and a valid key in
4735 * order for it to count as a used cache entry. Fixes CACHE status
4736 * messages.
4738 memset(crypto->key, '!', hashlen);
4739 goto decompress;
4742 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, crypto->blocksize))) {
4743 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
4744 return rc;
4747 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->key, crypto->keysize))) {
4748 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
4749 return rc;
4752 iter_progress = (guint64)get_key_file_double(client && client->filename ?
4753 client->filename : "global", "iteration_progress");
4755 if (iter_progress > 0ULL && fh_iter >= iter_progress) {
4756 rc = send_status(ctx, STATUS_DECRYPT, "0 %llu", fh_iter);
4758 if (rc)
4759 return rc;
4762 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
4764 if (rc)
4765 return rc;
4767 crypto->tkey = gcry_malloc(hashlen);
4769 if (!crypto->tkey) {
4770 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(GPG_ERR_ENOMEM));
4771 return GPG_ERR_ENOMEM;
4774 memcpy(crypto->tkey, crypto->key, hashlen);
4775 guchar *tkey = crypto->tkey;
4776 tkey[0] ^= 1;
4778 if ((rc = gcry_cipher_setkey(crypto->gh, crypto->tkey, crypto->keysize))) {
4779 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
4780 return rc;
4783 while (iter < (crypto->fh->v1 ? fh_iter : fh_iter-1)) {
4784 if (iter_progress > 0ULL && iter >= iter_progress) {
4785 if (!(iter % iter_progress)) {
4786 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu",
4787 ++n_iter * iter_progress, fh_iter);
4789 if (rc)
4790 return rc;
4794 if ((rc = gcry_cipher_setiv(crypto->gh, crypto->iv, crypto->blocksize))) {
4795 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
4796 return rc;
4799 rc = iterate_crypto_once(client, crypto, STATUS_DECRYPT);
4801 if (rc) {
4802 log_write("%s(%i): %s", __FUNCTION__, __LINE__, pwmd_strerror(rc));
4803 return rc;
4806 iter++;
4809 if (iter_progress && fh_iter >= iter_progress) {
4810 rc = send_status(ctx, STATUS_DECRYPT, "%llu %llu", fh_iter, fh_iter);
4812 if (rc)
4813 return rc;
4816 decompress:
4817 if (!crypto->fh->v1 && crypto->fh->ver.fh2.version >= 0x218) {
4818 len = 0;
4820 if (crypto->fh->ver.fh2.iter > 0ULL) {
4821 if (memcmp(crypto->inbuf, crypto_magic, sizeof(crypto_magic)))
4822 return GPG_ERR_INV_PASSPHRASE;
4824 len = sizeof(crypto_magic);
4827 rc = do_decompress(ctx, (guchar *)crypto->inbuf+len, crypto->insize-len,
4828 (gpointer *)&crypto->outbuf, &outsize);
4830 if (rc)
4831 return rc;
4833 else {
4834 rc = do_decompress(ctx, crypto->inbuf, crypto->insize,
4835 (gpointer *)&crypto->outbuf, &outsize);
4837 if (rc == GPG_ERR_ENOMEM)
4838 return rc;
4839 else if (rc)
4840 return GPG_ERR_INV_PASSPHRASE; // Not a valid gzip header. Must be a bad key.
4842 if (g_strncasecmp(crypto->outbuf, "<?xml ", 6) != 0) {
4843 gcry_free(crypto->outbuf);
4844 crypto->outbuf = NULL;
4845 return GPG_ERR_INV_PASSPHRASE;
4849 if (ctx) {
4850 client->xml = crypto->outbuf;
4851 client->len = outsize;
4852 crypto->outbuf = NULL;
4854 else if (dst) {
4855 *dst = crypto->outbuf;
4856 *dst_len = outsize;
4857 crypto->outbuf = NULL;
4860 /* The calling function should free the crypto struct. */
4861 return 0;