Send the total number of iterations with the ENCRYPT and DECRYPT
[pwmd.git] / src / commands.c
blob07008e3225c9265968a22e7297d0d548da9cbd87
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_usleep(100000);
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)
449 g_key_file_set_integer(keyfileh, client->filename, "iterations", iter);
451 return send_error(ctx, rc);
454 static int open_command(assuan_context_t ctx, char *line)
456 struct stat st;
457 guchar shakey[gcrykeysize];
458 gboolean cached = FALSE;
459 gpg_error_t rc;
460 struct client_s *client = assuan_get_pointer(ctx);
461 gchar **req;
462 gchar *filename = NULL;
463 file_header_t file_header;
465 memset(shakey, 0, sizeof(shakey));
467 if ((req = split_input_line(line, " ", 2)) != NULL)
468 filename = req[0];
470 if (!filename || !*filename) {
471 g_strfreev(req);
472 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
475 if (valid_filename(filename) == FALSE) {
476 g_strfreev(req);
477 return send_error(ctx, EPWMD_INVALID_FILENAME);
480 if (client->state == STATE_OPEN)
481 cleanup_client(client);
483 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
484 CACHE_LOCK(client->ctx);
486 if (cache_has_file(client->md5file) == FALSE) {
487 if (cache_add_file(client->md5file, NULL) == FALSE) {
488 g_strfreev(req);
489 CACHE_UNLOCK;
490 return send_error(ctx, EPWMD_MAX_SLOTS);
494 cache_incr_refcount(client->md5file);
495 CACHE_UNLOCK;
496 rc = lock_file_mutex(client);
498 if (rc) {
499 g_strfreev(req);
500 return send_error(ctx, rc);
503 client->freed = FALSE;
505 if ((rc = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
506 g_strfreev(req);
507 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
508 cleanup_client(client);
509 return send_error(ctx, rc);
512 if (stat(filename, &st) == 0) {
513 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
514 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
515 g_strfreev(req);
516 cleanup_client(client);
517 return send_error(ctx, EPWMD_INVALID_FILENAME);
520 client->mtime = st.st_mtime;
523 client->filename = g_strdup(filename);
525 if (!client->filename) {
526 memset(shakey, 0, sizeof(shakey));
527 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
528 cleanup_client(client);
529 g_strfreev(req);
530 return send_syserror(ctx, ENOMEM);
533 #ifdef WITH_PINENTRY
534 client->pinentry->filename = g_strdup(client->filename);
536 if (!client->pinentry->filename) {
537 memset(shakey, 0, sizeof(shakey));
538 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
539 cleanup_client(client);
540 g_strfreev(req);
541 return send_syserror(ctx, ENOMEM);
543 #endif
546 * New files don't need a key.
548 if (access(filename, R_OK) != 0) {
549 if (errno != ENOENT) {
550 rc = errno;
551 log_write("%s: %s", filename, strerror(errno));
552 g_strfreev(req);
553 cleanup_client(client);
554 return send_syserror(ctx, rc);
557 if ((client->xml = new_document()) == NULL) {
558 log_write("%s", strerror(ENOMEM));
559 g_strfreev(req);
560 cleanup_client(client);
561 return send_syserror(ctx, ENOMEM);
564 client->len = xmlStrlen(client->xml);
565 client->new = TRUE;
566 client->filename = g_strdup(filename);
568 if (!client->filename) {
569 g_strfreev(req);
570 cleanup_client(client);
571 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
572 return send_syserror(ctx, ENOMEM);
575 memset(shakey, 0, sizeof(shakey));
577 if (req[1] && *req[1])
578 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
580 g_strfreev(req);
581 return open_command_finalize(ctx, shakey, cached);
584 rc = read_file_header(filename, &file_header);
586 if (rc) {
587 g_strfreev(req);
588 cleanup_client(client);
589 return send_error(ctx, rc);
592 if (file_header.iter == -1)
593 goto done;
595 CACHE_LOCK(client->ctx);
596 cached = cache_get_key(client->md5file, shakey);
597 CACHE_UNLOCK;
599 if (cached == FALSE) {
601 * No key specified and no matching filename found in the cache. Use
602 * pinentry to retrieve the key. Cannot return assuan_process_done()
603 * here otherwise the command will be interrupted. The event loop in
604 * client_thread() will poll the file descriptor waiting for it to
605 * become ready to read a pinentry_key_s which will contain the
606 * entered key or rc. It will then call open_command_finalize() to
607 * to finish the command.
609 if (!req[1] || !*req[1]) {
610 #ifdef WITH_PINENTRY
611 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
613 /* From set_pinentry_defaults(). */
614 if (client->pinentry->enable == FALSE ||
615 (client->pinentry->enable == -1 && b == FALSE)) {
616 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
617 goto done;
620 g_strfreev(req);
621 lock_pin_mutex(client);
622 client->pinentry->which = PINENTRY_OPEN;
623 rc = pinentry_fork(ctx);
625 if (rc) {
626 unlock_pin_mutex(client->pinentry);
627 cleanup_client(client);
628 return send_error(ctx, rc);
631 client->pinentry->cb = open_command_finalize;
632 client->pinentry->status = PINENTRY_INIT;
633 return 0;
634 #else
635 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
636 goto done;
637 #endif
640 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
643 done:
644 g_strfreev(req);
645 return open_command_finalize(ctx, shakey, cached);
648 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
649 gint size, gpointer *out, glong *outsize, gint *rc)
651 z_stream z;
652 gpointer pout, pin;
653 gz_header h;
654 gchar buf[17];
655 gint cmd = Z_NO_FLUSH;
656 gchar str[ASSUAN_LINELENGTH];
658 z.zalloc = z_alloc;
659 z.zfree = z_free;
660 z.next_in = pin = data;
661 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
662 z.avail_out = zlib_bufsize;
663 z.next_out = pout = g_malloc(zlib_bufsize);
665 if (!pout) {
666 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
667 *rc = Z_MEM_ERROR;
668 return FALSE;
671 *rc = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
673 if (*rc != Z_OK) {
674 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
675 g_free(pout);
676 return FALSE;
679 memset(&h, 0, sizeof(gz_header));
680 g_snprintf(buf, sizeof(buf), "%i", size);
681 h.comment = (guchar *)buf;
682 *rc = deflateSetHeader(&z, &h);
684 if (*rc != Z_OK) {
685 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
686 g_free(pout);
687 deflateEnd(&z);
688 return FALSE;
691 do {
692 gpointer p;
694 *rc = deflate(&z, cmd);
696 switch (*rc) {
697 case Z_OK:
698 break;
699 case Z_BUF_ERROR:
700 if (!z.avail_out) {
701 p = g_realloc(pout, z.total_out + zlib_bufsize);
703 if (!p) {
704 *rc = Z_MEM_ERROR;
705 goto fail;
708 pout = p;
709 z.next_out = pout + z.total_out;
710 z.avail_out = zlib_bufsize;
713 if (!z.avail_in && z.total_in < size) {
714 if (z.total_in + zlib_bufsize > size)
715 z.avail_in = size - z.total_in;
716 else
717 z.avail_in = zlib_bufsize;
719 if (ctx) {
720 *rc = assuan_write_status(ctx, "COMPRESS",
721 print_fmt(str, sizeof(str), "%i %i", z.total_in, size));
723 if (*rc)
724 goto fail;
728 if (z.total_in >= size)
729 cmd = Z_FINISH;
731 break;
732 case Z_STREAM_END:
733 break;
734 default:
735 goto fail;
738 pth_yield(NULL);
739 } while (*rc != Z_STREAM_END);
741 if (ctx) {
742 *rc = assuan_write_status(ctx, "COMPRESS",
743 print_fmt(str, sizeof(str), "%i %i", z.total_in, size));
745 if (*rc)
746 goto fail;
749 *out = pout;
750 *outsize = z.total_out;
751 deflateEnd(&z);
752 *rc = 0;
753 return TRUE;
755 fail:
756 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
757 g_free(pout);
758 deflateEnd(&z);
759 return FALSE;
762 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
763 const gchar *filename, gpointer data, size_t insize, guchar *shakey,
764 gint iter)
766 gsize len = insize;
767 gint fd;
768 gpointer inbuf;
769 guchar tkey[gcrykeysize];
770 gchar *p;
771 gpg_error_t rc;
772 guint iter_progress = 0, n_iter = 0, xiter = 0;
773 gchar tmp[FILENAME_MAX];
774 struct stat st;
775 mode_t mode = 0;
776 file_header_t file_header;
777 gchar str[ASSUAN_LINELENGTH];
779 if (iter == -1) {
781 * cache_file_count() needs both .used == TRUE and a valid key in
782 * order for it to count as a used cache entry. Fixes CACHE status
783 * messages.
785 memset(shakey, '!', gcrykeysize);
786 inbuf = data;
787 file_header.iter = iter;
788 goto write_file;
791 if (insize / gcryblocksize) {
792 len = (insize / gcryblocksize) * gcryblocksize;
794 if (insize % gcryblocksize)
795 len += gcryblocksize;
799 * Resize the existing xml buffer to the block size required by gcrypt
800 * rather than duplicating it and wasting memory.
802 inbuf = gcry_realloc(data, len);
804 if (!inbuf)
805 return gpg_error_from_errno(ENOMEM);
807 insize = len;
808 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
809 memcpy(tkey, shakey, sizeof(tkey));
810 tkey[0] ^= 1;
812 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
813 memset(tkey, 0, sizeof(tkey));
814 gcry_free(inbuf);
815 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
816 return rc;
819 memset(tkey, 0, sizeof(tkey));
820 file_header.iter = iter;
822 if (client)
823 iter_progress = get_key_file_integer(client->filename, "iteration_progress");
825 if (client && iter_progress && file_header.iter >= iter_progress) {
826 rc = assuan_write_status(client->ctx, "ENCRYPT",
827 print_fmt(str, sizeof(str), "%i %i", 0, file_header.iter));
829 if (rc) {
830 gcry_free(inbuf);
831 return rc;
835 while (xiter < file_header.iter) {
836 if (client && iter_progress > 0 && xiter >= iter_progress) {
837 if (!(xiter % iter_progress)) {
838 rc = assuan_write_status(client->ctx, "ENCRYPT",
839 print_fmt(str, sizeof(str), "%i %i", ++n_iter * iter_progress, file_header.iter));
841 if (rc) {
842 gcry_free(inbuf);
843 return rc;
848 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
849 sizeof(file_header.iv)))) {
850 gcry_free(inbuf);
851 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
852 return rc;
855 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
856 == FALSE) {
857 gcry_free(inbuf);
858 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
859 return rc;
862 xiter++;
863 pth_yield(NULL);
866 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
867 sizeof(file_header.iv)))) {
868 gcry_free(inbuf);
869 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
870 return rc;
873 if ((rc = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
874 gcry_free(inbuf);
875 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
876 return rc;
879 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
880 gcry_free(inbuf);
881 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
882 return rc;
885 if (client && iter_progress && file_header.iter >= iter_progress) {
886 rc = assuan_write_status(client->ctx, "ENCRYPT",
887 print_fmt(str, sizeof(str), "%i %i", file_header.iter, file_header.iter));
889 if (rc) {
890 gcry_free(inbuf);
891 return rc;
895 write_file:
896 if (filename) {
897 if (stat(filename, &st) == 0) {
898 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
901 * FIXME What if the file has an ACL?
903 if (!(mode & S_IWUSR)) {
904 gcry_free(inbuf);
905 return gpg_error_from_errno(EACCES);
908 else {
909 if (errno != ENOENT) {
910 rc = errno;
911 gcry_free(inbuf);
912 return gpg_error_from_errno(rc);
916 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
918 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
919 rc = errno;
920 gcry_free(inbuf);
921 p = strrchr(tmp, '/');
922 p++;
923 log_write("%s: %s", p, strerror(rc));
924 return gpg_error_from_errno(rc);
927 else
929 * xml_import() from command line.
931 fd = STDOUT_FILENO;
933 len = pth_write(fd, &file_header, sizeof(file_header_t));
935 if (len != sizeof(file_header)) {
936 len = errno;
938 if (filename) {
939 close(fd);
940 unlink(tmp);
943 gcry_free(inbuf);
944 return gpg_error_from_errno(len);
947 len = pth_write(fd, inbuf, insize);
949 if (len != insize) {
950 len = errno;
952 if (filename) {
953 close(fd);
954 unlink(tmp);
957 gcry_free(inbuf);
958 return gpg_error_from_errno(len);
961 if (fsync(fd) == -1) {
962 len = errno;
964 if (filename) {
965 close(fd);
966 unlink(tmp);
969 gcry_free(inbuf);
970 return gpg_error_from_errno(len);
973 if (filename) {
974 close(fd);
976 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
977 gchar tmp2[FILENAME_MAX];
979 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
981 if (rename(filename, tmp2) == -1) {
982 unlink(tmp);
983 len = errno;
984 gcry_free(inbuf);
985 return gpg_error_from_errno(len);
989 if (rename(tmp, filename) == -1) {
990 len = errno;
991 unlink(tmp);
992 gcry_free(inbuf);
993 return gpg_error_from_errno(len);
996 if (mode)
997 chmod(filename, mode);
1000 gcry_free(inbuf);
1001 return 0;
1004 static gpg_error_t save_command_finalize(assuan_context_t ctx,
1005 guchar shakey[], gboolean cached)
1007 struct client_s *client = assuan_get_pointer(ctx);
1008 gpointer xmlbuf;
1009 xmlChar *p;
1010 gint len;
1011 gint iter;
1012 gint timeout;
1013 gpointer outbuf;
1014 glong outsize = 0;
1015 gint zrc;
1016 gpg_error_t rc;
1017 struct stat st;
1019 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
1020 xmlbuf = p;
1022 iter = get_key_file_integer(client->filename, "compression_level");
1024 if (iter < 0)
1025 iter = 0;
1027 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zrc) == FALSE) {
1028 memset(shakey, 0, sizeof(shakey));
1029 xmlFree(xmlbuf);
1031 if (zrc == Z_MEM_ERROR) {
1032 return send_syserror(ctx, ENOMEM);
1034 else
1035 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1037 else {
1038 gcry_free(xmlbuf);
1039 xmlbuf = outbuf;
1040 len = outsize;
1043 iter = get_key_file_integer(client->filename, "iterations");
1044 rc = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
1046 if (rc) {
1047 memset(shakey, 0, sizeof(shakey));
1048 return send_error(ctx, rc);
1051 stat(client->filename, &st);
1052 client->mtime = st.st_mtime;
1053 timeout = get_key_file_integer(client->filename, "cache_timeout");
1054 CACHE_LOCK(client->ctx);
1056 if (cached) {
1057 memset(shakey, 0, sizeof(shakey));
1058 cache_reset_timeout(client->md5file, timeout);
1059 CACHE_UNLOCK;
1061 if (client->new == TRUE)
1062 send_status_all(STATUS_CACHE);
1064 client->new = FALSE;
1065 return send_error(ctx, 0);
1068 if (cache_update_key(client->md5file, shakey) == FALSE) {
1069 memset(shakey, 0, sizeof(shakey));
1070 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
1071 CACHE_UNLOCK;
1072 return send_error(ctx, EPWMD_MAX_SLOTS);
1075 client->new = FALSE;
1076 memset(shakey, 0, sizeof(shakey));
1077 cache_reset_timeout(client->md5file, timeout);
1078 CACHE_UNLOCK;
1079 send_status_all(STATUS_CACHE);
1080 return send_error(ctx, 0);
1083 static int save_command(assuan_context_t ctx, char *line)
1085 gboolean cached = FALSE;
1086 guchar shakey[gcrykeysize];
1087 struct stat st;
1088 struct client_s *client = assuan_get_pointer(ctx);
1089 gpg_error_t rc;
1091 memset(shakey, 0, sizeof(shakey));
1092 rc = file_modified(client);
1094 if (rc) {
1095 log_write("%s: %s", client->filename ? client->filename : "",
1096 pwmd_strerror(rc));
1097 return send_error(ctx, rc);
1100 rc = lock_file_mutex(client);
1102 if (rc)
1103 return send_error(ctx, rc);
1105 if (stat(client->filename, &st) == -1 && errno != ENOENT)
1106 return send_syserror(ctx, errno);
1108 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
1109 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
1110 return send_error(ctx, EPWMD_INVALID_FILENAME);
1113 if (get_key_file_integer(client->filename, "iterations") == -1)
1114 goto done;
1116 if (!line || !*line) {
1117 guchar tmp[sizeof(shakey)];
1118 CACHE_LOCK(ctx);
1120 memset(tmp, '!', sizeof(tmp));
1122 if (cache_get_key(client->md5file, shakey) == FALSE ||
1123 memcmp(shakey, tmp, sizeof(shakey)) == 0) {
1124 CACHE_UNLOCK;
1125 #ifdef WITH_PINENTRY
1126 if (get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1127 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1128 goto done;
1131 lock_pin_mutex(client);
1132 client->pinentry->which = PINENTRY_SAVE;
1133 rc = pinentry_fork(ctx);
1135 if (rc) {
1136 unlock_pin_mutex(client->pinentry);
1137 return send_error(ctx, rc);
1140 client->pinentry->cb = save_command_finalize;
1141 client->pinentry->status = PINENTRY_INIT;
1142 return 0;
1143 #else
1144 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1145 goto done;
1146 #endif
1148 else {
1149 CACHE_UNLOCK;
1150 cached = TRUE;
1153 else {
1154 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
1155 memset(line, 0, strlen(line));
1158 done:
1159 return save_command_finalize(ctx, shakey, cached);
1162 static int delete_command(assuan_context_t ctx, char *line)
1164 struct client_s *client = assuan_get_pointer(ctx);
1165 gchar **req;
1166 gpg_error_t rc;
1167 xmlNodePtr n;
1169 rc = file_modified(client);
1171 if (rc) {
1172 log_write("%s: %s", client->filename ? client->filename : "",
1173 pwmd_strerror(rc));
1174 return send_error(ctx, rc);
1177 if (strchr(line, '\t'))
1178 req = split_input_line(line, "\t", -1);
1179 else
1180 req = split_input_line(line, " ", -1);
1182 if (!req || !*req)
1183 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1185 n = find_account(client->doc, &req, &rc, NULL, 0);
1187 if (!n) {
1188 g_strfreev(req);
1189 return send_error(ctx, rc);
1193 * No sub-node defined. Remove the entire node (account).
1195 if (!req[1]) {
1196 if (n) {
1197 xmlUnlinkNode(n);
1198 xmlFreeNode(n);
1201 g_strfreev(req);
1202 return send_error(ctx, 0);
1205 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1206 g_strfreev(req);
1208 if (!n)
1209 return send_error(ctx, rc);
1211 if (n) {
1212 xmlUnlinkNode(n);
1213 xmlFreeNode(n);
1216 return send_error(ctx, 0);
1220 * Don't return with assuan_process_done() here. This has been called from
1221 * assuan_process_next() and the command should be finished in
1222 * client_thread().
1224 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1225 gsize len)
1227 assuan_context_t ctx = data;
1228 struct client_s *client = assuan_get_pointer(ctx);
1229 gchar **req;
1230 xmlNodePtr n;
1231 gpg_error_t rc = file_modified(client);
1233 if (assuan_rc || rc) {
1234 if (line)
1235 #ifndef MEM_DEBUG
1236 xfree(line);
1237 #else
1238 free(line);
1239 #endif
1240 return assuan_rc ? assuan_rc : rc;
1243 req = split_input_line((gchar *)line, "\t", 0);
1244 #ifndef MEM_DEBUG
1245 xfree(line);
1246 #else
1247 free(line);
1248 #endif
1250 if (!req || !*req)
1251 return EPWMD_COMMAND_SYNTAX;
1253 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1254 g_strfreev(req);
1255 return EPWMD_INVALID_ELEMENT;
1258 if (valid_element_path(req+1, TRUE) == FALSE) {
1259 g_strfreev(req);
1260 return EPWMD_INVALID_ELEMENT;
1263 again:
1264 n = find_account(client->doc, &req, &rc, NULL, 0);
1266 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1267 rc = new_account(client->doc, *req);
1269 if (rc) {
1270 g_strfreev(req);
1271 return rc;
1274 goto again;
1277 if (!n) {
1278 g_strfreev(req);
1279 return rc;
1282 if (req[1]) {
1283 if (!n->children)
1284 create_elements_cb(n, req+1, &rc, NULL);
1285 else
1286 find_elements(client->doc, n->children, req+1, &rc,
1287 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1290 g_strfreev(req);
1291 client->inquire_status = INQUIRE_DONE;
1292 return rc;
1295 static int store_command(assuan_context_t ctx, char *line)
1297 struct client_s *client = assuan_get_pointer(ctx);
1298 gpg_error_t rc = file_modified(client);
1300 if (rc) {
1301 log_write("%s: %s", client->filename ? client->filename : "",
1302 pwmd_strerror(rc));
1303 return send_error(ctx, rc);
1306 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1308 if (rc)
1309 return send_error(ctx, rc);
1311 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1312 client->inquire_status = INQUIRE_BUSY;
1313 return 0;
1316 static int get_command(assuan_context_t ctx, char *line)
1318 struct client_s *client = assuan_get_pointer(ctx);
1319 gchar **req;
1320 gpg_error_t rc;
1321 xmlNodePtr n;
1323 rc = file_modified(client);
1325 if (rc) {
1326 log_write("%s: %s", client->filename ? client->filename : "",
1327 pwmd_strerror(rc));
1328 return send_error(ctx, rc);
1331 req = split_input_line(line, "\t", -1);
1333 if (!req || !*req) {
1334 g_strfreev(req);
1335 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1338 n = find_account(client->doc, &req, &rc, NULL, 0);
1340 if (!n) {
1341 g_strfreev(req);
1342 return send_error(ctx, rc);
1345 if (req[1])
1346 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1348 g_strfreev(req);
1350 if (rc)
1351 return send_error(ctx, rc);
1353 if (!n || !n->children)
1354 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1356 n = find_text_node(n->children);
1358 if (!n || !n->content || !*n->content)
1359 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1361 rc = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1362 return send_error(ctx, rc);
1365 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1366 gpg_error_t *rc, gchar **req_orig, void *data)
1368 gchar *path = *(gchar **)data;
1369 gchar *tmp = NULL, *result;
1371 if (path) {
1372 g_free(path);
1373 *(gchar **)data = NULL;
1376 path = g_strjoinv("\t", target);
1378 if (!path) {
1379 *rc = gpg_error_from_errno(ENOMEM);
1380 return NULL;
1383 if (req_orig) {
1384 tmp = g_strjoinv("\t", req_orig);
1386 if (!tmp) {
1387 g_free(path);
1388 *rc = gpg_error_from_errno(ENOMEM);
1389 return NULL;
1393 if (tmp && *tmp)
1394 result = g_strdup_printf("%s\t%s", path, tmp);
1395 else
1396 result = g_strdup(path);
1398 if (!result) {
1399 *rc = gpg_error_from_errno(ENOMEM);
1400 g_free(path);
1401 g_free(tmp);
1402 return NULL;
1405 g_free(path);
1406 g_free(tmp);
1407 *(gchar **)data = result;
1408 return node;
1411 static int realpath_command(assuan_context_t ctx, char *line)
1413 gpg_error_t rc;
1414 struct client_s *client = assuan_get_pointer(ctx);
1415 gchar **req;
1416 gchar *t;
1417 gint i;
1418 xmlNodePtr n;
1419 GString *string;
1420 gchar *rp = NULL;
1422 rc = file_modified(client);
1424 if (rc) {
1425 log_write("%s: %s", client->filename ? client->filename : "",
1426 pwmd_strerror(rc));
1427 return send_error(ctx, rc);
1430 if (strchr(line, '\t') != NULL) {
1431 if ((req = split_input_line(line, "\t", 0)) == NULL)
1432 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1434 else {
1435 if ((req = split_input_line(line, " ", 0)) == NULL)
1436 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1439 n = find_account(client->doc, &req, &rc, NULL, 0);
1441 if (!n) {
1442 g_strfreev(req);
1443 return send_error(ctx, rc);
1446 rp = g_strjoinv("\t", req);
1448 if (!rp) {
1449 g_strfreev(req);
1450 return send_syserror(ctx, ENOMEM);
1453 if (req[1]) {
1454 n = find_elements(client->doc, n->children, req+1, &rc,
1455 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1457 if (!n) {
1458 g_free(rp);
1459 g_strfreev(req);
1460 return send_error(ctx, rc);
1464 string = g_string_new(rp);
1465 g_free(rp);
1466 g_strfreev(req);
1468 if (!string)
1469 return send_syserror(ctx, ENOMEM);
1471 again:
1472 for (i = 0, t = string->str + i; *t; t++, i++) {
1473 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1474 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1475 goto again;
1479 rc = assuan_send_data(ctx, string->str, string->len);
1480 g_string_free(string, TRUE);
1481 return send_error(ctx, rc);
1484 static int list_command(assuan_context_t ctx, char *line)
1486 struct client_s *client = assuan_get_pointer(ctx);
1487 gpg_error_t rc;
1488 struct element_list_s *elements = NULL;
1489 gchar *tmp;
1491 if (disable_list_and_dump == TRUE)
1492 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1494 rc = file_modified(client);
1496 if (rc) {
1497 log_write("%s: %s", client->filename, pwmd_strerror(rc));
1498 return send_error(ctx, rc);
1501 if (!*line) {
1502 GString *str;
1504 rc = list_accounts(client->doc, &str);
1506 if (rc)
1507 return send_error(ctx, rc);
1509 rc = assuan_send_data(ctx, str->str, str->len);
1510 g_string_free(str, TRUE);
1511 return send_error(ctx, rc);
1514 elements = g_malloc0(sizeof(struct element_list_s));
1516 if (!elements) {
1517 rc = gpg_err_code_from_errno(ENOMEM);
1518 goto fail;
1521 rc = create_path_list(client->doc, elements, line);
1523 if (rc)
1524 goto fail;
1526 if (elements) {
1527 gint total = g_slist_length(elements->list);
1528 gint i;
1529 GString *str;
1531 if (!total) {
1532 rc = EPWMD_EMPTY_ELEMENT;
1533 goto fail;
1536 str = g_string_new(NULL);
1538 if (!str) {
1539 rc = gpg_err_code_from_errno(ENOMEM);
1540 goto fail;
1543 for (i = 0; i < total; i++) {
1544 tmp = g_slist_nth_data(elements->list, i);
1545 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1548 rc = assuan_send_data(ctx, str->str, str->len);
1549 g_string_free(str, TRUE);
1551 else
1552 rc = EPWMD_EMPTY_ELEMENT;
1554 fail:
1555 if (elements) {
1556 gint total = g_slist_length(elements->list);
1557 gint i;
1559 for (i = 0; i < total; i++) {
1560 tmp = g_slist_nth_data(elements->list, i);
1561 g_free(tmp);
1564 g_slist_free(elements->list);
1566 if (elements->prefix)
1567 g_free(elements->prefix);
1569 g_free(elements);
1572 return send_error(ctx, rc);
1575 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1576 const gchar *value)
1578 xmlAttrPtr a;
1580 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1581 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1583 if (!a)
1584 return EPWMD_LIBXML_ERROR;
1586 else
1587 xmlNodeSetContent(a->children, (xmlChar *)value);
1589 return 0;
1593 * req[0] - element path
1595 static int attribute_list(assuan_context_t ctx, gchar **req)
1597 struct client_s *client = assuan_get_pointer(ctx);
1598 gchar **attrlist = NULL;
1599 gint i = 0;
1600 gchar **path = NULL;
1601 xmlAttrPtr a;
1602 xmlNodePtr n, an;
1603 gchar *line;
1604 gpg_error_t rc;
1606 if (!req || !req[0])
1607 return EPWMD_COMMAND_SYNTAX;
1609 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1611 * The first argument may be only an account.
1613 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1614 return EPWMD_COMMAND_SYNTAX;
1617 n = find_account(client->doc, &path, &rc, NULL, 0);
1619 if (!n) {
1620 g_strfreev(path);
1621 return rc;
1624 if (path[1]) {
1625 n = find_elements(client->doc, n->children, path+1, &rc,
1626 NULL, NULL, NULL, FALSE, 0, NULL);
1628 if (!n) {
1629 g_strfreev(path);
1630 return rc;
1634 g_strfreev(path);
1636 for (a = n->properties; a; a = a->next) {
1637 gchar **pa;
1639 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1640 if (attrlist)
1641 g_strfreev(attrlist);
1643 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1644 return gpg_error_from_errno(ENOMEM);
1647 attrlist = pa;
1648 an = a->children;
1649 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1651 if (!attrlist[i]) {
1652 g_strfreev(attrlist);
1653 return gpg_error_from_errno(ENOMEM);
1656 attrlist[++i] = NULL;
1659 if (!attrlist)
1660 return EPWMD_EMPTY_ELEMENT;
1662 line = g_strjoinv("\n", attrlist);
1664 if (!line) {
1665 g_strfreev(attrlist);
1666 return gpg_error_from_errno(ENOMEM);
1669 rc = assuan_send_data(ctx, line, strlen(line));
1670 g_free(line);
1671 g_strfreev(attrlist);
1672 return rc;
1676 * req[0] - attribute
1677 * req[1] - element path
1679 static int attribute_delete(struct client_s *client, gchar **req)
1681 xmlAttrPtr a;
1682 xmlNodePtr n;
1683 gchar **path = NULL;
1684 gpg_error_t rc;
1686 if (!req || !req[0] || !req[1])
1687 return EPWMD_COMMAND_SYNTAX;
1689 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1691 * The first argument may be only an account.
1693 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1694 return EPWMD_COMMAND_SYNTAX;
1698 * Don't remove the "name" attribute for the account element. To remove an
1699 * account use DELETE <account>.
1701 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1702 rc = EPWMD_ATTR_SYNTAX;
1703 goto fail;
1706 n = find_account(client->doc, &path, &rc, NULL, 0);
1708 if (!n)
1709 goto fail;
1711 if (path[1]) {
1712 n = find_elements(client->doc, n->children, path+1, &rc,
1713 NULL, NULL, NULL, FALSE, 0, NULL);
1715 if (!n)
1716 goto fail;
1719 g_strfreev(path);
1721 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1722 return EPWMD_ATTR_NOT_FOUND;
1724 if (xmlRemoveProp(a) == -1)
1725 return EPWMD_LIBXML_ERROR;
1727 return 0;
1729 fail:
1730 g_strfreev(path);
1731 return rc;
1734 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
1735 gpg_error_t *rc)
1737 gchar **src = *path;
1738 gchar **src_orig = g_strdupv(src);
1739 xmlNodePtr n = NULL;
1741 *rc = 0;
1743 if (!src_orig) {
1744 *rc = gpg_error_from_errno(ENOMEM);
1745 goto fail;
1748 again:
1749 n = find_account(client->doc, &src, rc, NULL, 0);
1751 if (!n) {
1752 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
1753 *rc = new_account(client->doc, src[0]);
1755 if (*rc)
1756 goto fail;
1758 goto again;
1760 else
1761 goto fail;
1764 if (src[1]) {
1765 if (!n->children)
1766 n = create_target_elements_cb(n, src+1, rc, NULL);
1767 else
1768 n = find_elements(client->doc, n->children, src+1, rc,
1769 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
1771 if (!n)
1772 goto fail;
1775 * Reset the position of the element tree now that the elements
1776 * have been created.
1778 g_strfreev(src);
1779 src = src_orig;
1780 src_orig = NULL;
1781 n = find_account(client->doc, &src, rc, NULL, 0);
1783 if (!n)
1784 goto fail;
1786 n = find_elements(client->doc, n->children, src+1, rc,
1787 NULL, NULL, NULL, FALSE, 0, NULL);
1789 if (!n)
1790 goto fail;
1793 fail:
1794 if (src_orig)
1795 g_strfreev(src_orig);
1797 *path = src;
1798 return n;
1802 * Creates a "target" attribute. When other commands encounter an element with
1803 * this attribute, the element path is modified to the target value. If the
1804 * source element path doesn't exist when using 'ATTR SET target', it is
1805 * created, but the destination element path must exist.
1807 * req[0] - source element path
1808 * req[1] - destination element path
1810 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1812 gchar **src, **dst, *line = NULL;
1813 gpg_error_t rc;
1814 xmlNodePtr n;
1816 if (!req || !req[0] || !req[1])
1817 return EPWMD_COMMAND_SYNTAX;
1819 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1821 * The first argument may be only an account.
1823 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1824 return EPWMD_COMMAND_SYNTAX;
1827 if (valid_element_path(src, FALSE) == FALSE) {
1828 g_strfreev(src);
1829 return EPWMD_INVALID_ELEMENT;
1832 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1834 * The first argument may be only an account.
1836 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1837 rc = EPWMD_COMMAND_SYNTAX;
1838 goto fail;
1842 n = find_account(client->doc, &dst, &rc, NULL, 0);
1845 * Make sure the destination element path exists.
1847 if (!n)
1848 goto fail;
1850 if (dst[1]) {
1851 n = find_elements(client->doc, n->children, dst+1, &rc,
1852 NULL, NULL, NULL, FALSE, 0, NULL);
1854 if (!n)
1855 goto fail;
1858 n = create_element_path(client, &src, &rc);
1860 if (rc)
1861 goto fail;
1863 line = g_strjoinv("\t", dst);
1865 if (!line) {
1866 rc = gpg_error_from_errno(ENOMEM);
1867 goto fail;
1870 rc = add_attribute(n, "target", line);
1872 fail:
1873 g_free(line);
1874 g_strfreev(src);
1875 g_strfreev(dst);
1876 return rc;
1880 * req[0] - account name
1881 * req[1] - new name
1883 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1885 gpg_error_t rc;
1886 gchar **tmp;
1887 xmlNodePtr n;
1889 tmp = g_strdupv(req);
1891 if (!tmp)
1892 return gpg_error_from_errno(ENOMEM);
1894 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1895 g_strfreev(tmp);
1897 if (!n)
1898 return rc;
1900 if (g_utf8_collate(req[0], req[1]) == 0)
1901 return 0;
1904 * Will not overwrite an existing account.
1906 tmp = g_strdupv(req+1);
1908 if (!tmp)
1909 return gpg_error_from_errno(ENOMEM);
1911 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1912 g_strfreev(tmp);
1914 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
1915 return rc;
1917 if (n)
1918 return EPWMD_ACCOUNT_EXISTS;
1921 * Whitespace not allowed in account names.
1923 if (contains_whitespace(req[1]) == TRUE)
1924 return EPWMD_ATTR_SYNTAX;
1926 tmp = g_strdupv(req);
1928 if (!tmp)
1929 return gpg_error_from_errno(ENOMEM);
1931 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1932 g_strfreev(tmp);
1934 if (!n)
1935 return EPWMD_ELEMENT_NOT_FOUND;
1937 return add_attribute(n, "name", req[1]);
1941 * req[0] - attribute
1942 * req[1] - element path
1944 static int attribute_get(assuan_context_t ctx, gchar **req)
1946 struct client_s *client = assuan_get_pointer(ctx);
1947 xmlNodePtr n;
1948 xmlChar *a;
1949 gchar **path= NULL;
1950 gpg_error_t rc;
1952 if (!req || !req[0] || !req[1])
1953 return EPWMD_COMMAND_SYNTAX;
1955 if (strchr(req[1], '\t')) {
1956 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1957 return EPWMD_COMMAND_SYNTAX;
1959 else {
1960 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1961 return EPWMD_COMMAND_SYNTAX;
1964 n = find_account(client->doc, &path, &rc, NULL, 0);
1966 if (!n)
1967 goto fail;
1969 if (path[1]) {
1970 n = find_elements(client->doc, n->children, path+1, &rc,
1971 NULL, NULL, NULL, FALSE, 0, NULL);
1973 if (!n)
1974 goto fail;
1977 g_strfreev(path);
1979 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1980 return EPWMD_ATTR_NOT_FOUND;
1982 rc = assuan_send_data(ctx, a, xmlStrlen(a));
1983 xmlFree(a);
1984 return rc;
1986 fail:
1987 g_strfreev(path);
1988 return rc;
1992 * req[0] - attribute
1993 * req[1] - element path
1994 * req[2] - value
1996 static int attribute_set(struct client_s *client, gchar **req)
1998 gchar **path = NULL;
1999 gpg_error_t rc;
2000 xmlNodePtr n;
2002 if (!req || !req[0] || !req[1] || !req[2])
2003 return EPWMD_COMMAND_SYNTAX;
2006 * Reserved attribute names.
2008 if (g_utf8_collate(req[0], "name") == 0) {
2010 * Only reserved for the account element. Not the rest of the
2011 * document.
2013 if (strchr(req[1], '\t') == NULL)
2014 return name_attribute(client, req + 1);
2016 else if (g_utf8_collate(req[0], "target") == 0)
2017 return target_attribute(client, req + 1);
2019 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2021 * The first argument may be only an account.
2023 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2024 return EPWMD_COMMAND_SYNTAX;
2027 n = find_account(client->doc, &path, &rc, NULL, 0);
2029 if (!n)
2030 goto fail;
2032 if (path[1]) {
2033 n = find_elements(client->doc, n->children, path+1, &rc,
2034 NULL, NULL, NULL, FALSE, 0, NULL);
2036 if (!n)
2037 goto fail;
2040 g_strfreev(path);
2041 return add_attribute(n, req[0], req[2]);
2043 fail:
2044 g_strfreev(path);
2045 return rc;
2049 * req[0] - command
2050 * req[1] - attribute name or element path if command is LIST
2051 * req[2] - element path
2052 * req[2] - element path or value
2054 static int attr_command(assuan_context_t ctx, char *line)
2056 struct client_s *client = assuan_get_pointer(ctx);
2057 gchar **req;
2058 gpg_error_t rc = 0;
2060 rc = file_modified(client);
2062 if (rc) {
2063 log_write("%s: %s", client->filename ? client->filename : "",
2064 pwmd_strerror(rc));
2065 return send_error(ctx, rc);
2068 req = split_input_line(line, " ", 4);
2070 if (!req || !req[0] || !req[1]) {
2071 g_strfreev(req);
2072 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2075 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2076 rc = attribute_set(client, req+1);
2077 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2078 rc = attribute_get(ctx, req+1);
2079 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2080 rc = attribute_delete(client, req+1);
2081 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2082 rc = attribute_list(ctx, req+1);
2083 else
2084 rc = EPWMD_COMMAND_SYNTAX;
2086 g_strfreev(req);
2087 return send_error(ctx, rc);
2090 static int iscached_command(assuan_context_t ctx, char *line)
2092 gchar **req = split_input_line(line, " ", 0);
2093 guchar md5file[16];
2095 if (!req || !*req) {
2096 g_strfreev(req);
2097 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2100 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2101 g_strfreev(req);
2102 CACHE_LOCK(ctx);
2104 if (cache_iscached(md5file) == FALSE) {
2105 CACHE_UNLOCK;
2106 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2109 CACHE_UNLOCK;
2110 return send_error(ctx, 0);
2113 static int clearcache_command(assuan_context_t ctx, char *line)
2115 struct client_s *client = assuan_get_pointer(ctx);
2116 gchar **req = split_input_line(line, " ", 0);
2117 guchar md5file[16];
2119 CACHE_LOCK(ctx);
2121 if (!req || !*req) {
2122 g_strfreev(req);
2123 cache_clear(client->md5file, 2);
2124 CACHE_UNLOCK;
2125 return send_error(ctx, 0);
2128 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2129 g_strfreev(req);
2131 if (cache_clear(md5file, 1) == FALSE) {
2132 CACHE_UNLOCK;
2133 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2136 CACHE_UNLOCK;
2137 return send_error(ctx, 0);
2140 static int cachetimeout_command(assuan_context_t ctx, char *line)
2142 guchar md5file[16];
2143 glong timeout;
2144 gchar **req = split_input_line(line, " ", 0);
2145 gchar *p;
2146 struct client_s *client = assuan_get_pointer(ctx);
2148 if (!req || !*req || !req[1]) {
2149 g_strfreev(req);
2150 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2153 errno = 0;
2154 timeout = strtol(req[0], &p, 10);
2156 if (errno != 0 || *p != 0) {
2157 g_strfreev(req);
2158 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2161 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2162 g_strfreev(req);
2163 CACHE_LOCK(client->ctx);
2165 if (cache_set_timeout(md5file, timeout) == FALSE) {
2166 CACHE_UNLOCK;
2167 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2170 CACHE_UNLOCK;
2171 return send_error(ctx, 0);
2174 static int dump_command(assuan_context_t ctx, char *line)
2176 xmlChar *xml;
2177 gssize len;
2178 struct client_s *client = assuan_get_pointer(ctx);
2179 gpg_error_t rc;
2181 if (disable_list_and_dump == TRUE)
2182 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2184 rc = file_modified(client);
2186 if (rc) {
2187 log_write("%s: %s", client->filename ? client->filename : "",
2188 pwmd_strerror(rc));
2189 return send_error(ctx, rc);
2192 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2194 if (!xml)
2195 return send_syserror(ctx, ENOMEM);
2197 rc = assuan_send_data(ctx, xml, len);
2198 xmlFree(xml);
2199 return send_error(ctx, rc);
2202 static int getconfig_command(assuan_context_t ctx, gchar *line)
2204 struct client_s *client = assuan_get_pointer(ctx);
2205 gpg_error_t rc = 0;
2206 gchar filename[255]={0}, param[747]={0};
2207 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2209 if (strchr(line, ' ')) {
2210 sscanf(line, " %254[a-zA-Z] %746c", filename, param);
2211 fp = filename;
2212 paramp = param;
2215 if (strcmp(line, "key") == 0 || strcmp(line, "key_file") == 0)
2216 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2218 p = get_key_file_string(fp ? fp : "global", paramp);
2220 if (!p)
2221 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2223 tmp = expand_homedir(p);
2225 if (!tmp) {
2226 g_free(p);
2227 return send_syserror(ctx, ENOMEM);
2230 g_free(p);
2231 p = tmp;
2232 rc = assuan_send_data(ctx, p, strlen(p));
2233 g_free(p);
2234 return send_error(ctx, rc);
2237 static int xpath_command(assuan_context_t ctx, gchar *line)
2239 struct client_s *client = assuan_get_pointer(ctx);
2240 gpg_error_t rc;
2241 xmlXPathContextPtr xp;
2242 xmlXPathObjectPtr result;
2243 xmlBufferPtr buf = NULL;
2244 gchar **req = NULL;
2246 rc = file_modified(client);
2248 if (rc) {
2249 log_write("%s: %s", client->filename ? client->filename : "",
2250 pwmd_strerror(rc));
2251 return send_error(ctx, rc);
2254 if (!line || !*line)
2255 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2257 if ((req = split_input_line(line, "\t", 2)) == NULL) {
2258 if (strv_printf(&req, "%s", line) == FALSE)
2259 return send_syserror(ctx, ENOMEM);
2262 xp = xmlXPathNewContext(client->doc);
2264 if (!xp)
2265 return send_error(ctx, EPWMD_LIBXML_ERROR);
2267 result = xmlXPathEvalExpression((xmlChar *)req[0], xp);
2269 if (!result) {
2270 xmlXPathFreeContext(xp);
2271 return send_error(ctx, EPWMD_LIBXML_ERROR);
2274 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2275 rc = EPWMD_EMPTY_ELEMENT;
2276 goto fail;
2279 rc = recurse_xpath_nodeset(client->doc, result->nodesetval,
2280 (xmlChar *)req[1], &buf);
2282 if (rc)
2283 goto fail;
2284 else if (!req[1] && !xmlBufferLength(buf)) {
2285 rc = EPWMD_EMPTY_ELEMENT;
2286 goto fail;
2288 else if (req[1])
2289 goto fail;
2291 rc = assuan_send_data(ctx, xmlBufferContent(buf), xmlBufferLength(buf));
2293 fail:
2294 g_strfreev(req);
2296 if (buf)
2297 xmlBufferFree(buf);
2299 if (result)
2300 xmlXPathFreeObject(result);
2302 if (xp)
2303 xmlXPathFreeContext(xp);
2305 return send_error(ctx, rc);
2308 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2309 gsize len)
2311 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2312 gpg_error_t rc = file_modified(client);
2313 gchar **req, **path = NULL, **path_orig = NULL, *content;
2314 xmlDocPtr doc;
2315 xmlNodePtr n, root, copy;
2317 if (assuan_rc || rc) {
2318 if (line)
2319 #ifndef MEM_DEBUG
2320 xfree(line);
2321 #else
2322 free(line);
2323 #endif
2324 return assuan_rc ? assuan_rc : rc;
2327 req = split_input_line((gchar *)line, " ", 2);
2328 #ifndef MEM_DEBUG
2329 xfree(line);
2330 #else
2331 free(line);
2332 #endif
2334 if (!req || !*req)
2335 return EPWMD_COMMAND_SYNTAX;
2337 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2338 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2339 return EPWMD_COMMAND_SYNTAX;
2342 content = req[1];
2344 if (!content || !*content) {
2345 rc = EPWMD_COMMAND_SYNTAX;
2346 goto fail;
2349 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2350 rc = EPWMD_INVALID_ELEMENT;
2351 goto fail;
2354 if (valid_element_path(path+1, FALSE) == FALSE) {
2355 rc = EPWMD_INVALID_ELEMENT;
2356 goto fail;
2359 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2361 if (!doc) {
2362 rc = EPWMD_LIBXML_ERROR;
2363 goto fail;
2366 root = xmlDocGetRootElement(doc);
2367 path_orig = g_strdupv(path);
2369 if (!path_orig) {
2370 xmlFreeDoc(doc);
2371 rc = gpg_error_from_errno(ENOMEM);
2372 goto fail;
2375 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2376 g_strfreev(path_orig);
2377 xmlFreeDoc(doc);
2378 rc = gpg_error_from_errno(ENOMEM);
2379 goto fail;
2382 n = find_account(client->doc, &path, &rc, NULL, 0);
2384 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2385 g_strfreev(path_orig);
2386 xmlFreeDoc(doc);
2387 goto fail;
2389 else if (!rc) {
2390 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2392 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2393 g_strfreev(path_orig);
2394 xmlFreeDoc(doc);
2395 goto fail;
2397 else if (!rc) {
2398 xmlNodePtr parent = n->parent;
2400 xmlUnlinkNode(n);
2401 xmlFreeNode(n);
2402 n = parent;
2406 g_strfreev(path);
2407 path = path_orig;
2409 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2410 n = create_element_path(client, &path, &rc);
2412 if (rc) {
2413 xmlFreeDoc(doc);
2414 goto fail;
2418 copy = xmlCopyNode(root, 1);
2419 n = xmlAddChild(n, copy);
2420 xmlFreeDoc(doc);
2422 if (!n)
2423 rc = EPWMD_LIBXML_ERROR;
2425 fail:
2426 g_strfreev(path);
2427 g_strfreev(req);
2428 client->inquire_status = INQUIRE_DONE;
2429 return rc;
2432 static int import_command(assuan_context_t ctx, gchar *line)
2434 gpg_error_t rc;
2435 struct client_s *client = assuan_get_pointer(ctx);
2437 rc = file_modified(client);
2439 if (rc) {
2440 log_write("%s: %s", client->filename ? client->filename : "",
2441 pwmd_strerror(rc));
2442 return send_error(ctx, rc);
2445 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2447 if (rc)
2448 return send_error(ctx, rc);
2450 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2451 client->inquire_status = INQUIRE_BUSY;
2452 return 0;
2455 static int lock_command(assuan_context_t ctx, gchar *line)
2457 gpg_error_t rc;
2458 struct client_s *client = assuan_get_pointer(ctx);
2460 rc = file_modified(client);
2462 if (rc) {
2463 log_write("%s: %s", client->filename ? client->filename : "",
2464 pwmd_strerror(rc));
2465 return send_error(ctx, rc);
2468 rc = lock_file_mutex(client);
2470 if (!rc)
2471 client->is_lock_cmd = TRUE;
2473 return send_error(ctx, rc);
2476 static int unlock_command(assuan_context_t ctx, gchar *line)
2478 gpg_error_t rc;
2479 struct client_s *client = assuan_get_pointer(ctx);
2481 rc = file_modified(client);
2483 if (rc) {
2484 log_write("%s: %s", client->filename ? client->filename : "",
2485 pwmd_strerror(rc));
2486 return send_error(ctx, rc);
2489 unlock_file_mutex(client);
2490 return send_error(ctx, 0);
2493 static int getpid_command(assuan_context_t ctx, gchar *line)
2495 gpg_error_t rc;
2496 gchar buf[32];
2497 pid_t pid = getpid();
2499 print_fmt(buf, sizeof(buf), "%i", pid);
2500 rc = assuan_send_data(ctx, buf, strlen(buf));
2501 return send_error(ctx, rc);
2504 void cleanup_assuan(assuan_context_t ctx)
2506 struct client_s *cl = assuan_get_pointer(ctx);
2508 if (cl)
2509 cleanup_client(cl);
2512 static void reset_notify(assuan_context_t ctx)
2514 struct client_s *cl = assuan_get_pointer(ctx);
2516 if (cl)
2517 cleanup_client(cl);
2520 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2522 gchar name[32] = {0}, value[256] = {0};
2524 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2525 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2527 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2528 pth_attr_t attr = pth_attr_of(pth_self());
2530 log_write("OPTION CLIENT %s", line);
2531 pth_attr_set(attr, PTH_ATTR_NAME, value);
2532 pth_attr_destroy(attr);
2534 else
2535 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2537 return 0;
2540 static int option_handler(assuan_context_t ctx, const gchar *name,
2541 const gchar *value)
2543 #ifdef WITH_PINENTRY
2544 struct client_s *client = assuan_get_pointer(ctx);
2545 #endif
2547 if (!value || !*value)
2548 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2550 if (g_strcasecmp(name, (gchar *)"client") == 0)
2551 return parse_client_option(ctx, value);
2553 if (g_strcasecmp(name, (gchar *)"iterations") == 0) {
2554 long n;
2555 gchar *p = NULL;
2557 errno = 0;
2558 n = strtol(value, &p, 10);
2560 if (errno || (p && *p))
2561 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2563 g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", n);
2564 send_status_all(STATUS_CONFIG);
2566 #ifdef WITH_PINENTRY
2567 else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2568 g_free(client->pinentry->ttyname);
2569 client->pinentry->ttyname = g_strdup(value);
2571 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2572 g_free(client->pinentry->ttytype);
2573 client->pinentry->ttytype = g_strdup(value);
2575 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2576 g_free(client->pinentry->display);
2577 client->pinentry->display = g_strdup(value);
2579 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2580 g_free(client->pinentry->path);
2581 client->pinentry->path = g_strdup(value);
2583 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2584 g_free(client->pinentry->title);
2585 client->pinentry->title = g_strdup(value);
2587 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2588 g_free(client->pinentry->prompt);
2589 client->pinentry->prompt = g_strdup(value);
2591 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2592 g_free(client->pinentry->desc);
2593 client->pinentry->desc = g_strdup(value);
2595 #if 0
2596 /* Need to wait for pinentry to support a --timeout option so it can
2597 * terminate itself. Cannot do it here because assuan_pipe_connect() calls
2598 * execv() which replaces the pid of the fork()ed thread from
2599 * pinentry_fork(). So pinentry will become a real process after the
2600 * thread terminates and won't be able to be kill()ed from pwmd.
2602 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2603 gchar *p = NULL;
2604 gint n = strtol(value, &p, 10);
2606 if (*p || n < 0)
2607 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2609 client->pinentry->timeout = n;
2611 #endif
2612 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2613 gchar *p = NULL;
2614 gint n = strtol(value, &p, 10);
2616 if (*p || n < 0 || n > 1)
2617 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2619 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2621 #else
2622 else
2623 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2624 #endif
2626 log_write("OPTION %s=%s", name, value);
2627 return 0;
2630 gpg_error_t register_commands(assuan_context_t ctx)
2632 static struct {
2633 const gchar *name;
2634 gint (*handler)(assuan_context_t, gchar *line);
2635 } table[] = {
2636 { "OPEN", open_command },
2637 { "SAVE", save_command },
2638 { "LIST", list_command },
2639 { "REALPATH", realpath_command },
2640 { "STORE", store_command },
2641 { "DELETE", delete_command },
2642 { "GET", get_command },
2643 { "ATTR", attr_command },
2644 { "ISCACHED", iscached_command },
2645 { "CLEARCACHE", clearcache_command },
2646 { "CACHETIMEOUT", cachetimeout_command },
2647 { "GETCONFIG", getconfig_command },
2648 { "DUMP", dump_command },
2649 { "XPATH", xpath_command },
2650 { "IMPORT", import_command },
2651 { "LOCK", lock_command },
2652 { "UNLOCK", unlock_command },
2653 { "GETPID", getpid_command },
2654 { "INPUT", NULL },
2655 { "OUTPUT", NULL },
2656 { NULL, NULL }
2658 gint i, rc;
2660 for (i=0; table[i].name; i++) {
2661 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2663 if (rc)
2664 return rc;
2667 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2669 if (rc)
2670 return rc;
2672 rc = assuan_register_option_handler(ctx, option_handler);
2674 if (rc)
2675 return rc;
2677 return assuan_register_reset_notify(ctx, reset_notify);
2680 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2681 guchar *key, gint *dst_iter)
2683 guchar *iv;
2684 void *inbuf;
2685 gsize insize, len;
2686 guchar tkey[gcrykeysize];
2687 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2688 gcry_cipher_hd_t gh;
2689 guint iter = 0, n_iter = 0;
2690 gint iter_progress;
2691 void *outbuf = NULL;
2692 gint zrc = 0;
2693 glong outsize = 0;
2694 gpg_error_t rc;
2695 file_header_t file_header;
2696 gchar str[ASSUAN_LINELENGTH];
2698 if (!ctx) {
2699 rc = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2701 if (rc)
2702 return rc;
2704 else
2705 gh = client->gh;
2707 lseek(fd, 0, SEEK_SET);
2708 insize = st.st_size - sizeof(file_header_t);
2709 iv = gcry_malloc(gcryblocksize);
2711 if (!iv) {
2712 if (!ctx)
2713 gcry_cipher_close(gh);
2715 return gpg_error_from_errno(ENOMEM);
2718 len = pth_read(fd, &file_header, sizeof(file_header_t));
2720 if (len != sizeof(file_header_t)) {
2721 len = errno;
2723 if (!ctx)
2724 gcry_cipher_close(gh);
2726 gcry_free(iv);
2727 errno = len;
2728 return gpg_error_from_errno(errno);
2731 *dst_iter = file_header.iter;
2733 /* No encryption iterations. This is a plain (gzipped) file. */
2734 if (file_header.iter == -1) {
2736 * cache_file_count() needs both .used == TRUE and a valid key in
2737 * order for it to count as a used cache entry. Fixes CACHE status
2738 * messages.
2740 memset(key, '!', gcrykeysize);
2741 insize = st.st_size - sizeof(file_header_t);
2744 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2745 inbuf = gcry_malloc(insize);
2747 if (!inbuf) {
2748 if (!ctx)
2749 gcry_cipher_close(gh);
2751 gcry_free(iv);
2752 return gpg_error_from_errno(ENOMEM);
2755 len = pth_read(fd, inbuf, insize);
2757 if (len != insize) {
2758 len = errno;
2760 if (!ctx)
2761 gcry_cipher_close(gh);
2763 gcry_free(iv);
2764 errno = len;
2765 return gpg_error_from_errno(errno);
2768 if (file_header.iter == -1)
2769 goto decompress;
2771 if ((rc = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2772 if (!ctx) {
2773 gcry_cipher_close(gh);
2774 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2776 else
2777 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2779 gcry_free(inbuf);
2780 gcry_free(iv);
2781 return rc;
2784 if ((rc = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2785 if (!ctx) {
2786 gcry_cipher_close(gh);
2787 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2789 else
2790 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2792 gcry_free(inbuf);
2793 gcry_free(iv);
2795 if (!ctx)
2796 gcry_cipher_close(gh);
2798 return rc;
2801 iter_progress = get_key_file_integer(client->filename, "iteration_progress");
2803 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2804 rc = assuan_write_status(client->ctx, "DECRYPT",
2805 print_fmt(str, sizeof(str), "%i %i", 0, file_header.iter));
2807 if (rc) {
2808 gcry_free(inbuf);
2809 gcry_free(iv);
2810 return rc;
2814 rc = decrypt_xml(gh, inbuf, insize, NULL, 0);
2816 if (rc) {
2817 gcry_free(inbuf);
2818 gcry_free(iv);
2819 return rc;
2822 memcpy(tkey, key, sizeof(tkey));
2823 tkey[0] ^= 1;
2825 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2826 if (!ctx) {
2827 gcry_cipher_close(gh);
2828 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2830 else
2831 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2833 memset(tkey, 0, sizeof(tkey));
2834 gcry_free(inbuf);
2835 gcry_free(iv);
2836 return rc;
2839 memset(tkey, 0, sizeof(tkey));
2841 while (iter < file_header.iter) {
2842 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2843 if (!(iter % iter_progress)) {
2844 rc = assuan_write_status(ctx, "DECRYPT",
2845 print_fmt(str, sizeof(str), "%i %i", ++n_iter * iter_progress, file_header.iter));
2847 if (rc) {
2848 gcry_free(inbuf);
2849 gcry_free(iv);
2850 return rc;
2855 if ((rc = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2856 if (!ctx) {
2857 gcry_cipher_close(gh);
2858 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2860 else
2861 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2863 gcry_free(inbuf);
2864 gcry_free(iv);
2865 return rc;
2868 rc = decrypt_xml(gh, inbuf, insize, NULL, 0);
2870 if (rc) {
2871 if (!ctx) {
2872 gcry_cipher_close(gh);
2873 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2875 else
2876 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2878 gcry_free(inbuf);
2879 gcry_free(iv);
2880 return rc;
2883 iter++;
2884 pth_yield(NULL);
2887 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2888 rc = assuan_write_status(ctx, "DECRYPT",
2889 print_fmt(str, sizeof(str), "%i %i", file_header.iter, file_header.iter));
2891 if (rc) {
2892 gcry_free(inbuf);
2893 gcry_free(iv);
2894 return rc;
2898 gcry_free(iv);
2900 decompress:
2901 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zrc) == FALSE) {
2903 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2905 if (zrc == Z_MEM_ERROR) {
2906 gcry_free(inbuf);
2907 return gpg_error_from_errno(ENOMEM);
2909 else if (zrc != Z_DATA_ERROR) {
2910 gcry_free(inbuf);
2912 if (!ctx)
2913 gcry_cipher_close(gh);
2915 return EPWMD_BADKEY;
2918 else {
2919 gcry_free(inbuf);
2920 inbuf = outbuf;
2921 insize = outsize;
2924 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2925 gcry_free(inbuf);
2927 if (!ctx)
2928 gcry_cipher_close(gh);
2930 return EPWMD_BADKEY;
2933 if (ctx) {
2934 client->xml = inbuf;
2935 client->len = insize;
2937 else {
2938 gcry_cipher_close(gh);
2939 gcry_free(inbuf);
2942 return 0;
2946 * This is called after every Assuan command.
2948 void command_finalize(assuan_context_t ctx, gint rc)
2950 struct client_s *client = assuan_get_pointer(ctx);
2952 if (!client->is_lock_cmd)
2953 unlock_file_mutex(client);