Fixed some compiler warnings.
[pwmd.git] / src / commands.c
blobee57cc06c8b29b1f5f1bde28f1a717672741a7c6
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2008 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 02111-1307 USA
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <unistd.h>
22 #include <err.h>
23 #include <errno.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <ctype.h>
28 #include <glib.h>
29 #include <gcrypt.h>
30 #include <zlib.h>
32 #ifdef HAVE_CONFIG_H
33 #include <config.h>
34 #endif
36 #ifndef MEM_DEBUG
37 #include "mem.h"
38 #endif
40 #include "xml.h"
41 #include "common.h"
43 #ifdef WITH_PINENTRY
44 #include "pinentry.h"
45 #endif
47 #include "pwmd_error.h"
48 #include "cache.h"
49 #include "misc.h"
50 #include "commands.h"
52 static void *z_alloc(void *data, unsigned items, unsigned size)
54 #ifndef MEM_DEBUG
55 return gcry_calloc(items, size);
56 #else
57 return calloc(items, size);
58 #endif
61 static void z_free(void *data, void *p)
63 #ifndef MEM_DEBUG
64 gcry_free(p);
65 #else
66 free(p);
67 #endif
70 static gpg_error_t file_modified(struct client_s *client)
72 struct stat st;
73 gpg_error_t rc;
75 if (client->state != STATE_OPEN)
76 return EPWMD_NO_FILE;
78 rc = lock_file_mutex(client);
80 if (rc)
81 return rc;
83 if (stat(client->filename, &st) == 0 && client->mtime) {
84 if (client->mtime != st.st_mtime)
85 return EPWMD_FILE_MODIFIED;
88 return 0;
91 static gboolean encrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
92 void *inbuf, gsize insize)
94 gpg_error_t rc;
96 if ((rc = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
97 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
98 return FALSE;
101 return TRUE;
104 static gpg_error_t decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
105 void *inbuf, gsize insize)
107 gpg_error_t rc;
109 if ((rc = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize)))
110 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
112 return rc;
115 static gpg_error_t parse_xml(assuan_context_t ctx)
117 struct client_s *client = assuan_get_pointer(ctx);
119 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
121 if (!client->doc)
122 return EPWMD_LIBXML_ERROR;
124 return 0;
127 void unlock_file_mutex(struct client_s *client)
129 pth_mutex_t *m;
131 #ifdef WITH_PINENTRY
132 if (client->has_lock == FALSE || client->pinentry->status != PINENTRY_NONE)
133 #else
134 if (client->has_lock == FALSE)
135 #endif
136 return;
138 CACHE_LOCK(client->ctx);
140 if (cache_get_mutex(client->md5file, &m) == FALSE) {
141 CACHE_UNLOCK;
142 return;
145 CACHE_UNLOCK;
146 pth_mutex_release(m);
147 client->has_lock = client->is_lock_cmd = FALSE;
150 gpg_error_t lock_file_mutex(struct client_s *client)
152 pth_mutex_t *m;
154 if (client->has_lock == TRUE)
155 return 0;
157 CACHE_LOCK(client->ctx);
159 if (cache_get_mutex(client->md5file, &m) == FALSE) {
160 CACHE_UNLOCK;
161 return 0;
164 CACHE_UNLOCK;
166 if (pth_mutex_acquire(m, TRUE, NULL) == FALSE) {
167 if (errno == EBUSY) {
168 if (client->ctx) {
170 * If a client disconnects unexpectedly while waiting for a
171 * lock, this lets the thread terminate because send_status()
172 * will return an error. We can't use an PTH_EVENT_FUNC here
173 * because the thread that would call the callback uses it's
174 * own stack space which messes up access to the client data
175 * (if I understand it correctly).
177 while (pth_mutex_acquire(m, TRUE, NULL) == FALSE) {
178 gpg_error_t rc = send_status(client->ctx, STATUS_LOCKED);
180 if (rc)
181 return rc;
183 pth_sleep(1);
186 else
187 pth_mutex_acquire(m, FALSE, NULL);
189 else {
190 gint e = errno;
191 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(errno));
192 return gpg_error_from_errno(e);
196 client->has_lock = TRUE;
197 return 0;
200 void free_client(struct client_s *client)
202 if (client->doc)
203 xmlFreeDoc(client->doc);
205 if (client->xml)
206 gcry_free(client->xml);
208 if (client->filename)
209 g_free(client->filename);
211 if (client->gh)
212 gcry_cipher_close(client->gh);
215 void cleanup_client(struct client_s *client)
217 assuan_context_t ctx = client->ctx;
218 #ifdef WITH_PINENTRY
219 struct pinentry_s *pin = client->pinentry;
220 #endif
222 unlock_file_mutex(client);
223 CACHE_LOCK(client->ctx);
224 cache_decr_refcount(client->md5file);
227 * This may be a new file so don't use a cache slot. save_command() will
228 * set this to FALSE on success.
230 if (client->new == TRUE)
231 cache_clear(client->md5file, 1);
233 free_client(client);
234 memset(client, 0, sizeof(struct client_s));
235 client->state = STATE_CONNECTED;
236 client->ctx = ctx;
237 client->freed = TRUE;
238 #ifdef WITH_PINENTRY
239 client->pinentry = pin;
240 #endif
241 CACHE_UNLOCK;
244 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
245 gpointer *out, glong *outsize, gint *rc)
247 z_stream z;
248 gpointer pout;
249 gz_header h;
250 gchar buf[17];
251 gchar str[ASSUAN_LINELENGTH];
253 z.zalloc = z_alloc;
254 z.zfree = z_free;
255 z.next_in = in;
256 z.avail_in = insize;
257 z.avail_out = zlib_bufsize;
258 z.next_out = pout = g_malloc(zlib_bufsize);
260 if (!pout) {
261 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
262 *rc = Z_MEM_ERROR;
263 return FALSE;
266 *rc = inflateInit2(&z, 47);
268 if (*rc != Z_OK) {
269 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
270 g_free(pout);
271 return FALSE;
274 memset(&h, 0, sizeof(gz_header));
275 h.comment = (guchar *)buf;
276 h.comm_max = sizeof(buf);
277 *rc = inflateGetHeader(&z, &h);
279 if (*rc != Z_OK) {
280 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
281 g_free(pout);
282 inflateEnd(&z);
283 return FALSE;
286 *rc = inflate(&z, Z_BLOCK);
288 if (*rc != Z_OK) {
289 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
290 g_free(pout);
291 inflateEnd(&z);
292 return FALSE;
295 if (h.comment)
296 insize = atoi((gchar *)h.comment);
298 do {
299 gpointer p;
301 *rc = inflate(&z, Z_FINISH);
303 switch (*rc) {
304 case Z_OK:
305 break;
306 case Z_BUF_ERROR:
307 if (!z.avail_out) {
308 p = g_realloc(pout, z.total_out + zlib_bufsize);
310 if (!p) {
311 *rc = Z_MEM_ERROR;
312 goto fail;
315 pout = p;
316 z.next_out = pout + z.total_out;
317 z.avail_out = zlib_bufsize;
319 if (ctx) {
320 *rc = assuan_write_status(ctx, "DECOMPRESS",
321 print_fmt(str, sizeof(str), "%i %i", z.total_out, insize));
323 if (*rc)
324 goto fail;
327 break;
328 case Z_STREAM_END:
329 break;
330 default:
331 goto fail;
332 break;
335 pth_yield(NULL);
336 } while (*rc != Z_STREAM_END);
338 if (ctx) {
339 *rc = assuan_write_status(ctx, "DECOMPRESS",
340 print_fmt(str, sizeof(str), "%i %i", z.total_out, insize));
342 if (*rc)
343 goto fail;
346 *out = pout;
347 *outsize = z.total_out;
348 inflateEnd(&z);
349 *rc = 0;
350 return TRUE;
352 fail:
353 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
354 g_free(pout);
355 inflateEnd(&z);
356 return FALSE;
359 gpg_error_t read_file_header(const gchar *filename, file_header_t *fh)
361 gint fd;
362 gsize len;
364 fd = open(filename, O_RDONLY);
366 if (fd == -1)
367 return gpg_error_from_errno(errno);
369 len = pth_read(fd, fh, sizeof(file_header_t));
370 close(fd);
372 if (len != sizeof(file_header_t))
373 return gpg_error_from_errno(errno);
375 return 0;
378 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar shakey[],
379 gboolean cached)
381 struct client_s *client = assuan_get_pointer(ctx);
382 gpg_error_t rc;
383 struct stat st;
384 gint fd;
385 gint timeout;
386 gint iter;
388 if ((fd = open_file(client->filename, &st)) == -1) {
389 /* New file. */
390 if (errno == ENOENT) {
391 if (shakey[0])
392 goto update_cache;
394 goto done;
397 rc = errno;
398 log_write("%s: %s", client->filename, strerror(errno));
399 cleanup_client(client);
400 memset(shakey, 0, sizeof(shakey));
401 return send_syserror(ctx, rc);
404 rc = try_xml_decrypt(ctx, fd, st, shakey, &iter);
405 close(fd);
407 if (rc) {
408 memset(shakey, 0, sizeof(shakey));
409 cleanup_client(client);
410 return send_error(ctx, rc);
413 update_cache:
414 CACHE_LOCK(client->ctx);
416 if (cached == FALSE) {
417 if (cache_update_key(client->md5file, shakey) == FALSE) {
418 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
419 cleanup_client(client);
420 CACHE_UNLOCK;
421 return send_error(ctx, EPWMD_MAX_SLOTS);
424 timeout = get_key_file_integer(client->filename, "cache_timeout");
425 cache_reset_timeout(client->md5file, timeout);
427 else
428 cache_set_timeout(client->md5file, -2);
430 CACHE_UNLOCK;
432 done:
433 memset(shakey, 0, sizeof(shakey));
434 rc = parse_xml(ctx);
436 if (client->xml) {
437 gcry_free(client->xml);
438 client->xml = NULL;
441 if (!rc) {
442 if (client->new == FALSE)
443 send_status_all(STATUS_CACHE);
445 client->state = STATE_OPEN;
448 if (!rc && client->new == FALSE &&
449 iter != get_key_file_integer(client->filename, "iterations")) {
450 g_key_file_set_integer(keyfileh, client->filename, "iterations", iter);
451 send_status_all(STATUS_CONFIG);
454 return send_error(ctx, rc);
457 static int open_command(assuan_context_t ctx, char *line)
459 struct stat st;
460 guchar shakey[gcrykeysize];
461 gboolean cached = FALSE;
462 gpg_error_t rc;
463 struct client_s *client = assuan_get_pointer(ctx);
464 gchar **req;
465 gchar *filename = NULL;
466 file_header_t file_header;
468 memset(shakey, 0, sizeof(shakey));
470 if ((req = split_input_line(line, " ", 2)) != NULL)
471 filename = req[0];
473 if (!filename || !*filename) {
474 g_strfreev(req);
475 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
478 if (valid_filename(filename) == FALSE) {
479 g_strfreev(req);
480 return send_error(ctx, EPWMD_INVALID_FILENAME);
483 if (client->state == STATE_OPEN)
484 cleanup_client(client);
486 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
487 CACHE_LOCK(client->ctx);
489 if (cache_has_file(client->md5file) == FALSE) {
490 if (cache_add_file(client->md5file, NULL) == FALSE) {
491 g_strfreev(req);
492 CACHE_UNLOCK;
493 return send_error(ctx, EPWMD_MAX_SLOTS);
497 cache_incr_refcount(client->md5file);
498 CACHE_UNLOCK;
499 rc = lock_file_mutex(client);
501 if (rc) {
502 g_strfreev(req);
503 return send_error(ctx, rc);
506 client->freed = FALSE;
508 if ((rc = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
509 g_strfreev(req);
510 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
511 cleanup_client(client);
512 return send_error(ctx, rc);
515 if (stat(filename, &st) == 0) {
516 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
517 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
518 g_strfreev(req);
519 cleanup_client(client);
520 return send_error(ctx, EPWMD_INVALID_FILENAME);
523 client->mtime = st.st_mtime;
526 client->filename = g_strdup(filename);
528 if (!client->filename) {
529 memset(shakey, 0, sizeof(shakey));
530 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
531 cleanup_client(client);
532 g_strfreev(req);
533 return send_syserror(ctx, ENOMEM);
536 #ifdef WITH_PINENTRY
537 client->pinentry->filename = g_strdup(client->filename);
539 if (!client->pinentry->filename) {
540 memset(shakey, 0, sizeof(shakey));
541 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
542 cleanup_client(client);
543 g_strfreev(req);
544 return send_syserror(ctx, ENOMEM);
546 #endif
549 * New files don't need a key.
551 if (access(filename, R_OK) != 0) {
552 if (errno != ENOENT) {
553 rc = errno;
554 log_write("%s: %s", filename, strerror(errno));
555 g_strfreev(req);
556 cleanup_client(client);
557 return send_syserror(ctx, rc);
560 if ((client->xml = new_document()) == NULL) {
561 log_write("%s", strerror(ENOMEM));
562 g_strfreev(req);
563 cleanup_client(client);
564 return send_syserror(ctx, ENOMEM);
567 client->len = xmlStrlen(client->xml);
568 client->new = TRUE;
569 client->filename = g_strdup(filename);
571 if (!client->filename) {
572 g_strfreev(req);
573 cleanup_client(client);
574 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
575 return send_syserror(ctx, ENOMEM);
578 memset(shakey, 0, sizeof(shakey));
580 if (req[1] && *req[1])
581 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
583 g_strfreev(req);
584 return open_command_finalize(ctx, shakey, cached);
587 rc = read_file_header(filename, &file_header);
589 if (rc) {
590 g_strfreev(req);
591 cleanup_client(client);
592 return send_error(ctx, rc);
595 if (file_header.iter == -1)
596 goto done;
598 CACHE_LOCK(client->ctx);
599 cached = cache_get_key(client->md5file, shakey);
600 CACHE_UNLOCK;
602 if (cached == FALSE) {
604 * No key specified and no matching filename found in the cache. Use
605 * pinentry to retrieve the key. Cannot return assuan_process_done()
606 * here otherwise the command will be interrupted. The event loop in
607 * client_thread() will poll the file descriptor waiting for it to
608 * become ready to read a pinentry_key_s which will contain the
609 * entered key or rc. It will then call open_command_finalize() to
610 * to finish the command.
612 if (!req[1] || !*req[1]) {
613 #ifdef WITH_PINENTRY
614 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
616 /* From set_pinentry_defaults(). */
617 if (client->pinentry->enable == FALSE ||
618 (client->pinentry->enable == -1 && b == FALSE)) {
619 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
620 goto done;
623 g_strfreev(req);
624 rc = lock_pin_mutex(client);
626 if (rc) {
627 unlock_pin_mutex(client->pinentry);
628 cleanup_client(client);
629 return send_error(ctx, rc);
632 client->pinentry->which = PINENTRY_OPEN;
633 rc = pinentry_fork(ctx);
635 if (rc) {
636 unlock_pin_mutex(client->pinentry);
637 cleanup_client(client);
638 return send_error(ctx, rc);
641 client->pinentry->cb = open_command_finalize;
642 client->pinentry->status = PINENTRY_INIT;
643 return 0;
644 #else
645 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
646 goto done;
647 #endif
650 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
653 done:
654 g_strfreev(req);
655 return open_command_finalize(ctx, shakey, cached);
658 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
659 gint size, gpointer *out, glong *outsize, gint *rc)
661 z_stream z;
662 gpointer pout, pin;
663 gz_header h;
664 gchar buf[17];
665 gint cmd = Z_NO_FLUSH;
666 gchar str[ASSUAN_LINELENGTH];
668 z.zalloc = z_alloc;
669 z.zfree = z_free;
670 z.next_in = pin = data;
671 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
672 z.avail_out = zlib_bufsize;
673 z.next_out = pout = g_malloc(zlib_bufsize);
675 if (!pout) {
676 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
677 *rc = Z_MEM_ERROR;
678 return FALSE;
681 *rc = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
683 if (*rc != Z_OK) {
684 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
685 g_free(pout);
686 return FALSE;
689 memset(&h, 0, sizeof(gz_header));
690 g_snprintf(buf, sizeof(buf), "%i", size);
691 h.comment = (guchar *)buf;
692 *rc = deflateSetHeader(&z, &h);
694 if (*rc != Z_OK) {
695 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
696 g_free(pout);
697 deflateEnd(&z);
698 return FALSE;
701 do {
702 gpointer p;
704 *rc = deflate(&z, cmd);
706 switch (*rc) {
707 case Z_OK:
708 break;
709 case Z_BUF_ERROR:
710 if (!z.avail_out) {
711 p = g_realloc(pout, z.total_out + zlib_bufsize);
713 if (!p) {
714 *rc = Z_MEM_ERROR;
715 goto fail;
718 pout = p;
719 z.next_out = pout + z.total_out;
720 z.avail_out = zlib_bufsize;
723 if (!z.avail_in && z.total_in < size) {
724 if (z.total_in + zlib_bufsize > size)
725 z.avail_in = size - z.total_in;
726 else
727 z.avail_in = zlib_bufsize;
729 if (ctx) {
730 *rc = assuan_write_status(ctx, "COMPRESS",
731 print_fmt(str, sizeof(str), "%i %i", z.total_in, size));
733 if (*rc)
734 goto fail;
738 if (z.total_in >= size)
739 cmd = Z_FINISH;
741 break;
742 case Z_STREAM_END:
743 break;
744 default:
745 goto fail;
748 pth_yield(NULL);
749 } while (*rc != Z_STREAM_END);
751 if (ctx) {
752 *rc = assuan_write_status(ctx, "COMPRESS",
753 print_fmt(str, sizeof(str), "%i %i", z.total_in, size));
755 if (*rc)
756 goto fail;
759 *out = pout;
760 *outsize = z.total_out;
761 deflateEnd(&z);
762 *rc = 0;
763 return TRUE;
765 fail:
766 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
767 g_free(pout);
768 deflateEnd(&z);
769 return FALSE;
772 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
773 const gchar *filename, gpointer data, size_t insize, guchar *shakey,
774 gint iter)
776 gsize len = insize;
777 gint fd;
778 gpointer inbuf;
779 guchar tkey[gcrykeysize];
780 gchar *p;
781 gpg_error_t rc;
782 guint iter_progress = 0, n_iter = 0, xiter = 0;
783 gchar tmp[FILENAME_MAX];
784 struct stat st;
785 mode_t mode = 0;
786 file_header_t file_header;
787 gchar str[ASSUAN_LINELENGTH];
789 if (iter == -1) {
791 * cache_file_count() needs both .used == TRUE and a valid key in
792 * order for it to count as a used cache entry. Fixes CACHE status
793 * messages.
795 memset(shakey, '!', gcrykeysize);
796 inbuf = data;
797 file_header.iter = iter;
798 goto write_file;
801 if (insize / gcryblocksize) {
802 len = (insize / gcryblocksize) * gcryblocksize;
804 if (insize % gcryblocksize)
805 len += gcryblocksize;
809 * Resize the existing xml buffer to the block size required by gcrypt
810 * rather than duplicating it and wasting memory.
812 inbuf = gcry_realloc(data, len);
814 if (!inbuf)
815 return gpg_error_from_errno(ENOMEM);
817 insize = len;
818 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
819 memcpy(tkey, shakey, sizeof(tkey));
820 tkey[0] ^= 1;
822 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
823 memset(tkey, 0, sizeof(tkey));
824 gcry_free(inbuf);
825 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
826 return rc;
829 memset(tkey, 0, sizeof(tkey));
830 file_header.iter = iter;
832 if (client)
833 iter_progress = get_key_file_integer(client->filename, "iteration_progress");
835 if (client && iter_progress && file_header.iter >= iter_progress) {
836 rc = assuan_write_status(client->ctx, "ENCRYPT",
837 print_fmt(str, sizeof(str), "%i %i", 0, file_header.iter));
839 if (rc) {
840 gcry_free(inbuf);
841 return rc;
845 while (xiter < file_header.iter) {
846 if (client && iter_progress > 0 && xiter >= iter_progress) {
847 if (!(xiter % iter_progress)) {
848 rc = assuan_write_status(client->ctx, "ENCRYPT",
849 print_fmt(str, sizeof(str), "%i %i", ++n_iter * iter_progress, file_header.iter));
851 if (rc) {
852 gcry_free(inbuf);
853 return rc;
858 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
859 sizeof(file_header.iv)))) {
860 gcry_free(inbuf);
861 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
862 return rc;
865 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
866 == FALSE) {
867 gcry_free(inbuf);
868 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
869 return rc;
872 xiter++;
873 pth_yield(NULL);
876 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
877 sizeof(file_header.iv)))) {
878 gcry_free(inbuf);
879 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
880 return rc;
883 if ((rc = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
884 gcry_free(inbuf);
885 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
886 return rc;
889 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
890 gcry_free(inbuf);
891 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
892 return rc;
895 if (client && iter_progress && file_header.iter >= iter_progress) {
896 rc = assuan_write_status(client->ctx, "ENCRYPT",
897 print_fmt(str, sizeof(str), "%i %i", file_header.iter, file_header.iter));
899 if (rc) {
900 gcry_free(inbuf);
901 return rc;
905 write_file:
906 if (filename) {
907 if (stat(filename, &st) == 0) {
908 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
911 * FIXME What if the file has an ACL?
913 if (!(mode & S_IWUSR)) {
914 gcry_free(inbuf);
915 return gpg_error_from_errno(EACCES);
918 else {
919 if (errno != ENOENT) {
920 rc = errno;
921 gcry_free(inbuf);
922 return gpg_error_from_errno(rc);
926 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
928 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
929 rc = errno;
930 gcry_free(inbuf);
931 p = strrchr(tmp, '/');
932 p++;
933 log_write("%s: %s", p, strerror(rc));
934 return gpg_error_from_errno(rc);
937 else
939 * xml_import() from command line.
941 fd = STDOUT_FILENO;
943 len = pth_write(fd, &file_header, sizeof(file_header_t));
945 if (len != sizeof(file_header)) {
946 len = errno;
948 if (filename) {
949 close(fd);
950 unlink(tmp);
953 gcry_free(inbuf);
954 return gpg_error_from_errno(len);
957 len = pth_write(fd, inbuf, insize);
959 if (len != insize) {
960 len = errno;
962 if (filename) {
963 close(fd);
964 unlink(tmp);
967 gcry_free(inbuf);
968 return gpg_error_from_errno(len);
971 if (fsync(fd) == -1) {
972 len = errno;
974 if (filename) {
975 close(fd);
976 unlink(tmp);
979 gcry_free(inbuf);
980 return gpg_error_from_errno(len);
983 if (filename) {
984 close(fd);
986 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
987 gchar tmp2[FILENAME_MAX];
989 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
991 if (rename(filename, tmp2) == -1) {
992 unlink(tmp);
993 len = errno;
994 gcry_free(inbuf);
995 return gpg_error_from_errno(len);
999 if (rename(tmp, filename) == -1) {
1000 len = errno;
1001 unlink(tmp);
1002 gcry_free(inbuf);
1003 return gpg_error_from_errno(len);
1006 if (mode)
1007 chmod(filename, mode);
1010 gcry_free(inbuf);
1011 return 0;
1014 static gpg_error_t save_command_finalize(assuan_context_t ctx,
1015 guchar shakey[], gboolean cached)
1017 struct client_s *client = assuan_get_pointer(ctx);
1018 gpointer xmlbuf;
1019 xmlChar *p;
1020 gint len;
1021 gint iter;
1022 gint timeout;
1023 gpointer outbuf;
1024 glong outsize = 0;
1025 gint zrc;
1026 gpg_error_t rc;
1027 struct stat st;
1029 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
1030 xmlbuf = p;
1032 iter = get_key_file_integer(client->filename, "compression_level");
1034 if (iter < 0)
1035 iter = 0;
1037 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zrc) == FALSE) {
1038 memset(shakey, 0, sizeof(shakey));
1039 xmlFree(xmlbuf);
1041 if (zrc == Z_MEM_ERROR) {
1042 return send_syserror(ctx, ENOMEM);
1044 else
1045 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1047 else {
1048 gcry_free(xmlbuf);
1049 xmlbuf = outbuf;
1050 len = outsize;
1053 iter = get_key_file_integer(client->filename, "iterations");
1054 rc = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
1056 if (rc) {
1057 memset(shakey, 0, sizeof(shakey));
1058 return send_error(ctx, rc);
1061 stat(client->filename, &st);
1062 client->mtime = st.st_mtime;
1063 timeout = get_key_file_integer(client->filename, "cache_timeout");
1064 CACHE_LOCK(client->ctx);
1066 if (cached) {
1067 memset(shakey, 0, sizeof(shakey));
1068 cache_reset_timeout(client->md5file, timeout);
1069 CACHE_UNLOCK;
1071 if (client->new == TRUE)
1072 send_status_all(STATUS_CACHE);
1074 client->new = FALSE;
1075 return send_error(ctx, 0);
1078 if (cache_update_key(client->md5file, shakey) == FALSE) {
1079 memset(shakey, 0, sizeof(shakey));
1080 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
1081 CACHE_UNLOCK;
1082 return send_error(ctx, EPWMD_MAX_SLOTS);
1085 client->new = FALSE;
1086 memset(shakey, 0, sizeof(shakey));
1087 cache_reset_timeout(client->md5file, timeout);
1088 CACHE_UNLOCK;
1089 send_status_all(STATUS_CACHE);
1090 return send_error(ctx, 0);
1093 static int save_command(assuan_context_t ctx, char *line)
1095 gboolean cached = FALSE;
1096 guchar shakey[gcrykeysize];
1097 struct stat st;
1098 struct client_s *client = assuan_get_pointer(ctx);
1099 gpg_error_t rc;
1101 memset(shakey, 0, sizeof(shakey));
1102 rc = file_modified(client);
1104 if (rc) {
1105 log_write("%s: %s", client->filename ? client->filename : "",
1106 pwmd_strerror(rc));
1107 return send_error(ctx, rc);
1110 rc = lock_file_mutex(client);
1112 if (rc)
1113 return send_error(ctx, rc);
1115 if (stat(client->filename, &st) == -1 && errno != ENOENT)
1116 return send_syserror(ctx, errno);
1118 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
1119 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
1120 return send_error(ctx, EPWMD_INVALID_FILENAME);
1123 if (get_key_file_integer(client->filename, "iterations") == -1)
1124 goto done;
1126 if (!line || !*line) {
1127 guchar tmp[sizeof(shakey)];
1128 CACHE_LOCK(ctx);
1130 memset(tmp, '!', sizeof(tmp));
1132 if (cache_get_key(client->md5file, shakey) == FALSE ||
1133 memcmp(shakey, tmp, sizeof(shakey)) == 0) {
1134 CACHE_UNLOCK;
1135 #ifdef WITH_PINENTRY
1136 if (get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1137 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1138 goto done;
1141 lock_pin_mutex(client);
1142 client->pinentry->which = PINENTRY_SAVE;
1143 rc = pinentry_fork(ctx);
1145 if (rc) {
1146 unlock_pin_mutex(client->pinentry);
1147 return send_error(ctx, rc);
1150 client->pinentry->cb = save_command_finalize;
1151 client->pinentry->status = PINENTRY_INIT;
1152 return 0;
1153 #else
1154 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1155 goto done;
1156 #endif
1158 else {
1159 CACHE_UNLOCK;
1160 cached = TRUE;
1163 else {
1164 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
1165 memset(line, 0, strlen(line));
1168 done:
1169 return save_command_finalize(ctx, shakey, cached);
1172 static int delete_command(assuan_context_t ctx, char *line)
1174 struct client_s *client = assuan_get_pointer(ctx);
1175 gchar **req;
1176 gpg_error_t rc;
1177 xmlNodePtr n;
1179 rc = file_modified(client);
1181 if (rc) {
1182 log_write("%s: %s", client->filename ? client->filename : "",
1183 pwmd_strerror(rc));
1184 return send_error(ctx, rc);
1187 if (strchr(line, '\t'))
1188 req = split_input_line(line, "\t", -1);
1189 else
1190 req = split_input_line(line, " ", -1);
1192 if (!req || !*req)
1193 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1195 n = find_account(client->doc, &req, &rc, NULL, 0);
1197 if (!n) {
1198 g_strfreev(req);
1199 return send_error(ctx, rc);
1203 * No sub-node defined. Remove the entire node (account).
1205 if (!req[1]) {
1206 if (n) {
1207 xmlUnlinkNode(n);
1208 xmlFreeNode(n);
1211 g_strfreev(req);
1212 return send_error(ctx, 0);
1215 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1216 g_strfreev(req);
1218 if (!n)
1219 return send_error(ctx, rc);
1221 if (n) {
1222 xmlUnlinkNode(n);
1223 xmlFreeNode(n);
1226 return send_error(ctx, 0);
1230 * Don't return with assuan_process_done() here. This has been called from
1231 * assuan_process_next() and the command should be finished in
1232 * client_thread().
1234 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1235 gsize len)
1237 assuan_context_t ctx = data;
1238 struct client_s *client = assuan_get_pointer(ctx);
1239 gchar **req;
1240 xmlNodePtr n;
1241 gpg_error_t rc = file_modified(client);
1243 if (assuan_rc || rc) {
1244 if (line)
1245 #ifndef MEM_DEBUG
1246 xfree(line);
1247 #else
1248 free(line);
1249 #endif
1250 return assuan_rc ? assuan_rc : rc;
1253 req = split_input_line((gchar *)line, "\t", 0);
1254 #ifndef MEM_DEBUG
1255 xfree(line);
1256 #else
1257 free(line);
1258 #endif
1260 if (!req || !*req)
1261 return EPWMD_COMMAND_SYNTAX;
1263 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1264 g_strfreev(req);
1265 return EPWMD_INVALID_ELEMENT;
1268 if (valid_element_path(req+1, TRUE) == FALSE) {
1269 g_strfreev(req);
1270 return EPWMD_INVALID_ELEMENT;
1273 again:
1274 n = find_account(client->doc, &req, &rc, NULL, 0);
1276 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1277 rc = new_account(client->doc, *req);
1279 if (rc) {
1280 g_strfreev(req);
1281 return rc;
1284 goto again;
1287 if (!n) {
1288 g_strfreev(req);
1289 return rc;
1292 if (req[1]) {
1293 if (!n->children)
1294 create_elements_cb(n, req+1, &rc, NULL);
1295 else
1296 find_elements(client->doc, n->children, req+1, &rc,
1297 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1300 g_strfreev(req);
1301 client->inquire_status = INQUIRE_DONE;
1302 return rc;
1305 static int store_command(assuan_context_t ctx, char *line)
1307 struct client_s *client = assuan_get_pointer(ctx);
1308 gpg_error_t rc = file_modified(client);
1310 if (rc) {
1311 log_write("%s: %s", client->filename ? client->filename : "",
1312 pwmd_strerror(rc));
1313 return send_error(ctx, rc);
1316 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1318 if (rc)
1319 return send_error(ctx, rc);
1321 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1322 client->inquire_status = INQUIRE_BUSY;
1323 return 0;
1326 static int get_command(assuan_context_t ctx, char *line)
1328 struct client_s *client = assuan_get_pointer(ctx);
1329 gchar **req;
1330 gpg_error_t rc;
1331 xmlNodePtr n;
1333 rc = file_modified(client);
1335 if (rc) {
1336 log_write("%s: %s", client->filename ? client->filename : "",
1337 pwmd_strerror(rc));
1338 return send_error(ctx, rc);
1341 req = split_input_line(line, "\t", -1);
1343 if (!req || !*req) {
1344 g_strfreev(req);
1345 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1348 n = find_account(client->doc, &req, &rc, NULL, 0);
1350 if (!n) {
1351 g_strfreev(req);
1352 return send_error(ctx, rc);
1355 if (req[1])
1356 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1358 g_strfreev(req);
1360 if (rc)
1361 return send_error(ctx, rc);
1363 if (!n || !n->children)
1364 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1366 n = find_text_node(n->children);
1368 if (!n || !n->content || !*n->content)
1369 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1371 rc = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1372 return send_error(ctx, rc);
1375 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1376 gpg_error_t *rc, gchar **req_orig, void *data)
1378 gchar *path = *(gchar **)data;
1379 gchar *tmp = NULL, *result;
1381 if (path) {
1382 g_free(path);
1383 *(gchar **)data = NULL;
1386 path = g_strjoinv("\t", target);
1388 if (!path) {
1389 *rc = gpg_error_from_errno(ENOMEM);
1390 return NULL;
1393 if (req_orig) {
1394 tmp = g_strjoinv("\t", req_orig);
1396 if (!tmp) {
1397 g_free(path);
1398 *rc = gpg_error_from_errno(ENOMEM);
1399 return NULL;
1403 if (tmp && *tmp)
1404 result = g_strdup_printf("%s\t%s", path, tmp);
1405 else
1406 result = g_strdup(path);
1408 if (!result) {
1409 *rc = gpg_error_from_errno(ENOMEM);
1410 g_free(path);
1411 g_free(tmp);
1412 return NULL;
1415 g_free(path);
1416 g_free(tmp);
1417 *(gchar **)data = result;
1418 return node;
1421 static int realpath_command(assuan_context_t ctx, char *line)
1423 gpg_error_t rc;
1424 struct client_s *client = assuan_get_pointer(ctx);
1425 gchar **req;
1426 gchar *t;
1427 gint i;
1428 xmlNodePtr n;
1429 GString *string;
1430 gchar *rp = NULL;
1432 rc = file_modified(client);
1434 if (rc) {
1435 log_write("%s: %s", client->filename ? client->filename : "",
1436 pwmd_strerror(rc));
1437 return send_error(ctx, rc);
1440 if (strchr(line, '\t') != NULL) {
1441 if ((req = split_input_line(line, "\t", 0)) == NULL)
1442 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1444 else {
1445 if ((req = split_input_line(line, " ", 0)) == NULL)
1446 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1449 n = find_account(client->doc, &req, &rc, NULL, 0);
1451 if (!n) {
1452 g_strfreev(req);
1453 return send_error(ctx, rc);
1456 rp = g_strjoinv("\t", req);
1458 if (!rp) {
1459 g_strfreev(req);
1460 return send_syserror(ctx, ENOMEM);
1463 if (req[1]) {
1464 n = find_elements(client->doc, n->children, req+1, &rc,
1465 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1467 if (!n) {
1468 g_free(rp);
1469 g_strfreev(req);
1470 return send_error(ctx, rc);
1474 string = g_string_new(rp);
1475 g_free(rp);
1476 g_strfreev(req);
1478 if (!string)
1479 return send_syserror(ctx, ENOMEM);
1481 again:
1482 for (i = 0, t = string->str + i; *t; t++, i++) {
1483 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1484 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1485 goto again;
1489 rc = assuan_send_data(ctx, string->str, string->len);
1490 g_string_free(string, TRUE);
1491 return send_error(ctx, rc);
1494 static int list_command(assuan_context_t ctx, char *line)
1496 struct client_s *client = assuan_get_pointer(ctx);
1497 gpg_error_t rc;
1498 struct element_list_s *elements = NULL;
1499 gchar *tmp;
1501 if (disable_list_and_dump == TRUE)
1502 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1504 rc = file_modified(client);
1506 if (rc) {
1507 log_write("%s: %s", client->filename, pwmd_strerror(rc));
1508 return send_error(ctx, rc);
1511 if (!*line) {
1512 GString *str;
1514 rc = list_accounts(client->doc, &str);
1516 if (rc)
1517 return send_error(ctx, rc);
1519 rc = assuan_send_data(ctx, str->str, str->len);
1520 g_string_free(str, TRUE);
1521 return send_error(ctx, rc);
1524 elements = g_malloc0(sizeof(struct element_list_s));
1526 if (!elements) {
1527 rc = gpg_err_code_from_errno(ENOMEM);
1528 goto fail;
1531 rc = create_path_list(client->doc, elements, line);
1533 if (rc)
1534 goto fail;
1536 if (elements) {
1537 gint total = g_slist_length(elements->list);
1538 gint i;
1539 GString *str;
1541 if (!total) {
1542 rc = EPWMD_EMPTY_ELEMENT;
1543 goto fail;
1546 str = g_string_new(NULL);
1548 if (!str) {
1549 rc = gpg_err_code_from_errno(ENOMEM);
1550 goto fail;
1553 for (i = 0; i < total; i++) {
1554 tmp = g_slist_nth_data(elements->list, i);
1555 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1558 rc = assuan_send_data(ctx, str->str, str->len);
1559 g_string_free(str, TRUE);
1561 else
1562 rc = EPWMD_EMPTY_ELEMENT;
1564 fail:
1565 if (elements) {
1566 gint total = g_slist_length(elements->list);
1567 gint i;
1569 for (i = 0; i < total; i++) {
1570 tmp = g_slist_nth_data(elements->list, i);
1571 g_free(tmp);
1574 g_slist_free(elements->list);
1576 if (elements->prefix)
1577 g_free(elements->prefix);
1579 g_free(elements);
1582 return send_error(ctx, rc);
1585 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1586 const gchar *value)
1588 xmlAttrPtr a;
1590 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1591 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1593 if (!a)
1594 return EPWMD_LIBXML_ERROR;
1596 else
1597 xmlNodeSetContent(a->children, (xmlChar *)value);
1599 return 0;
1603 * req[0] - element path
1605 static int attribute_list(assuan_context_t ctx, gchar **req)
1607 struct client_s *client = assuan_get_pointer(ctx);
1608 gchar **attrlist = NULL;
1609 gint i = 0;
1610 gchar **path = NULL;
1611 xmlAttrPtr a;
1612 xmlNodePtr n, an;
1613 gchar *line;
1614 gpg_error_t rc;
1616 if (!req || !req[0])
1617 return EPWMD_COMMAND_SYNTAX;
1619 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1621 * The first argument may be only an account.
1623 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1624 return EPWMD_COMMAND_SYNTAX;
1627 n = find_account(client->doc, &path, &rc, NULL, 0);
1629 if (!n) {
1630 g_strfreev(path);
1631 return rc;
1634 if (path[1]) {
1635 n = find_elements(client->doc, n->children, path+1, &rc,
1636 NULL, NULL, NULL, FALSE, 0, NULL);
1638 if (!n) {
1639 g_strfreev(path);
1640 return rc;
1644 g_strfreev(path);
1646 for (a = n->properties; a; a = a->next) {
1647 gchar **pa;
1649 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1650 if (attrlist)
1651 g_strfreev(attrlist);
1653 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1654 return gpg_error_from_errno(ENOMEM);
1657 attrlist = pa;
1658 an = a->children;
1659 attrlist[i] = g_strdup_printf("%s %s", (gchar *)a->name, (gchar *)an->content);
1661 if (!attrlist[i]) {
1662 g_strfreev(attrlist);
1663 return gpg_error_from_errno(ENOMEM);
1666 attrlist[++i] = NULL;
1669 if (!attrlist)
1670 return EPWMD_EMPTY_ELEMENT;
1672 line = g_strjoinv("\n", attrlist);
1674 if (!line) {
1675 g_strfreev(attrlist);
1676 return gpg_error_from_errno(ENOMEM);
1679 rc = assuan_send_data(ctx, line, strlen(line));
1680 g_free(line);
1681 g_strfreev(attrlist);
1682 return rc;
1686 * req[0] - attribute
1687 * req[1] - element path
1689 static int attribute_delete(struct client_s *client, gchar **req)
1691 xmlAttrPtr a;
1692 xmlNodePtr n;
1693 gchar **path = NULL;
1694 gpg_error_t rc;
1696 if (!req || !req[0] || !req[1])
1697 return EPWMD_COMMAND_SYNTAX;
1699 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1701 * The first argument may be only an account.
1703 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1704 return EPWMD_COMMAND_SYNTAX;
1708 * Don't remove the "name" attribute for the account element. To remove an
1709 * account use DELETE <account>.
1711 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1712 rc = EPWMD_ATTR_SYNTAX;
1713 goto fail;
1716 n = find_account(client->doc, &path, &rc, NULL, 0);
1718 if (!n)
1719 goto fail;
1721 if (path[1]) {
1722 n = find_elements(client->doc, n->children, path+1, &rc,
1723 NULL, NULL, NULL, FALSE, 0, NULL);
1725 if (!n)
1726 goto fail;
1729 g_strfreev(path);
1731 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1732 return EPWMD_ATTR_NOT_FOUND;
1734 if (xmlRemoveProp(a) == -1)
1735 return EPWMD_LIBXML_ERROR;
1737 return 0;
1739 fail:
1740 g_strfreev(path);
1741 return rc;
1744 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
1745 gpg_error_t *rc)
1747 gchar **src = *path;
1748 gchar **src_orig = g_strdupv(src);
1749 xmlNodePtr n = NULL;
1751 *rc = 0;
1753 if (!src_orig) {
1754 *rc = gpg_error_from_errno(ENOMEM);
1755 goto fail;
1758 again:
1759 n = find_account(client->doc, &src, rc, NULL, 0);
1761 if (!n) {
1762 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
1763 *rc = new_account(client->doc, src[0]);
1765 if (*rc)
1766 goto fail;
1768 goto again;
1770 else
1771 goto fail;
1774 if (src[1]) {
1775 if (!n->children)
1776 n = create_target_elements_cb(n, src+1, rc, NULL);
1777 else
1778 n = find_elements(client->doc, n->children, src+1, rc,
1779 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
1781 if (!n)
1782 goto fail;
1785 * Reset the position of the element tree now that the elements
1786 * have been created.
1788 g_strfreev(src);
1789 src = src_orig;
1790 src_orig = NULL;
1791 n = find_account(client->doc, &src, rc, NULL, 0);
1793 if (!n)
1794 goto fail;
1796 n = find_elements(client->doc, n->children, src+1, rc,
1797 NULL, NULL, NULL, FALSE, 0, NULL);
1799 if (!n)
1800 goto fail;
1803 fail:
1804 if (src_orig)
1805 g_strfreev(src_orig);
1807 *path = src;
1808 return n;
1812 * Creates a "target" attribute. When other commands encounter an element with
1813 * this attribute, the element path is modified to the target value. If the
1814 * source element path doesn't exist when using 'ATTR SET target', it is
1815 * created, but the destination element path must exist.
1817 * req[0] - source element path
1818 * req[1] - destination element path
1820 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1822 gchar **src, **dst, *line = NULL;
1823 gpg_error_t rc;
1824 xmlNodePtr n;
1826 if (!req || !req[0] || !req[1])
1827 return EPWMD_COMMAND_SYNTAX;
1829 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1831 * The first argument may be only an account.
1833 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1834 return EPWMD_COMMAND_SYNTAX;
1837 if (valid_element_path(src, FALSE) == FALSE) {
1838 g_strfreev(src);
1839 return EPWMD_INVALID_ELEMENT;
1842 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1844 * The first argument may be only an account.
1846 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1847 rc = EPWMD_COMMAND_SYNTAX;
1848 goto fail;
1852 n = find_account(client->doc, &dst, &rc, NULL, 0);
1855 * Make sure the destination element path exists.
1857 if (!n)
1858 goto fail;
1860 if (dst[1]) {
1861 n = find_elements(client->doc, n->children, dst+1, &rc,
1862 NULL, NULL, NULL, FALSE, 0, NULL);
1864 if (!n)
1865 goto fail;
1868 n = create_element_path(client, &src, &rc);
1870 if (rc)
1871 goto fail;
1873 line = g_strjoinv("\t", dst);
1875 if (!line) {
1876 rc = gpg_error_from_errno(ENOMEM);
1877 goto fail;
1880 rc = add_attribute(n, "target", line);
1882 fail:
1883 g_free(line);
1884 g_strfreev(src);
1885 g_strfreev(dst);
1886 return rc;
1890 * req[0] - account name
1891 * req[1] - new name
1893 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1895 gpg_error_t rc;
1896 gchar **tmp;
1897 xmlNodePtr n;
1899 tmp = g_strdupv(req);
1901 if (!tmp)
1902 return gpg_error_from_errno(ENOMEM);
1904 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1905 g_strfreev(tmp);
1907 if (!n)
1908 return rc;
1910 if (g_utf8_collate(req[0], req[1]) == 0)
1911 return 0;
1914 * Will not overwrite an existing account.
1916 tmp = g_strdupv(req+1);
1918 if (!tmp)
1919 return gpg_error_from_errno(ENOMEM);
1921 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1922 g_strfreev(tmp);
1924 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
1925 return rc;
1927 if (n)
1928 return EPWMD_ACCOUNT_EXISTS;
1931 * Whitespace not allowed in account names.
1933 if (contains_whitespace(req[1]) == TRUE)
1934 return EPWMD_ATTR_SYNTAX;
1936 tmp = g_strdupv(req);
1938 if (!tmp)
1939 return gpg_error_from_errno(ENOMEM);
1941 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1942 g_strfreev(tmp);
1944 if (!n)
1945 return EPWMD_ELEMENT_NOT_FOUND;
1947 return add_attribute(n, "name", req[1]);
1951 * req[0] - attribute
1952 * req[1] - element path
1954 static int attribute_get(assuan_context_t ctx, gchar **req)
1956 struct client_s *client = assuan_get_pointer(ctx);
1957 xmlNodePtr n;
1958 xmlChar *a;
1959 gchar **path= NULL;
1960 gpg_error_t rc;
1962 if (!req || !req[0] || !req[1])
1963 return EPWMD_COMMAND_SYNTAX;
1965 if (strchr(req[1], '\t')) {
1966 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1967 return EPWMD_COMMAND_SYNTAX;
1969 else {
1970 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1971 return EPWMD_COMMAND_SYNTAX;
1974 n = find_account(client->doc, &path, &rc, NULL, 0);
1976 if (!n)
1977 goto fail;
1979 if (path[1]) {
1980 n = find_elements(client->doc, n->children, path+1, &rc,
1981 NULL, NULL, NULL, FALSE, 0, NULL);
1983 if (!n)
1984 goto fail;
1987 g_strfreev(path);
1989 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1990 return EPWMD_ATTR_NOT_FOUND;
1992 rc = assuan_send_data(ctx, a, xmlStrlen(a));
1993 xmlFree(a);
1994 return rc;
1996 fail:
1997 g_strfreev(path);
1998 return rc;
2002 * req[0] - attribute
2003 * req[1] - element path
2004 * req[2] - value
2006 static int attribute_set(struct client_s *client, gchar **req)
2008 gchar **path = NULL;
2009 gpg_error_t rc;
2010 xmlNodePtr n;
2012 if (!req || !req[0] || !req[1] || !req[2])
2013 return EPWMD_COMMAND_SYNTAX;
2016 * Reserved attribute names.
2018 if (g_utf8_collate(req[0], "name") == 0) {
2020 * Only reserved for the account element. Not the rest of the
2021 * document.
2023 if (strchr(req[1], '\t') == NULL)
2024 return name_attribute(client, req + 1);
2026 else if (g_utf8_collate(req[0], "target") == 0)
2027 return target_attribute(client, req + 1);
2029 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2031 * The first argument may be only an account.
2033 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2034 return EPWMD_COMMAND_SYNTAX;
2037 n = find_account(client->doc, &path, &rc, NULL, 0);
2039 if (!n)
2040 goto fail;
2042 if (path[1]) {
2043 n = find_elements(client->doc, n->children, path+1, &rc,
2044 NULL, NULL, NULL, FALSE, 0, NULL);
2046 if (!n)
2047 goto fail;
2050 g_strfreev(path);
2051 return add_attribute(n, req[0], req[2]);
2053 fail:
2054 g_strfreev(path);
2055 return rc;
2059 * req[0] - command
2060 * req[1] - attribute name or element path if command is LIST
2061 * req[2] - element path
2062 * req[2] - element path or value
2064 static int attr_command(assuan_context_t ctx, char *line)
2066 struct client_s *client = assuan_get_pointer(ctx);
2067 gchar **req;
2068 gpg_error_t rc = 0;
2070 rc = file_modified(client);
2072 if (rc) {
2073 log_write("%s: %s", client->filename ? client->filename : "",
2074 pwmd_strerror(rc));
2075 return send_error(ctx, rc);
2078 req = split_input_line(line, " ", 4);
2080 if (!req || !req[0] || !req[1]) {
2081 g_strfreev(req);
2082 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2085 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2086 rc = attribute_set(client, req+1);
2087 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2088 rc = attribute_get(ctx, req+1);
2089 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2090 rc = attribute_delete(client, req+1);
2091 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2092 rc = attribute_list(ctx, req+1);
2093 else
2094 rc = EPWMD_COMMAND_SYNTAX;
2096 g_strfreev(req);
2097 return send_error(ctx, rc);
2100 static int iscached_command(assuan_context_t ctx, char *line)
2102 gchar **req = split_input_line(line, " ", 0);
2103 guchar md5file[16];
2105 if (!req || !*req) {
2106 g_strfreev(req);
2107 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2110 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2111 g_strfreev(req);
2112 CACHE_LOCK(ctx);
2114 if (cache_iscached(md5file) == FALSE) {
2115 CACHE_UNLOCK;
2116 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2119 CACHE_UNLOCK;
2120 return send_error(ctx, 0);
2123 static int clearcache_command(assuan_context_t ctx, char *line)
2125 struct client_s *client = assuan_get_pointer(ctx);
2126 gchar **req = split_input_line(line, " ", 0);
2127 guchar md5file[16];
2129 CACHE_LOCK(ctx);
2131 if (!req || !*req) {
2132 g_strfreev(req);
2133 cache_clear(client->md5file, 2);
2134 CACHE_UNLOCK;
2135 return send_error(ctx, 0);
2138 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2139 g_strfreev(req);
2141 if (cache_clear(md5file, 1) == FALSE) {
2142 CACHE_UNLOCK;
2143 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2146 CACHE_UNLOCK;
2147 return send_error(ctx, 0);
2150 static int cachetimeout_command(assuan_context_t ctx, char *line)
2152 guchar md5file[16];
2153 glong timeout;
2154 gchar **req = split_input_line(line, " ", 0);
2155 gchar *p;
2156 struct client_s *client = assuan_get_pointer(ctx);
2158 if (!req || !*req || !req[1]) {
2159 g_strfreev(req);
2160 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2163 errno = 0;
2164 timeout = strtol(req[0], &p, 10);
2166 if (errno != 0 || *p != 0) {
2167 g_strfreev(req);
2168 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2171 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2172 g_strfreev(req);
2173 CACHE_LOCK(client->ctx);
2175 if (cache_set_timeout(md5file, timeout) == FALSE) {
2176 CACHE_UNLOCK;
2177 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2180 CACHE_UNLOCK;
2181 return send_error(ctx, 0);
2184 static int dump_command(assuan_context_t ctx, char *line)
2186 xmlChar *xml;
2187 gssize len;
2188 struct client_s *client = assuan_get_pointer(ctx);
2189 gpg_error_t rc;
2191 if (disable_list_and_dump == TRUE)
2192 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2194 rc = file_modified(client);
2196 if (rc) {
2197 log_write("%s: %s", client->filename ? client->filename : "",
2198 pwmd_strerror(rc));
2199 return send_error(ctx, rc);
2202 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2204 if (!xml)
2205 return send_syserror(ctx, ENOMEM);
2207 rc = assuan_send_data(ctx, xml, len);
2208 xmlFree(xml);
2209 return send_error(ctx, rc);
2212 static int getconfig_command(assuan_context_t ctx, gchar *line)
2214 struct client_s *client = assuan_get_pointer(ctx);
2215 gpg_error_t rc = 0;
2216 gchar filename[255]={0}, param[747]={0};
2217 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2219 if (strchr(line, ' ')) {
2220 sscanf(line, " %254[a-zA-Z] %746c", filename, param);
2221 fp = filename;
2222 paramp = param;
2225 paramp = g_ascii_strdown(paramp, -1);
2227 if (!paramp)
2228 return send_syserror(ctx, ENOMEM);
2230 if (strcmp(paramp, "key") == 0 || strcmp(paramp, "key_file") == 0) {
2231 g_free(paramp);
2232 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2235 p = get_key_file_string(fp ? fp : "global", paramp);
2236 g_free(paramp);
2238 if (!p)
2239 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2241 tmp = expand_homedir(p);
2243 if (!tmp) {
2244 g_free(p);
2245 return send_syserror(ctx, ENOMEM);
2248 g_free(p);
2249 p = tmp;
2250 rc = assuan_send_data(ctx, p, strlen(p));
2251 g_free(p);
2252 return send_error(ctx, rc);
2255 static int xpath_command(assuan_context_t ctx, gchar *line)
2257 struct client_s *client = assuan_get_pointer(ctx);
2258 gpg_error_t rc;
2259 xmlXPathContextPtr xp;
2260 xmlXPathObjectPtr result;
2261 xmlBufferPtr buf = NULL;
2262 gchar **req = NULL;
2264 rc = file_modified(client);
2266 if (rc) {
2267 log_write("%s: %s", client->filename ? client->filename : "",
2268 pwmd_strerror(rc));
2269 return send_error(ctx, rc);
2272 if (!line || !*line)
2273 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2275 if ((req = split_input_line(line, "\t", 2)) == NULL) {
2276 if (strv_printf(&req, "%s", line) == FALSE)
2277 return send_syserror(ctx, ENOMEM);
2280 xp = xmlXPathNewContext(client->doc);
2282 if (!xp)
2283 return send_error(ctx, EPWMD_LIBXML_ERROR);
2285 result = xmlXPathEvalExpression((xmlChar *)req[0], xp);
2287 if (!result) {
2288 xmlXPathFreeContext(xp);
2289 return send_error(ctx, EPWMD_LIBXML_ERROR);
2292 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2293 rc = EPWMD_EMPTY_ELEMENT;
2294 goto fail;
2297 rc = recurse_xpath_nodeset(client->doc, result->nodesetval,
2298 (xmlChar *)req[1], &buf);
2300 if (rc)
2301 goto fail;
2302 else if (!req[1] && !xmlBufferLength(buf)) {
2303 rc = EPWMD_EMPTY_ELEMENT;
2304 goto fail;
2306 else if (req[1])
2307 goto fail;
2309 rc = assuan_send_data(ctx, xmlBufferContent(buf), xmlBufferLength(buf));
2311 fail:
2312 g_strfreev(req);
2314 if (buf)
2315 xmlBufferFree(buf);
2317 if (result)
2318 xmlXPathFreeObject(result);
2320 if (xp)
2321 xmlXPathFreeContext(xp);
2323 return send_error(ctx, rc);
2326 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2327 gsize len)
2329 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2330 gpg_error_t rc = file_modified(client);
2331 gchar **req, **path = NULL, **path_orig = NULL, *content;
2332 xmlDocPtr doc;
2333 xmlNodePtr n, root, copy;
2335 if (assuan_rc || rc) {
2336 if (line)
2337 #ifndef MEM_DEBUG
2338 xfree(line);
2339 #else
2340 free(line);
2341 #endif
2342 return assuan_rc ? assuan_rc : rc;
2345 req = split_input_line((gchar *)line, " ", 2);
2346 #ifndef MEM_DEBUG
2347 xfree(line);
2348 #else
2349 free(line);
2350 #endif
2352 if (!req || !*req)
2353 return EPWMD_COMMAND_SYNTAX;
2355 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2356 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2357 return EPWMD_COMMAND_SYNTAX;
2360 content = req[1];
2362 if (!content || !*content) {
2363 rc = EPWMD_COMMAND_SYNTAX;
2364 goto fail;
2367 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2368 rc = EPWMD_INVALID_ELEMENT;
2369 goto fail;
2372 if (valid_element_path(path+1, FALSE) == FALSE) {
2373 rc = EPWMD_INVALID_ELEMENT;
2374 goto fail;
2377 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2379 if (!doc) {
2380 rc = EPWMD_LIBXML_ERROR;
2381 goto fail;
2384 root = xmlDocGetRootElement(doc);
2385 path_orig = g_strdupv(path);
2387 if (!path_orig) {
2388 xmlFreeDoc(doc);
2389 rc = gpg_error_from_errno(ENOMEM);
2390 goto fail;
2393 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2394 g_strfreev(path_orig);
2395 xmlFreeDoc(doc);
2396 rc = gpg_error_from_errno(ENOMEM);
2397 goto fail;
2400 n = find_account(client->doc, &path, &rc, NULL, 0);
2402 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2403 g_strfreev(path_orig);
2404 xmlFreeDoc(doc);
2405 goto fail;
2407 else if (!rc) {
2408 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2410 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2411 g_strfreev(path_orig);
2412 xmlFreeDoc(doc);
2413 goto fail;
2415 else if (!rc) {
2416 xmlNodePtr parent = n->parent;
2418 xmlUnlinkNode(n);
2419 xmlFreeNode(n);
2420 n = parent;
2424 g_strfreev(path);
2425 path = path_orig;
2427 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2428 n = create_element_path(client, &path, &rc);
2430 if (rc) {
2431 xmlFreeDoc(doc);
2432 goto fail;
2436 copy = xmlCopyNode(root, 1);
2437 n = xmlAddChild(n, copy);
2438 xmlFreeDoc(doc);
2440 if (!n)
2441 rc = EPWMD_LIBXML_ERROR;
2443 fail:
2444 g_strfreev(path);
2445 g_strfreev(req);
2446 client->inquire_status = INQUIRE_DONE;
2447 return rc;
2450 static int import_command(assuan_context_t ctx, gchar *line)
2452 gpg_error_t rc;
2453 struct client_s *client = assuan_get_pointer(ctx);
2455 rc = file_modified(client);
2457 if (rc) {
2458 log_write("%s: %s", client->filename ? client->filename : "",
2459 pwmd_strerror(rc));
2460 return send_error(ctx, rc);
2463 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2465 if (rc)
2466 return send_error(ctx, rc);
2468 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2469 client->inquire_status = INQUIRE_BUSY;
2470 return 0;
2473 static int lock_command(assuan_context_t ctx, gchar *line)
2475 gpg_error_t rc;
2476 struct client_s *client = assuan_get_pointer(ctx);
2478 rc = file_modified(client);
2480 if (rc) {
2481 log_write("%s: %s", client->filename ? client->filename : "",
2482 pwmd_strerror(rc));
2483 return send_error(ctx, rc);
2486 rc = lock_file_mutex(client);
2488 if (!rc)
2489 client->is_lock_cmd = TRUE;
2491 return send_error(ctx, rc);
2494 static int unlock_command(assuan_context_t ctx, gchar *line)
2496 gpg_error_t rc;
2497 struct client_s *client = assuan_get_pointer(ctx);
2499 rc = file_modified(client);
2501 if (rc) {
2502 log_write("%s: %s", client->filename ? client->filename : "",
2503 pwmd_strerror(rc));
2504 return send_error(ctx, rc);
2507 unlock_file_mutex(client);
2508 return send_error(ctx, 0);
2511 static int getpid_command(assuan_context_t ctx, gchar *line)
2513 gpg_error_t rc;
2514 gchar buf[32];
2515 pid_t pid = getpid();
2517 print_fmt(buf, sizeof(buf), "%i", pid);
2518 rc = assuan_send_data(ctx, buf, strlen(buf));
2519 return send_error(ctx, rc);
2522 void cleanup_assuan(assuan_context_t ctx)
2524 struct client_s *cl = assuan_get_pointer(ctx);
2526 if (cl)
2527 cleanup_client(cl);
2530 static void reset_notify(assuan_context_t ctx)
2532 struct client_s *cl = assuan_get_pointer(ctx);
2534 if (cl)
2535 cleanup_client(cl);
2538 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2540 gchar name[32] = {0}, value[256] = {0};
2542 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2543 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2545 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2546 pth_attr_t attr = pth_attr_of(pth_self());
2548 log_write("OPTION CLIENT %s", line);
2549 pth_attr_set(attr, PTH_ATTR_NAME, value);
2550 pth_attr_destroy(attr);
2552 else
2553 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2555 return 0;
2558 static int option_handler(assuan_context_t ctx, const gchar *name,
2559 const gchar *value)
2561 #ifdef WITH_PINENTRY
2562 struct client_s *client = assuan_get_pointer(ctx);
2563 #endif
2565 if (!value || !*value)
2566 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2568 if (g_strcasecmp(name, (gchar *)"client") == 0)
2569 return parse_client_option(ctx, value);
2571 if (g_strcasecmp(name, (gchar *)"iterations") == 0) {
2572 long n;
2573 gchar *p = NULL;
2575 errno = 0;
2576 n = strtol(value, &p, 10);
2578 if (errno || (p && *p) || n < -1)
2579 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2581 g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", n);
2582 send_status_all(STATUS_CONFIG);
2584 #ifdef WITH_PINENTRY
2585 else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2586 g_free(client->pinentry->ttyname);
2587 client->pinentry->ttyname = g_strdup(value);
2589 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2590 g_free(client->pinentry->ttytype);
2591 client->pinentry->ttytype = g_strdup(value);
2593 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2594 g_free(client->pinentry->display);
2595 client->pinentry->display = g_strdup(value);
2597 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2598 g_free(client->pinentry->path);
2599 client->pinentry->path = g_strdup(value);
2601 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2602 g_free(client->pinentry->title);
2603 client->pinentry->title = g_strdup(value);
2605 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2606 g_free(client->pinentry->prompt);
2607 client->pinentry->prompt = g_strdup(value);
2609 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2610 g_free(client->pinentry->desc);
2611 client->pinentry->desc = g_strdup(value);
2614 * Look at client_thread() to see how this works.
2616 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2617 gchar *p = NULL;
2618 gint n = strtol(value, &p, 10);
2620 if (*p || n < 0)
2621 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2623 client->pinentry->timeout = n;
2625 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2626 gchar *p = NULL;
2627 gint n = strtol(value, &p, 10);
2629 if (*p || n < 0 || n > 1)
2630 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2632 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2634 #endif
2635 else
2636 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2638 log_write("OPTION %s=%s", name, value);
2639 return 0;
2642 gpg_error_t register_commands(assuan_context_t ctx)
2644 static struct {
2645 const gchar *name;
2646 gint (*handler)(assuan_context_t, gchar *line);
2647 } table[] = {
2648 { "OPEN", open_command },
2649 { "SAVE", save_command },
2650 { "LIST", list_command },
2651 { "REALPATH", realpath_command },
2652 { "STORE", store_command },
2653 { "DELETE", delete_command },
2654 { "GET", get_command },
2655 { "ATTR", attr_command },
2656 { "ISCACHED", iscached_command },
2657 { "CLEARCACHE", clearcache_command },
2658 { "CACHETIMEOUT", cachetimeout_command },
2659 { "GETCONFIG", getconfig_command },
2660 { "DUMP", dump_command },
2661 { "XPATH", xpath_command },
2662 { "IMPORT", import_command },
2663 { "LOCK", lock_command },
2664 { "UNLOCK", unlock_command },
2665 { "GETPID", getpid_command },
2666 { "INPUT", NULL },
2667 { "OUTPUT", NULL },
2668 { NULL, NULL }
2670 gint i, rc;
2672 for (i=0; table[i].name; i++) {
2673 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2675 if (rc)
2676 return rc;
2679 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2681 if (rc)
2682 return rc;
2684 rc = assuan_register_option_handler(ctx, option_handler);
2686 if (rc)
2687 return rc;
2689 rc = assuan_register_reset_notify(ctx, reset_notify);
2691 if (rc)
2692 return rc;
2694 return assuan_register_post_cmd_notify(ctx, command_finalize);
2697 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2698 guchar *key, gint *dst_iter)
2700 guchar *iv;
2701 void *inbuf;
2702 gsize insize, len;
2703 guchar tkey[gcrykeysize];
2704 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2705 gcry_cipher_hd_t gh;
2706 guint iter = 0, n_iter = 0;
2707 gint iter_progress;
2708 void *outbuf = NULL;
2709 gint zrc = 0;
2710 glong outsize = 0;
2711 gpg_error_t rc;
2712 file_header_t file_header;
2713 gchar str[ASSUAN_LINELENGTH];
2715 if (!ctx) {
2716 rc = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2718 if (rc)
2719 return rc;
2721 else
2722 gh = client->gh;
2724 lseek(fd, 0, SEEK_SET);
2725 insize = st.st_size - sizeof(file_header_t);
2726 iv = gcry_malloc(gcryblocksize);
2728 if (!iv) {
2729 if (!ctx)
2730 gcry_cipher_close(gh);
2732 return gpg_error_from_errno(ENOMEM);
2735 len = pth_read(fd, &file_header, sizeof(file_header_t));
2737 if (len != sizeof(file_header_t)) {
2738 len = errno;
2740 if (!ctx)
2741 gcry_cipher_close(gh);
2743 gcry_free(iv);
2744 errno = len;
2745 return gpg_error_from_errno(errno);
2748 *dst_iter = file_header.iter;
2750 /* No encryption iterations. This is a plain (gzipped) file. */
2751 if (file_header.iter == -1) {
2753 * cache_file_count() needs both .used == TRUE and a valid key in
2754 * order for it to count as a used cache entry. Fixes CACHE status
2755 * messages.
2757 memset(key, '!', gcrykeysize);
2758 insize = st.st_size - sizeof(file_header_t);
2761 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2762 inbuf = gcry_malloc(insize);
2764 if (!inbuf) {
2765 if (!ctx)
2766 gcry_cipher_close(gh);
2768 gcry_free(iv);
2769 return gpg_error_from_errno(ENOMEM);
2772 len = pth_read(fd, inbuf, insize);
2774 if (len != insize) {
2775 len = errno;
2777 if (!ctx)
2778 gcry_cipher_close(gh);
2780 gcry_free(iv);
2781 errno = len;
2782 return gpg_error_from_errno(errno);
2785 if (file_header.iter == -1)
2786 goto decompress;
2788 if ((rc = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2789 if (!ctx) {
2790 gcry_cipher_close(gh);
2791 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2793 else
2794 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2796 gcry_free(inbuf);
2797 gcry_free(iv);
2798 return rc;
2801 if ((rc = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2802 if (!ctx) {
2803 gcry_cipher_close(gh);
2804 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2806 else
2807 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2809 gcry_free(inbuf);
2810 gcry_free(iv);
2812 if (!ctx)
2813 gcry_cipher_close(gh);
2815 return rc;
2818 iter_progress = get_key_file_integer(client->filename, "iteration_progress");
2820 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2821 rc = assuan_write_status(client->ctx, "DECRYPT",
2822 print_fmt(str, sizeof(str), "%i %i", 0, file_header.iter));
2824 if (rc) {
2825 gcry_free(inbuf);
2826 gcry_free(iv);
2827 return rc;
2831 rc = decrypt_xml(gh, inbuf, insize, NULL, 0);
2833 if (rc) {
2834 gcry_free(inbuf);
2835 gcry_free(iv);
2836 return rc;
2839 memcpy(tkey, key, sizeof(tkey));
2840 tkey[0] ^= 1;
2842 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2843 if (!ctx) {
2844 gcry_cipher_close(gh);
2845 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2847 else
2848 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2850 memset(tkey, 0, sizeof(tkey));
2851 gcry_free(inbuf);
2852 gcry_free(iv);
2853 return rc;
2856 memset(tkey, 0, sizeof(tkey));
2858 while (iter < file_header.iter) {
2859 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2860 if (!(iter % iter_progress)) {
2861 rc = assuan_write_status(ctx, "DECRYPT",
2862 print_fmt(str, sizeof(str), "%i %i", ++n_iter * iter_progress, file_header.iter));
2864 if (rc) {
2865 gcry_free(inbuf);
2866 gcry_free(iv);
2867 return rc;
2872 if ((rc = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2873 if (!ctx) {
2874 gcry_cipher_close(gh);
2875 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2877 else
2878 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2880 gcry_free(inbuf);
2881 gcry_free(iv);
2882 return rc;
2885 rc = decrypt_xml(gh, inbuf, insize, NULL, 0);
2887 if (rc) {
2888 if (!ctx) {
2889 gcry_cipher_close(gh);
2890 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2892 else
2893 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2895 gcry_free(inbuf);
2896 gcry_free(iv);
2897 return rc;
2900 iter++;
2901 pth_yield(NULL);
2904 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2905 rc = assuan_write_status(ctx, "DECRYPT",
2906 print_fmt(str, sizeof(str), "%i %i", file_header.iter, file_header.iter));
2908 if (rc) {
2909 gcry_free(inbuf);
2910 gcry_free(iv);
2911 return rc;
2915 gcry_free(iv);
2917 decompress:
2918 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zrc) == FALSE) {
2920 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2922 if (zrc == Z_MEM_ERROR) {
2923 gcry_free(inbuf);
2924 return gpg_error_from_errno(ENOMEM);
2926 else if (zrc != Z_DATA_ERROR) {
2927 gcry_free(inbuf);
2929 if (!ctx)
2930 gcry_cipher_close(gh);
2932 return EPWMD_BADKEY;
2935 else {
2936 gcry_free(inbuf);
2937 inbuf = outbuf;
2938 insize = outsize;
2941 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2942 gcry_free(inbuf);
2944 if (!ctx)
2945 gcry_cipher_close(gh);
2947 return EPWMD_BADKEY;
2950 if (ctx) {
2951 client->xml = inbuf;
2952 client->len = insize;
2954 else {
2955 gcry_cipher_close(gh);
2956 gcry_free(inbuf);
2959 return 0;
2963 * This is called after every Assuan command.
2965 void command_finalize(assuan_context_t ctx, gint rc)
2967 struct client_s *client = assuan_get_pointer(ctx);
2969 if (!client->is_lock_cmd)
2970 unlock_file_mutex(client);