Also send the CONFIG status message when the file header iterations
[pwmd.git] / src / commands.c
blobd14ff99bc41e6528ab4a4775fc0f123c28f30b24
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 && iter != get_key_file_integer(client->filename, "iterations")) {
449 g_key_file_set_integer(keyfileh, client->filename, "iterations", iter);
450 send_status_all(STATUS_CONFIG);
453 return send_error(ctx, rc);
456 static int open_command(assuan_context_t ctx, char *line)
458 struct stat st;
459 guchar shakey[gcrykeysize];
460 gboolean cached = FALSE;
461 gpg_error_t rc;
462 struct client_s *client = assuan_get_pointer(ctx);
463 gchar **req;
464 gchar *filename = NULL;
465 file_header_t file_header;
467 memset(shakey, 0, sizeof(shakey));
469 if ((req = split_input_line(line, " ", 2)) != NULL)
470 filename = req[0];
472 if (!filename || !*filename) {
473 g_strfreev(req);
474 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
477 if (valid_filename(filename) == FALSE) {
478 g_strfreev(req);
479 return send_error(ctx, EPWMD_INVALID_FILENAME);
482 if (client->state == STATE_OPEN)
483 cleanup_client(client);
485 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
486 CACHE_LOCK(client->ctx);
488 if (cache_has_file(client->md5file) == FALSE) {
489 if (cache_add_file(client->md5file, NULL) == FALSE) {
490 g_strfreev(req);
491 CACHE_UNLOCK;
492 return send_error(ctx, EPWMD_MAX_SLOTS);
496 cache_incr_refcount(client->md5file);
497 CACHE_UNLOCK;
498 rc = lock_file_mutex(client);
500 if (rc) {
501 g_strfreev(req);
502 return send_error(ctx, rc);
505 client->freed = FALSE;
507 if ((rc = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
508 g_strfreev(req);
509 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
510 cleanup_client(client);
511 return send_error(ctx, rc);
514 if (stat(filename, &st) == 0) {
515 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
516 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
517 g_strfreev(req);
518 cleanup_client(client);
519 return send_error(ctx, EPWMD_INVALID_FILENAME);
522 client->mtime = st.st_mtime;
525 client->filename = g_strdup(filename);
527 if (!client->filename) {
528 memset(shakey, 0, sizeof(shakey));
529 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
530 cleanup_client(client);
531 g_strfreev(req);
532 return send_syserror(ctx, ENOMEM);
535 #ifdef WITH_PINENTRY
536 client->pinentry->filename = g_strdup(client->filename);
538 if (!client->pinentry->filename) {
539 memset(shakey, 0, sizeof(shakey));
540 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
541 cleanup_client(client);
542 g_strfreev(req);
543 return send_syserror(ctx, ENOMEM);
545 #endif
548 * New files don't need a key.
550 if (access(filename, R_OK) != 0) {
551 if (errno != ENOENT) {
552 rc = errno;
553 log_write("%s: %s", filename, strerror(errno));
554 g_strfreev(req);
555 cleanup_client(client);
556 return send_syserror(ctx, rc);
559 if ((client->xml = new_document()) == NULL) {
560 log_write("%s", strerror(ENOMEM));
561 g_strfreev(req);
562 cleanup_client(client);
563 return send_syserror(ctx, ENOMEM);
566 client->len = xmlStrlen(client->xml);
567 client->new = TRUE;
568 client->filename = g_strdup(filename);
570 if (!client->filename) {
571 g_strfreev(req);
572 cleanup_client(client);
573 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
574 return send_syserror(ctx, ENOMEM);
577 memset(shakey, 0, sizeof(shakey));
579 if (req[1] && *req[1])
580 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
582 g_strfreev(req);
583 return open_command_finalize(ctx, shakey, cached);
586 rc = read_file_header(filename, &file_header);
588 if (rc) {
589 g_strfreev(req);
590 cleanup_client(client);
591 return send_error(ctx, rc);
594 if (file_header.iter == -1)
595 goto done;
597 CACHE_LOCK(client->ctx);
598 cached = cache_get_key(client->md5file, shakey);
599 CACHE_UNLOCK;
601 if (cached == FALSE) {
603 * No key specified and no matching filename found in the cache. Use
604 * pinentry to retrieve the key. Cannot return assuan_process_done()
605 * here otherwise the command will be interrupted. The event loop in
606 * client_thread() will poll the file descriptor waiting for it to
607 * become ready to read a pinentry_key_s which will contain the
608 * entered key or rc. It will then call open_command_finalize() to
609 * to finish the command.
611 if (!req[1] || !*req[1]) {
612 #ifdef WITH_PINENTRY
613 gboolean b = get_key_file_boolean(filename, "enable_pinentry");
615 /* From set_pinentry_defaults(). */
616 if (client->pinentry->enable == FALSE ||
617 (client->pinentry->enable == -1 && b == FALSE)) {
618 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
619 goto done;
622 g_strfreev(req);
623 lock_pin_mutex(client);
624 client->pinentry->which = PINENTRY_OPEN;
625 rc = pinentry_fork(ctx);
627 if (rc) {
628 unlock_pin_mutex(client->pinentry);
629 cleanup_client(client);
630 return send_error(ctx, rc);
633 client->pinentry->cb = open_command_finalize;
634 client->pinentry->status = PINENTRY_INIT;
635 return 0;
636 #else
637 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
638 goto done;
639 #endif
642 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
645 done:
646 g_strfreev(req);
647 return open_command_finalize(ctx, shakey, cached);
650 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
651 gint size, gpointer *out, glong *outsize, gint *rc)
653 z_stream z;
654 gpointer pout, pin;
655 gz_header h;
656 gchar buf[17];
657 gint cmd = Z_NO_FLUSH;
658 gchar str[ASSUAN_LINELENGTH];
660 z.zalloc = z_alloc;
661 z.zfree = z_free;
662 z.next_in = pin = data;
663 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
664 z.avail_out = zlib_bufsize;
665 z.next_out = pout = g_malloc(zlib_bufsize);
667 if (!pout) {
668 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
669 *rc = Z_MEM_ERROR;
670 return FALSE;
673 *rc = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
675 if (*rc != Z_OK) {
676 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
677 g_free(pout);
678 return FALSE;
681 memset(&h, 0, sizeof(gz_header));
682 g_snprintf(buf, sizeof(buf), "%i", size);
683 h.comment = (guchar *)buf;
684 *rc = deflateSetHeader(&z, &h);
686 if (*rc != Z_OK) {
687 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
688 g_free(pout);
689 deflateEnd(&z);
690 return FALSE;
693 do {
694 gpointer p;
696 *rc = deflate(&z, cmd);
698 switch (*rc) {
699 case Z_OK:
700 break;
701 case Z_BUF_ERROR:
702 if (!z.avail_out) {
703 p = g_realloc(pout, z.total_out + zlib_bufsize);
705 if (!p) {
706 *rc = Z_MEM_ERROR;
707 goto fail;
710 pout = p;
711 z.next_out = pout + z.total_out;
712 z.avail_out = zlib_bufsize;
715 if (!z.avail_in && z.total_in < size) {
716 if (z.total_in + zlib_bufsize > size)
717 z.avail_in = size - z.total_in;
718 else
719 z.avail_in = zlib_bufsize;
721 if (ctx) {
722 *rc = assuan_write_status(ctx, "COMPRESS",
723 print_fmt(str, sizeof(str), "%i %i", z.total_in, size));
725 if (*rc)
726 goto fail;
730 if (z.total_in >= size)
731 cmd = Z_FINISH;
733 break;
734 case Z_STREAM_END:
735 break;
736 default:
737 goto fail;
740 pth_yield(NULL);
741 } while (*rc != Z_STREAM_END);
743 if (ctx) {
744 *rc = assuan_write_status(ctx, "COMPRESS",
745 print_fmt(str, sizeof(str), "%i %i", z.total_in, size));
747 if (*rc)
748 goto fail;
751 *out = pout;
752 *outsize = z.total_out;
753 deflateEnd(&z);
754 *rc = 0;
755 return TRUE;
757 fail:
758 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
759 g_free(pout);
760 deflateEnd(&z);
761 return FALSE;
764 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
765 const gchar *filename, gpointer data, size_t insize, guchar *shakey,
766 gint iter)
768 gsize len = insize;
769 gint fd;
770 gpointer inbuf;
771 guchar tkey[gcrykeysize];
772 gchar *p;
773 gpg_error_t rc;
774 guint iter_progress = 0, n_iter = 0, xiter = 0;
775 gchar tmp[FILENAME_MAX];
776 struct stat st;
777 mode_t mode = 0;
778 file_header_t file_header;
779 gchar str[ASSUAN_LINELENGTH];
781 if (iter == -1) {
783 * cache_file_count() needs both .used == TRUE and a valid key in
784 * order for it to count as a used cache entry. Fixes CACHE status
785 * messages.
787 memset(shakey, '!', gcrykeysize);
788 inbuf = data;
789 file_header.iter = iter;
790 goto write_file;
793 if (insize / gcryblocksize) {
794 len = (insize / gcryblocksize) * gcryblocksize;
796 if (insize % gcryblocksize)
797 len += gcryblocksize;
801 * Resize the existing xml buffer to the block size required by gcrypt
802 * rather than duplicating it and wasting memory.
804 inbuf = gcry_realloc(data, len);
806 if (!inbuf)
807 return gpg_error_from_errno(ENOMEM);
809 insize = len;
810 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
811 memcpy(tkey, shakey, sizeof(tkey));
812 tkey[0] ^= 1;
814 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
815 memset(tkey, 0, sizeof(tkey));
816 gcry_free(inbuf);
817 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
818 return rc;
821 memset(tkey, 0, sizeof(tkey));
822 file_header.iter = iter;
824 if (client)
825 iter_progress = get_key_file_integer(client->filename, "iteration_progress");
827 if (client && iter_progress && file_header.iter >= iter_progress) {
828 rc = assuan_write_status(client->ctx, "ENCRYPT",
829 print_fmt(str, sizeof(str), "%i %i", 0, file_header.iter));
831 if (rc) {
832 gcry_free(inbuf);
833 return rc;
837 while (xiter < file_header.iter) {
838 if (client && iter_progress > 0 && xiter >= iter_progress) {
839 if (!(xiter % iter_progress)) {
840 rc = assuan_write_status(client->ctx, "ENCRYPT",
841 print_fmt(str, sizeof(str), "%i %i", ++n_iter * iter_progress, file_header.iter));
843 if (rc) {
844 gcry_free(inbuf);
845 return rc;
850 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
851 sizeof(file_header.iv)))) {
852 gcry_free(inbuf);
853 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
854 return rc;
857 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
858 == FALSE) {
859 gcry_free(inbuf);
860 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
861 return rc;
864 xiter++;
865 pth_yield(NULL);
868 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
869 sizeof(file_header.iv)))) {
870 gcry_free(inbuf);
871 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
872 return rc;
875 if ((rc = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
876 gcry_free(inbuf);
877 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
878 return rc;
881 if (encrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
882 gcry_free(inbuf);
883 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
884 return rc;
887 if (client && iter_progress && file_header.iter >= iter_progress) {
888 rc = assuan_write_status(client->ctx, "ENCRYPT",
889 print_fmt(str, sizeof(str), "%i %i", file_header.iter, file_header.iter));
891 if (rc) {
892 gcry_free(inbuf);
893 return rc;
897 write_file:
898 if (filename) {
899 if (stat(filename, &st) == 0) {
900 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
903 * FIXME What if the file has an ACL?
905 if (!(mode & S_IWUSR)) {
906 gcry_free(inbuf);
907 return gpg_error_from_errno(EACCES);
910 else {
911 if (errno != ENOENT) {
912 rc = errno;
913 gcry_free(inbuf);
914 return gpg_error_from_errno(rc);
918 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
920 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
921 rc = errno;
922 gcry_free(inbuf);
923 p = strrchr(tmp, '/');
924 p++;
925 log_write("%s: %s", p, strerror(rc));
926 return gpg_error_from_errno(rc);
929 else
931 * xml_import() from command line.
933 fd = STDOUT_FILENO;
935 len = pth_write(fd, &file_header, sizeof(file_header_t));
937 if (len != sizeof(file_header)) {
938 len = errno;
940 if (filename) {
941 close(fd);
942 unlink(tmp);
945 gcry_free(inbuf);
946 return gpg_error_from_errno(len);
949 len = pth_write(fd, inbuf, insize);
951 if (len != insize) {
952 len = errno;
954 if (filename) {
955 close(fd);
956 unlink(tmp);
959 gcry_free(inbuf);
960 return gpg_error_from_errno(len);
963 if (fsync(fd) == -1) {
964 len = errno;
966 if (filename) {
967 close(fd);
968 unlink(tmp);
971 gcry_free(inbuf);
972 return gpg_error_from_errno(len);
975 if (filename) {
976 close(fd);
978 if (mode && get_key_file_boolean(filename, "backup") == TRUE) {
979 gchar tmp2[FILENAME_MAX];
981 g_snprintf(tmp2, sizeof(tmp2), "%s.backup", filename);
983 if (rename(filename, tmp2) == -1) {
984 unlink(tmp);
985 len = errno;
986 gcry_free(inbuf);
987 return gpg_error_from_errno(len);
991 if (rename(tmp, filename) == -1) {
992 len = errno;
993 unlink(tmp);
994 gcry_free(inbuf);
995 return gpg_error_from_errno(len);
998 if (mode)
999 chmod(filename, mode);
1002 gcry_free(inbuf);
1003 return 0;
1006 static gpg_error_t save_command_finalize(assuan_context_t ctx,
1007 guchar shakey[], gboolean cached)
1009 struct client_s *client = assuan_get_pointer(ctx);
1010 gpointer xmlbuf;
1011 xmlChar *p;
1012 gint len;
1013 gint iter;
1014 gint timeout;
1015 gpointer outbuf;
1016 glong outsize = 0;
1017 gint zrc;
1018 gpg_error_t rc;
1019 struct stat st;
1021 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
1022 xmlbuf = p;
1024 iter = get_key_file_integer(client->filename, "compression_level");
1026 if (iter < 0)
1027 iter = 0;
1029 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zrc) == FALSE) {
1030 memset(shakey, 0, sizeof(shakey));
1031 xmlFree(xmlbuf);
1033 if (zrc == Z_MEM_ERROR) {
1034 return send_syserror(ctx, ENOMEM);
1036 else
1037 return send_error(ctx, GPG_ERR_COMPR_ALGO);
1039 else {
1040 gcry_free(xmlbuf);
1041 xmlbuf = outbuf;
1042 len = outsize;
1045 iter = get_key_file_integer(client->filename, "iterations");
1046 rc = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
1048 if (rc) {
1049 memset(shakey, 0, sizeof(shakey));
1050 return send_error(ctx, rc);
1053 stat(client->filename, &st);
1054 client->mtime = st.st_mtime;
1055 timeout = get_key_file_integer(client->filename, "cache_timeout");
1056 CACHE_LOCK(client->ctx);
1058 if (cached) {
1059 memset(shakey, 0, sizeof(shakey));
1060 cache_reset_timeout(client->md5file, timeout);
1061 CACHE_UNLOCK;
1063 if (client->new == TRUE)
1064 send_status_all(STATUS_CACHE);
1066 client->new = FALSE;
1067 return send_error(ctx, 0);
1070 if (cache_update_key(client->md5file, shakey) == FALSE) {
1071 memset(shakey, 0, sizeof(shakey));
1072 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
1073 CACHE_UNLOCK;
1074 return send_error(ctx, EPWMD_MAX_SLOTS);
1077 client->new = FALSE;
1078 memset(shakey, 0, sizeof(shakey));
1079 cache_reset_timeout(client->md5file, timeout);
1080 CACHE_UNLOCK;
1081 send_status_all(STATUS_CACHE);
1082 return send_error(ctx, 0);
1085 static int save_command(assuan_context_t ctx, char *line)
1087 gboolean cached = FALSE;
1088 guchar shakey[gcrykeysize];
1089 struct stat st;
1090 struct client_s *client = assuan_get_pointer(ctx);
1091 gpg_error_t rc;
1093 memset(shakey, 0, sizeof(shakey));
1094 rc = file_modified(client);
1096 if (rc) {
1097 log_write("%s: %s", client->filename ? client->filename : "",
1098 pwmd_strerror(rc));
1099 return send_error(ctx, rc);
1102 rc = lock_file_mutex(client);
1104 if (rc)
1105 return send_error(ctx, rc);
1107 if (stat(client->filename, &st) == -1 && errno != ENOENT)
1108 return send_syserror(ctx, errno);
1110 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
1111 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
1112 return send_error(ctx, EPWMD_INVALID_FILENAME);
1115 if (get_key_file_integer(client->filename, "iterations") == -1)
1116 goto done;
1118 if (!line || !*line) {
1119 guchar tmp[sizeof(shakey)];
1120 CACHE_LOCK(ctx);
1122 memset(tmp, '!', sizeof(tmp));
1124 if (cache_get_key(client->md5file, shakey) == FALSE ||
1125 memcmp(shakey, tmp, sizeof(shakey)) == 0) {
1126 CACHE_UNLOCK;
1127 #ifdef WITH_PINENTRY
1128 if (get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1129 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1130 goto done;
1133 lock_pin_mutex(client);
1134 client->pinentry->which = PINENTRY_SAVE;
1135 rc = pinentry_fork(ctx);
1137 if (rc) {
1138 unlock_pin_mutex(client->pinentry);
1139 return send_error(ctx, rc);
1142 client->pinentry->cb = save_command_finalize;
1143 client->pinentry->status = PINENTRY_INIT;
1144 return 0;
1145 #else
1146 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1147 goto done;
1148 #endif
1150 else {
1151 CACHE_UNLOCK;
1152 cached = TRUE;
1155 else {
1156 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
1157 memset(line, 0, strlen(line));
1160 done:
1161 return save_command_finalize(ctx, shakey, cached);
1164 static int delete_command(assuan_context_t ctx, char *line)
1166 struct client_s *client = assuan_get_pointer(ctx);
1167 gchar **req;
1168 gpg_error_t rc;
1169 xmlNodePtr n;
1171 rc = file_modified(client);
1173 if (rc) {
1174 log_write("%s: %s", client->filename ? client->filename : "",
1175 pwmd_strerror(rc));
1176 return send_error(ctx, rc);
1179 if (strchr(line, '\t'))
1180 req = split_input_line(line, "\t", -1);
1181 else
1182 req = split_input_line(line, " ", -1);
1184 if (!req || !*req)
1185 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1187 n = find_account(client->doc, &req, &rc, NULL, 0);
1189 if (!n) {
1190 g_strfreev(req);
1191 return send_error(ctx, rc);
1195 * No sub-node defined. Remove the entire node (account).
1197 if (!req[1]) {
1198 if (n) {
1199 xmlUnlinkNode(n);
1200 xmlFreeNode(n);
1203 g_strfreev(req);
1204 return send_error(ctx, 0);
1207 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1208 g_strfreev(req);
1210 if (!n)
1211 return send_error(ctx, rc);
1213 if (n) {
1214 xmlUnlinkNode(n);
1215 xmlFreeNode(n);
1218 return send_error(ctx, 0);
1222 * Don't return with assuan_process_done() here. This has been called from
1223 * assuan_process_next() and the command should be finished in
1224 * client_thread().
1226 static int store_command_finalize(gpointer data, gint assuan_rc, guchar *line,
1227 gsize len)
1229 assuan_context_t ctx = data;
1230 struct client_s *client = assuan_get_pointer(ctx);
1231 gchar **req;
1232 xmlNodePtr n;
1233 gpg_error_t rc = file_modified(client);
1235 if (assuan_rc || rc) {
1236 if (line)
1237 #ifndef MEM_DEBUG
1238 xfree(line);
1239 #else
1240 free(line);
1241 #endif
1242 return assuan_rc ? assuan_rc : rc;
1245 req = split_input_line((gchar *)line, "\t", 0);
1246 #ifndef MEM_DEBUG
1247 xfree(line);
1248 #else
1249 free(line);
1250 #endif
1252 if (!req || !*req)
1253 return EPWMD_COMMAND_SYNTAX;
1255 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1256 g_strfreev(req);
1257 return EPWMD_INVALID_ELEMENT;
1260 if (valid_element_path(req+1, TRUE) == FALSE) {
1261 g_strfreev(req);
1262 return EPWMD_INVALID_ELEMENT;
1265 again:
1266 n = find_account(client->doc, &req, &rc, NULL, 0);
1268 if (rc && rc == EPWMD_ELEMENT_NOT_FOUND) {
1269 rc = new_account(client->doc, *req);
1271 if (rc) {
1272 g_strfreev(req);
1273 return rc;
1276 goto again;
1279 if (!n) {
1280 g_strfreev(req);
1281 return rc;
1284 if (req[1]) {
1285 if (!n->children)
1286 create_elements_cb(n, req+1, &rc, NULL);
1287 else
1288 find_elements(client->doc, n->children, req+1, &rc,
1289 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1292 g_strfreev(req);
1293 client->inquire_status = INQUIRE_DONE;
1294 return rc;
1297 static int store_command(assuan_context_t ctx, char *line)
1299 struct client_s *client = assuan_get_pointer(ctx);
1300 gpg_error_t rc = file_modified(client);
1302 if (rc) {
1303 log_write("%s: %s", client->filename ? client->filename : "",
1304 pwmd_strerror(rc));
1305 return send_error(ctx, rc);
1308 rc = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1310 if (rc)
1311 return send_error(ctx, rc);
1313 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1314 client->inquire_status = INQUIRE_BUSY;
1315 return 0;
1318 static int get_command(assuan_context_t ctx, char *line)
1320 struct client_s *client = assuan_get_pointer(ctx);
1321 gchar **req;
1322 gpg_error_t rc;
1323 xmlNodePtr n;
1325 rc = file_modified(client);
1327 if (rc) {
1328 log_write("%s: %s", client->filename ? client->filename : "",
1329 pwmd_strerror(rc));
1330 return send_error(ctx, rc);
1333 req = split_input_line(line, "\t", -1);
1335 if (!req || !*req) {
1336 g_strfreev(req);
1337 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1340 n = find_account(client->doc, &req, &rc, NULL, 0);
1342 if (!n) {
1343 g_strfreev(req);
1344 return send_error(ctx, rc);
1347 if (req[1])
1348 n = find_elements(client->doc, n->children, req+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
1350 g_strfreev(req);
1352 if (rc)
1353 return send_error(ctx, rc);
1355 if (!n || !n->children)
1356 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1358 n = find_text_node(n->children);
1360 if (!n || !n->content || !*n->content)
1361 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1363 rc = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1364 return send_error(ctx, rc);
1367 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **target,
1368 gpg_error_t *rc, gchar **req_orig, void *data)
1370 gchar *path = *(gchar **)data;
1371 gchar *tmp = NULL, *result;
1373 if (path) {
1374 g_free(path);
1375 *(gchar **)data = NULL;
1378 path = g_strjoinv("\t", target);
1380 if (!path) {
1381 *rc = gpg_error_from_errno(ENOMEM);
1382 return NULL;
1385 if (req_orig) {
1386 tmp = g_strjoinv("\t", req_orig);
1388 if (!tmp) {
1389 g_free(path);
1390 *rc = gpg_error_from_errno(ENOMEM);
1391 return NULL;
1395 if (tmp && *tmp)
1396 result = g_strdup_printf("%s\t%s", path, tmp);
1397 else
1398 result = g_strdup(path);
1400 if (!result) {
1401 *rc = gpg_error_from_errno(ENOMEM);
1402 g_free(path);
1403 g_free(tmp);
1404 return NULL;
1407 g_free(path);
1408 g_free(tmp);
1409 *(gchar **)data = result;
1410 return node;
1413 static int realpath_command(assuan_context_t ctx, char *line)
1415 gpg_error_t rc;
1416 struct client_s *client = assuan_get_pointer(ctx);
1417 gchar **req;
1418 gchar *t;
1419 gint i;
1420 xmlNodePtr n;
1421 GString *string;
1422 gchar *rp = NULL;
1424 rc = file_modified(client);
1426 if (rc) {
1427 log_write("%s: %s", client->filename ? client->filename : "",
1428 pwmd_strerror(rc));
1429 return send_error(ctx, rc);
1432 if (strchr(line, '\t') != NULL) {
1433 if ((req = split_input_line(line, "\t", 0)) == NULL)
1434 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1436 else {
1437 if ((req = split_input_line(line, " ", 0)) == NULL)
1438 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1441 n = find_account(client->doc, &req, &rc, NULL, 0);
1443 if (!n) {
1444 g_strfreev(req);
1445 return send_error(ctx, rc);
1448 rp = g_strjoinv("\t", req);
1450 if (!rp) {
1451 g_strfreev(req);
1452 return send_syserror(ctx, ENOMEM);
1455 if (req[1]) {
1456 n = find_elements(client->doc, n->children, req+1, &rc,
1457 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp);
1459 if (!n) {
1460 g_free(rp);
1461 g_strfreev(req);
1462 return send_error(ctx, rc);
1466 string = g_string_new(rp);
1467 g_free(rp);
1468 g_strfreev(req);
1470 if (!string)
1471 return send_syserror(ctx, ENOMEM);
1473 again:
1474 for (i = 0, t = string->str + i; *t; t++, i++) {
1475 if ((!i && *t != '!') || (*t == '\t' && *(t+1) && *(t+1) != '!')) {
1476 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1477 goto again;
1481 rc = assuan_send_data(ctx, string->str, string->len);
1482 g_string_free(string, TRUE);
1483 return send_error(ctx, rc);
1486 static int list_command(assuan_context_t ctx, char *line)
1488 struct client_s *client = assuan_get_pointer(ctx);
1489 gpg_error_t rc;
1490 struct element_list_s *elements = NULL;
1491 gchar *tmp;
1493 if (disable_list_and_dump == TRUE)
1494 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1496 rc = file_modified(client);
1498 if (rc) {
1499 log_write("%s: %s", client->filename, pwmd_strerror(rc));
1500 return send_error(ctx, rc);
1503 if (!*line) {
1504 GString *str;
1506 rc = list_accounts(client->doc, &str);
1508 if (rc)
1509 return send_error(ctx, rc);
1511 rc = assuan_send_data(ctx, str->str, str->len);
1512 g_string_free(str, TRUE);
1513 return send_error(ctx, rc);
1516 elements = g_malloc0(sizeof(struct element_list_s));
1518 if (!elements) {
1519 rc = gpg_err_code_from_errno(ENOMEM);
1520 goto fail;
1523 rc = create_path_list(client->doc, elements, line);
1525 if (rc)
1526 goto fail;
1528 if (elements) {
1529 gint total = g_slist_length(elements->list);
1530 gint i;
1531 GString *str;
1533 if (!total) {
1534 rc = EPWMD_EMPTY_ELEMENT;
1535 goto fail;
1538 str = g_string_new(NULL);
1540 if (!str) {
1541 rc = gpg_err_code_from_errno(ENOMEM);
1542 goto fail;
1545 for (i = 0; i < total; i++) {
1546 tmp = g_slist_nth_data(elements->list, i);
1547 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1550 rc = assuan_send_data(ctx, str->str, str->len);
1551 g_string_free(str, TRUE);
1553 else
1554 rc = EPWMD_EMPTY_ELEMENT;
1556 fail:
1557 if (elements) {
1558 gint total = g_slist_length(elements->list);
1559 gint i;
1561 for (i = 0; i < total; i++) {
1562 tmp = g_slist_nth_data(elements->list, i);
1563 g_free(tmp);
1566 g_slist_free(elements->list);
1568 if (elements->prefix)
1569 g_free(elements->prefix);
1571 g_free(elements);
1574 return send_error(ctx, rc);
1577 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1578 const gchar *value)
1580 xmlAttrPtr a;
1582 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1583 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1585 if (!a)
1586 return EPWMD_LIBXML_ERROR;
1588 else
1589 xmlNodeSetContent(a->children, (xmlChar *)value);
1591 return 0;
1595 * req[0] - element path
1597 static int attribute_list(assuan_context_t ctx, gchar **req)
1599 struct client_s *client = assuan_get_pointer(ctx);
1600 gchar **attrlist = NULL;
1601 gint i = 0;
1602 gchar **path = NULL;
1603 xmlAttrPtr a;
1604 xmlNodePtr n, an;
1605 gchar *line;
1606 gpg_error_t rc;
1608 if (!req || !req[0])
1609 return EPWMD_COMMAND_SYNTAX;
1611 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1613 * The first argument may be only an account.
1615 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1616 return EPWMD_COMMAND_SYNTAX;
1619 n = find_account(client->doc, &path, &rc, NULL, 0);
1621 if (!n) {
1622 g_strfreev(path);
1623 return rc;
1626 if (path[1]) {
1627 n = find_elements(client->doc, n->children, path+1, &rc,
1628 NULL, NULL, NULL, FALSE, 0, NULL);
1630 if (!n) {
1631 g_strfreev(path);
1632 return rc;
1636 g_strfreev(path);
1638 for (a = n->properties; a; a = a->next) {
1639 gchar **pa;
1641 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1642 if (attrlist)
1643 g_strfreev(attrlist);
1645 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1646 return gpg_error_from_errno(ENOMEM);
1649 attrlist = pa;
1650 an = a->children;
1651 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1653 if (!attrlist[i]) {
1654 g_strfreev(attrlist);
1655 return gpg_error_from_errno(ENOMEM);
1658 attrlist[++i] = NULL;
1661 if (!attrlist)
1662 return EPWMD_EMPTY_ELEMENT;
1664 line = g_strjoinv("\n", attrlist);
1666 if (!line) {
1667 g_strfreev(attrlist);
1668 return gpg_error_from_errno(ENOMEM);
1671 rc = assuan_send_data(ctx, line, strlen(line));
1672 g_free(line);
1673 g_strfreev(attrlist);
1674 return rc;
1678 * req[0] - attribute
1679 * req[1] - element path
1681 static int attribute_delete(struct client_s *client, gchar **req)
1683 xmlAttrPtr a;
1684 xmlNodePtr n;
1685 gchar **path = NULL;
1686 gpg_error_t rc;
1688 if (!req || !req[0] || !req[1])
1689 return EPWMD_COMMAND_SYNTAX;
1691 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1693 * The first argument may be only an account.
1695 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1696 return EPWMD_COMMAND_SYNTAX;
1700 * Don't remove the "name" attribute for the account element. To remove an
1701 * account use DELETE <account>.
1703 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1704 rc = EPWMD_ATTR_SYNTAX;
1705 goto fail;
1708 n = find_account(client->doc, &path, &rc, NULL, 0);
1710 if (!n)
1711 goto fail;
1713 if (path[1]) {
1714 n = find_elements(client->doc, n->children, path+1, &rc,
1715 NULL, NULL, NULL, FALSE, 0, NULL);
1717 if (!n)
1718 goto fail;
1721 g_strfreev(path);
1723 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1724 return EPWMD_ATTR_NOT_FOUND;
1726 if (xmlRemoveProp(a) == -1)
1727 return EPWMD_LIBXML_ERROR;
1729 return 0;
1731 fail:
1732 g_strfreev(path);
1733 return rc;
1736 static xmlNodePtr create_element_path(struct client_s *client, gchar ***path,
1737 gpg_error_t *rc)
1739 gchar **src = *path;
1740 gchar **src_orig = g_strdupv(src);
1741 xmlNodePtr n = NULL;
1743 *rc = 0;
1745 if (!src_orig) {
1746 *rc = gpg_error_from_errno(ENOMEM);
1747 goto fail;
1750 again:
1751 n = find_account(client->doc, &src, rc, NULL, 0);
1753 if (!n) {
1754 if (*rc == EPWMD_ELEMENT_NOT_FOUND) {
1755 *rc = new_account(client->doc, src[0]);
1757 if (*rc)
1758 goto fail;
1760 goto again;
1762 else
1763 goto fail;
1766 if (src[1]) {
1767 if (!n->children)
1768 n = create_target_elements_cb(n, src+1, rc, NULL);
1769 else
1770 n = find_elements(client->doc, n->children, src+1, rc,
1771 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
1773 if (!n)
1774 goto fail;
1777 * Reset the position of the element tree now that the elements
1778 * have been created.
1780 g_strfreev(src);
1781 src = src_orig;
1782 src_orig = NULL;
1783 n = find_account(client->doc, &src, rc, NULL, 0);
1785 if (!n)
1786 goto fail;
1788 n = find_elements(client->doc, n->children, src+1, rc,
1789 NULL, NULL, NULL, FALSE, 0, NULL);
1791 if (!n)
1792 goto fail;
1795 fail:
1796 if (src_orig)
1797 g_strfreev(src_orig);
1799 *path = src;
1800 return n;
1804 * Creates a "target" attribute. When other commands encounter an element with
1805 * this attribute, the element path is modified to the target value. If the
1806 * source element path doesn't exist when using 'ATTR SET target', it is
1807 * created, but the destination element path must exist.
1809 * req[0] - source element path
1810 * req[1] - destination element path
1812 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1814 gchar **src, **dst, *line = NULL;
1815 gpg_error_t rc;
1816 xmlNodePtr n;
1818 if (!req || !req[0] || !req[1])
1819 return EPWMD_COMMAND_SYNTAX;
1821 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1823 * The first argument may be only an account.
1825 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1826 return EPWMD_COMMAND_SYNTAX;
1829 if (valid_element_path(src, FALSE) == FALSE) {
1830 g_strfreev(src);
1831 return EPWMD_INVALID_ELEMENT;
1834 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1836 * The first argument may be only an account.
1838 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1839 rc = EPWMD_COMMAND_SYNTAX;
1840 goto fail;
1844 n = find_account(client->doc, &dst, &rc, NULL, 0);
1847 * Make sure the destination element path exists.
1849 if (!n)
1850 goto fail;
1852 if (dst[1]) {
1853 n = find_elements(client->doc, n->children, dst+1, &rc,
1854 NULL, NULL, NULL, FALSE, 0, NULL);
1856 if (!n)
1857 goto fail;
1860 n = create_element_path(client, &src, &rc);
1862 if (rc)
1863 goto fail;
1865 line = g_strjoinv("\t", dst);
1867 if (!line) {
1868 rc = gpg_error_from_errno(ENOMEM);
1869 goto fail;
1872 rc = add_attribute(n, "target", line);
1874 fail:
1875 g_free(line);
1876 g_strfreev(src);
1877 g_strfreev(dst);
1878 return rc;
1882 * req[0] - account name
1883 * req[1] - new name
1885 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1887 gpg_error_t rc;
1888 gchar **tmp;
1889 xmlNodePtr n;
1891 tmp = g_strdupv(req);
1893 if (!tmp)
1894 return gpg_error_from_errno(ENOMEM);
1896 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1897 g_strfreev(tmp);
1899 if (!n)
1900 return rc;
1902 if (g_utf8_collate(req[0], req[1]) == 0)
1903 return 0;
1906 * Will not overwrite an existing account.
1908 tmp = g_strdupv(req+1);
1910 if (!tmp)
1911 return gpg_error_from_errno(ENOMEM);
1913 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1914 g_strfreev(tmp);
1916 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND)
1917 return rc;
1919 if (n)
1920 return EPWMD_ACCOUNT_EXISTS;
1923 * Whitespace not allowed in account names.
1925 if (contains_whitespace(req[1]) == TRUE)
1926 return EPWMD_ATTR_SYNTAX;
1928 tmp = g_strdupv(req);
1930 if (!tmp)
1931 return gpg_error_from_errno(ENOMEM);
1933 n = find_account(client->doc, &tmp, &rc, NULL, 0);
1934 g_strfreev(tmp);
1936 if (!n)
1937 return EPWMD_ELEMENT_NOT_FOUND;
1939 return add_attribute(n, "name", req[1]);
1943 * req[0] - attribute
1944 * req[1] - element path
1946 static int attribute_get(assuan_context_t ctx, gchar **req)
1948 struct client_s *client = assuan_get_pointer(ctx);
1949 xmlNodePtr n;
1950 xmlChar *a;
1951 gchar **path= NULL;
1952 gpg_error_t rc;
1954 if (!req || !req[0] || !req[1])
1955 return EPWMD_COMMAND_SYNTAX;
1957 if (strchr(req[1], '\t')) {
1958 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1959 return EPWMD_COMMAND_SYNTAX;
1961 else {
1962 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1963 return EPWMD_COMMAND_SYNTAX;
1966 n = find_account(client->doc, &path, &rc, NULL, 0);
1968 if (!n)
1969 goto fail;
1971 if (path[1]) {
1972 n = find_elements(client->doc, n->children, path+1, &rc,
1973 NULL, NULL, NULL, FALSE, 0, NULL);
1975 if (!n)
1976 goto fail;
1979 g_strfreev(path);
1981 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1982 return EPWMD_ATTR_NOT_FOUND;
1984 rc = assuan_send_data(ctx, a, xmlStrlen(a));
1985 xmlFree(a);
1986 return rc;
1988 fail:
1989 g_strfreev(path);
1990 return rc;
1994 * req[0] - attribute
1995 * req[1] - element path
1996 * req[2] - value
1998 static int attribute_set(struct client_s *client, gchar **req)
2000 gchar **path = NULL;
2001 gpg_error_t rc;
2002 xmlNodePtr n;
2004 if (!req || !req[0] || !req[1] || !req[2])
2005 return EPWMD_COMMAND_SYNTAX;
2008 * Reserved attribute names.
2010 if (g_utf8_collate(req[0], "name") == 0) {
2012 * Only reserved for the account element. Not the rest of the
2013 * document.
2015 if (strchr(req[1], '\t') == NULL)
2016 return name_attribute(client, req + 1);
2018 else if (g_utf8_collate(req[0], "target") == 0)
2019 return target_attribute(client, req + 1);
2021 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
2023 * The first argument may be only an account.
2025 if ((path = split_input_line(req[1], " ", 0)) == NULL)
2026 return EPWMD_COMMAND_SYNTAX;
2029 n = find_account(client->doc, &path, &rc, NULL, 0);
2031 if (!n)
2032 goto fail;
2034 if (path[1]) {
2035 n = find_elements(client->doc, n->children, path+1, &rc,
2036 NULL, NULL, NULL, FALSE, 0, NULL);
2038 if (!n)
2039 goto fail;
2042 g_strfreev(path);
2043 return add_attribute(n, req[0], req[2]);
2045 fail:
2046 g_strfreev(path);
2047 return rc;
2051 * req[0] - command
2052 * req[1] - attribute name or element path if command is LIST
2053 * req[2] - element path
2054 * req[2] - element path or value
2056 static int attr_command(assuan_context_t ctx, char *line)
2058 struct client_s *client = assuan_get_pointer(ctx);
2059 gchar **req;
2060 gpg_error_t rc = 0;
2062 rc = file_modified(client);
2064 if (rc) {
2065 log_write("%s: %s", client->filename ? client->filename : "",
2066 pwmd_strerror(rc));
2067 return send_error(ctx, rc);
2070 req = split_input_line(line, " ", 4);
2072 if (!req || !req[0] || !req[1]) {
2073 g_strfreev(req);
2074 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2077 if (g_ascii_strcasecmp(req[0], "SET") == 0)
2078 rc = attribute_set(client, req+1);
2079 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
2080 rc = attribute_get(ctx, req+1);
2081 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
2082 rc = attribute_delete(client, req+1);
2083 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
2084 rc = attribute_list(ctx, req+1);
2085 else
2086 rc = EPWMD_COMMAND_SYNTAX;
2088 g_strfreev(req);
2089 return send_error(ctx, rc);
2092 static int iscached_command(assuan_context_t ctx, char *line)
2094 gchar **req = split_input_line(line, " ", 0);
2095 guchar md5file[16];
2097 if (!req || !*req) {
2098 g_strfreev(req);
2099 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2102 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2103 g_strfreev(req);
2104 CACHE_LOCK(ctx);
2106 if (cache_iscached(md5file) == FALSE) {
2107 CACHE_UNLOCK;
2108 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2111 CACHE_UNLOCK;
2112 return send_error(ctx, 0);
2115 static int clearcache_command(assuan_context_t ctx, char *line)
2117 struct client_s *client = assuan_get_pointer(ctx);
2118 gchar **req = split_input_line(line, " ", 0);
2119 guchar md5file[16];
2121 CACHE_LOCK(ctx);
2123 if (!req || !*req) {
2124 g_strfreev(req);
2125 cache_clear(client->md5file, 2);
2126 CACHE_UNLOCK;
2127 return send_error(ctx, 0);
2130 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2131 g_strfreev(req);
2133 if (cache_clear(md5file, 1) == FALSE) {
2134 CACHE_UNLOCK;
2135 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2138 CACHE_UNLOCK;
2139 return send_error(ctx, 0);
2142 static int cachetimeout_command(assuan_context_t ctx, char *line)
2144 guchar md5file[16];
2145 glong timeout;
2146 gchar **req = split_input_line(line, " ", 0);
2147 gchar *p;
2148 struct client_s *client = assuan_get_pointer(ctx);
2150 if (!req || !*req || !req[1]) {
2151 g_strfreev(req);
2152 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2155 errno = 0;
2156 timeout = strtol(req[0], &p, 10);
2158 if (errno != 0 || *p != 0) {
2159 g_strfreev(req);
2160 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2163 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2164 g_strfreev(req);
2165 CACHE_LOCK(client->ctx);
2167 if (cache_set_timeout(md5file, timeout) == FALSE) {
2168 CACHE_UNLOCK;
2169 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2172 CACHE_UNLOCK;
2173 return send_error(ctx, 0);
2176 static int dump_command(assuan_context_t ctx, char *line)
2178 xmlChar *xml;
2179 gssize len;
2180 struct client_s *client = assuan_get_pointer(ctx);
2181 gpg_error_t rc;
2183 if (disable_list_and_dump == TRUE)
2184 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2186 rc = file_modified(client);
2188 if (rc) {
2189 log_write("%s: %s", client->filename ? client->filename : "",
2190 pwmd_strerror(rc));
2191 return send_error(ctx, rc);
2194 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2196 if (!xml)
2197 return send_syserror(ctx, ENOMEM);
2199 rc = assuan_send_data(ctx, xml, len);
2200 xmlFree(xml);
2201 return send_error(ctx, rc);
2204 static int getconfig_command(assuan_context_t ctx, gchar *line)
2206 struct client_s *client = assuan_get_pointer(ctx);
2207 gpg_error_t rc = 0;
2208 gchar filename[255]={0}, param[747]={0};
2209 gchar *p, *tmp, *fp = client->filename, *paramp = line;
2211 if (strchr(line, ' ')) {
2212 sscanf(line, " %254[a-zA-Z] %746c", filename, param);
2213 fp = filename;
2214 paramp = param;
2217 if (strcmp(line, "key") == 0 || strcmp(line, "key_file") == 0)
2218 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2220 p = get_key_file_string(fp ? fp : "global", paramp);
2222 if (!p)
2223 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2225 tmp = expand_homedir(p);
2227 if (!tmp) {
2228 g_free(p);
2229 return send_syserror(ctx, ENOMEM);
2232 g_free(p);
2233 p = tmp;
2234 rc = assuan_send_data(ctx, p, strlen(p));
2235 g_free(p);
2236 return send_error(ctx, rc);
2239 static int xpath_command(assuan_context_t ctx, gchar *line)
2241 struct client_s *client = assuan_get_pointer(ctx);
2242 gpg_error_t rc;
2243 xmlXPathContextPtr xp;
2244 xmlXPathObjectPtr result;
2245 xmlBufferPtr buf = NULL;
2246 gchar **req = NULL;
2248 rc = file_modified(client);
2250 if (rc) {
2251 log_write("%s: %s", client->filename ? client->filename : "",
2252 pwmd_strerror(rc));
2253 return send_error(ctx, rc);
2256 if (!line || !*line)
2257 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2259 if ((req = split_input_line(line, "\t", 2)) == NULL) {
2260 if (strv_printf(&req, "%s", line) == FALSE)
2261 return send_syserror(ctx, ENOMEM);
2264 xp = xmlXPathNewContext(client->doc);
2266 if (!xp)
2267 return send_error(ctx, EPWMD_LIBXML_ERROR);
2269 result = xmlXPathEvalExpression((xmlChar *)req[0], xp);
2271 if (!result) {
2272 xmlXPathFreeContext(xp);
2273 return send_error(ctx, EPWMD_LIBXML_ERROR);
2276 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2277 rc = EPWMD_EMPTY_ELEMENT;
2278 goto fail;
2281 rc = recurse_xpath_nodeset(client->doc, result->nodesetval,
2282 (xmlChar *)req[1], &buf);
2284 if (rc)
2285 goto fail;
2286 else if (!req[1] && !xmlBufferLength(buf)) {
2287 rc = EPWMD_EMPTY_ELEMENT;
2288 goto fail;
2290 else if (req[1])
2291 goto fail;
2293 rc = assuan_send_data(ctx, xmlBufferContent(buf), xmlBufferLength(buf));
2295 fail:
2296 g_strfreev(req);
2298 if (buf)
2299 xmlBufferFree(buf);
2301 if (result)
2302 xmlXPathFreeObject(result);
2304 if (xp)
2305 xmlXPathFreeContext(xp);
2307 return send_error(ctx, rc);
2310 static int import_command_finalize(gpointer data, gint assuan_rc, guchar *line,
2311 gsize len)
2313 struct client_s *client = assuan_get_pointer((assuan_context_t)data);
2314 gpg_error_t rc = file_modified(client);
2315 gchar **req, **path = NULL, **path_orig = NULL, *content;
2316 xmlDocPtr doc;
2317 xmlNodePtr n, root, copy;
2319 if (assuan_rc || rc) {
2320 if (line)
2321 #ifndef MEM_DEBUG
2322 xfree(line);
2323 #else
2324 free(line);
2325 #endif
2326 return assuan_rc ? assuan_rc : rc;
2329 req = split_input_line((gchar *)line, " ", 2);
2330 #ifndef MEM_DEBUG
2331 xfree(line);
2332 #else
2333 free(line);
2334 #endif
2336 if (!req || !*req)
2337 return EPWMD_COMMAND_SYNTAX;
2339 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
2340 if ((path = split_input_line(req[0], " ", 0)) == NULL)
2341 return EPWMD_COMMAND_SYNTAX;
2344 content = req[1];
2346 if (!content || !*content) {
2347 rc = EPWMD_COMMAND_SYNTAX;
2348 goto fail;
2351 if (valid_xml_element((xmlChar *)*path) == FALSE) {
2352 rc = EPWMD_INVALID_ELEMENT;
2353 goto fail;
2356 if (valid_element_path(path+1, FALSE) == FALSE) {
2357 rc = EPWMD_INVALID_ELEMENT;
2358 goto fail;
2361 doc = xmlReadDoc((xmlChar *)content, NULL, "UTF-8", XML_PARSE_NOBLANKS);
2363 if (!doc) {
2364 rc = EPWMD_LIBXML_ERROR;
2365 goto fail;
2368 root = xmlDocGetRootElement(doc);
2369 path_orig = g_strdupv(path);
2371 if (!path_orig) {
2372 xmlFreeDoc(doc);
2373 rc = gpg_error_from_errno(ENOMEM);
2374 goto fail;
2377 if (strv_printf(&path, "%s", (gchar *)root->name) == FALSE) {
2378 g_strfreev(path_orig);
2379 xmlFreeDoc(doc);
2380 rc = gpg_error_from_errno(ENOMEM);
2381 goto fail;
2384 n = find_account(client->doc, &path, &rc, NULL, 0);
2386 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2387 g_strfreev(path_orig);
2388 xmlFreeDoc(doc);
2389 goto fail;
2391 else if (!rc) {
2392 n = find_elements(client->doc, n->children, path+1, &rc, NULL, NULL, NULL, FALSE, 0, NULL);
2394 if (rc && rc != EPWMD_ELEMENT_NOT_FOUND) {
2395 g_strfreev(path_orig);
2396 xmlFreeDoc(doc);
2397 goto fail;
2399 else if (!rc) {
2400 xmlNodePtr parent = n->parent;
2402 xmlUnlinkNode(n);
2403 xmlFreeNode(n);
2404 n = parent;
2408 g_strfreev(path);
2409 path = path_orig;
2411 if (rc == EPWMD_ELEMENT_NOT_FOUND) {
2412 n = create_element_path(client, &path, &rc);
2414 if (rc) {
2415 xmlFreeDoc(doc);
2416 goto fail;
2420 copy = xmlCopyNode(root, 1);
2421 n = xmlAddChild(n, copy);
2422 xmlFreeDoc(doc);
2424 if (!n)
2425 rc = EPWMD_LIBXML_ERROR;
2427 fail:
2428 g_strfreev(path);
2429 g_strfreev(req);
2430 client->inquire_status = INQUIRE_DONE;
2431 return rc;
2434 static int import_command(assuan_context_t ctx, gchar *line)
2436 gpg_error_t rc;
2437 struct client_s *client = assuan_get_pointer(ctx);
2439 rc = file_modified(client);
2441 if (rc) {
2442 log_write("%s: %s", client->filename ? client->filename : "",
2443 pwmd_strerror(rc));
2444 return send_error(ctx, rc);
2447 rc = assuan_inquire_ext(ctx, "IMPORT", 0, import_command_finalize, ctx);
2449 if (rc)
2450 return send_error(ctx, rc);
2452 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
2453 client->inquire_status = INQUIRE_BUSY;
2454 return 0;
2457 static int lock_command(assuan_context_t ctx, gchar *line)
2459 gpg_error_t rc;
2460 struct client_s *client = assuan_get_pointer(ctx);
2462 rc = file_modified(client);
2464 if (rc) {
2465 log_write("%s: %s", client->filename ? client->filename : "",
2466 pwmd_strerror(rc));
2467 return send_error(ctx, rc);
2470 rc = lock_file_mutex(client);
2472 if (!rc)
2473 client->is_lock_cmd = TRUE;
2475 return send_error(ctx, rc);
2478 static int unlock_command(assuan_context_t ctx, gchar *line)
2480 gpg_error_t rc;
2481 struct client_s *client = assuan_get_pointer(ctx);
2483 rc = file_modified(client);
2485 if (rc) {
2486 log_write("%s: %s", client->filename ? client->filename : "",
2487 pwmd_strerror(rc));
2488 return send_error(ctx, rc);
2491 unlock_file_mutex(client);
2492 return send_error(ctx, 0);
2495 static int getpid_command(assuan_context_t ctx, gchar *line)
2497 gpg_error_t rc;
2498 gchar buf[32];
2499 pid_t pid = getpid();
2501 print_fmt(buf, sizeof(buf), "%i", pid);
2502 rc = assuan_send_data(ctx, buf, strlen(buf));
2503 return send_error(ctx, rc);
2506 void cleanup_assuan(assuan_context_t ctx)
2508 struct client_s *cl = assuan_get_pointer(ctx);
2510 if (cl)
2511 cleanup_client(cl);
2514 static void reset_notify(assuan_context_t ctx)
2516 struct client_s *cl = assuan_get_pointer(ctx);
2518 if (cl)
2519 cleanup_client(cl);
2522 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2524 gchar name[32] = {0}, value[256] = {0};
2526 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2527 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2529 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2530 pth_attr_t attr = pth_attr_of(pth_self());
2532 log_write("OPTION CLIENT %s", line);
2533 pth_attr_set(attr, PTH_ATTR_NAME, value);
2534 pth_attr_destroy(attr);
2536 else
2537 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2539 return 0;
2542 static int option_handler(assuan_context_t ctx, const gchar *name,
2543 const gchar *value)
2545 #ifdef WITH_PINENTRY
2546 struct client_s *client = assuan_get_pointer(ctx);
2547 #endif
2549 if (!value || !*value)
2550 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2552 if (g_strcasecmp(name, (gchar *)"client") == 0)
2553 return parse_client_option(ctx, value);
2555 if (g_strcasecmp(name, (gchar *)"iterations") == 0) {
2556 long n;
2557 gchar *p = NULL;
2559 errno = 0;
2560 n = strtol(value, &p, 10);
2562 if (errno || (p && *p))
2563 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2565 g_key_file_set_integer(keyfileh, client->filename ? client->filename : "global", "iterations", n);
2566 send_status_all(STATUS_CONFIG);
2568 #ifdef WITH_PINENTRY
2569 else if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2570 g_free(client->pinentry->ttyname);
2571 client->pinentry->ttyname = g_strdup(value);
2573 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2574 g_free(client->pinentry->ttytype);
2575 client->pinentry->ttytype = g_strdup(value);
2577 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2578 g_free(client->pinentry->display);
2579 client->pinentry->display = g_strdup(value);
2581 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2582 g_free(client->pinentry->path);
2583 client->pinentry->path = g_strdup(value);
2585 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2586 g_free(client->pinentry->title);
2587 client->pinentry->title = g_strdup(value);
2589 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2590 g_free(client->pinentry->prompt);
2591 client->pinentry->prompt = g_strdup(value);
2593 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2594 g_free(client->pinentry->desc);
2595 client->pinentry->desc = g_strdup(value);
2597 #if 0
2598 /* Need to wait for pinentry to support a --timeout option so it can
2599 * terminate itself. Cannot do it here because assuan_pipe_connect() calls
2600 * execv() which replaces the pid of the fork()ed thread from
2601 * pinentry_fork(). So pinentry will become a real process after the
2602 * thread terminates and won't be able to be kill()ed from pwmd.
2604 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2605 gchar *p = NULL;
2606 gint n = strtol(value, &p, 10);
2608 if (*p || n < 0)
2609 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2611 client->pinentry->timeout = n;
2613 #endif
2614 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2615 gchar *p = NULL;
2616 gint n = strtol(value, &p, 10);
2618 if (*p || n < 0 || n > 1)
2619 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2621 client->pinentry->enable = n == 0 ? FALSE : TRUE;
2623 #else
2624 else
2625 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2626 #endif
2628 log_write("OPTION %s=%s", name, value);
2629 return 0;
2632 gpg_error_t register_commands(assuan_context_t ctx)
2634 static struct {
2635 const gchar *name;
2636 gint (*handler)(assuan_context_t, gchar *line);
2637 } table[] = {
2638 { "OPEN", open_command },
2639 { "SAVE", save_command },
2640 { "LIST", list_command },
2641 { "REALPATH", realpath_command },
2642 { "STORE", store_command },
2643 { "DELETE", delete_command },
2644 { "GET", get_command },
2645 { "ATTR", attr_command },
2646 { "ISCACHED", iscached_command },
2647 { "CLEARCACHE", clearcache_command },
2648 { "CACHETIMEOUT", cachetimeout_command },
2649 { "GETCONFIG", getconfig_command },
2650 { "DUMP", dump_command },
2651 { "XPATH", xpath_command },
2652 { "IMPORT", import_command },
2653 { "LOCK", lock_command },
2654 { "UNLOCK", unlock_command },
2655 { "GETPID", getpid_command },
2656 { "INPUT", NULL },
2657 { "OUTPUT", NULL },
2658 { NULL, NULL }
2660 gint i, rc;
2662 for (i=0; table[i].name; i++) {
2663 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2665 if (rc)
2666 return rc;
2669 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2671 if (rc)
2672 return rc;
2674 rc = assuan_register_option_handler(ctx, option_handler);
2676 if (rc)
2677 return rc;
2679 return assuan_register_reset_notify(ctx, reset_notify);
2682 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2683 guchar *key, gint *dst_iter)
2685 guchar *iv;
2686 void *inbuf;
2687 gsize insize, len;
2688 guchar tkey[gcrykeysize];
2689 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2690 gcry_cipher_hd_t gh;
2691 guint iter = 0, n_iter = 0;
2692 gint iter_progress;
2693 void *outbuf = NULL;
2694 gint zrc = 0;
2695 glong outsize = 0;
2696 gpg_error_t rc;
2697 file_header_t file_header;
2698 gchar str[ASSUAN_LINELENGTH];
2700 if (!ctx) {
2701 rc = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2703 if (rc)
2704 return rc;
2706 else
2707 gh = client->gh;
2709 lseek(fd, 0, SEEK_SET);
2710 insize = st.st_size - sizeof(file_header_t);
2711 iv = gcry_malloc(gcryblocksize);
2713 if (!iv) {
2714 if (!ctx)
2715 gcry_cipher_close(gh);
2717 return gpg_error_from_errno(ENOMEM);
2720 len = pth_read(fd, &file_header, sizeof(file_header_t));
2722 if (len != sizeof(file_header_t)) {
2723 len = errno;
2725 if (!ctx)
2726 gcry_cipher_close(gh);
2728 gcry_free(iv);
2729 errno = len;
2730 return gpg_error_from_errno(errno);
2733 *dst_iter = file_header.iter;
2735 /* No encryption iterations. This is a plain (gzipped) file. */
2736 if (file_header.iter == -1) {
2738 * cache_file_count() needs both .used == TRUE and a valid key in
2739 * order for it to count as a used cache entry. Fixes CACHE status
2740 * messages.
2742 memset(key, '!', gcrykeysize);
2743 insize = st.st_size - sizeof(file_header_t);
2746 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2747 inbuf = gcry_malloc(insize);
2749 if (!inbuf) {
2750 if (!ctx)
2751 gcry_cipher_close(gh);
2753 gcry_free(iv);
2754 return gpg_error_from_errno(ENOMEM);
2757 len = pth_read(fd, inbuf, insize);
2759 if (len != insize) {
2760 len = errno;
2762 if (!ctx)
2763 gcry_cipher_close(gh);
2765 gcry_free(iv);
2766 errno = len;
2767 return gpg_error_from_errno(errno);
2770 if (file_header.iter == -1)
2771 goto decompress;
2773 if ((rc = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2774 if (!ctx) {
2775 gcry_cipher_close(gh);
2776 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2778 else
2779 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2781 gcry_free(inbuf);
2782 gcry_free(iv);
2783 return rc;
2786 if ((rc = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2787 if (!ctx) {
2788 gcry_cipher_close(gh);
2789 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2791 else
2792 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2794 gcry_free(inbuf);
2795 gcry_free(iv);
2797 if (!ctx)
2798 gcry_cipher_close(gh);
2800 return rc;
2803 iter_progress = get_key_file_integer(client->filename, "iteration_progress");
2805 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2806 rc = assuan_write_status(client->ctx, "DECRYPT",
2807 print_fmt(str, sizeof(str), "%i %i", 0, file_header.iter));
2809 if (rc) {
2810 gcry_free(inbuf);
2811 gcry_free(iv);
2812 return rc;
2816 rc = decrypt_xml(gh, inbuf, insize, NULL, 0);
2818 if (rc) {
2819 gcry_free(inbuf);
2820 gcry_free(iv);
2821 return rc;
2824 memcpy(tkey, key, sizeof(tkey));
2825 tkey[0] ^= 1;
2827 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2828 if (!ctx) {
2829 gcry_cipher_close(gh);
2830 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2832 else
2833 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2835 memset(tkey, 0, sizeof(tkey));
2836 gcry_free(inbuf);
2837 gcry_free(iv);
2838 return rc;
2841 memset(tkey, 0, sizeof(tkey));
2843 while (iter < file_header.iter) {
2844 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2845 if (!(iter % iter_progress)) {
2846 rc = assuan_write_status(ctx, "DECRYPT",
2847 print_fmt(str, sizeof(str), "%i %i", ++n_iter * iter_progress, file_header.iter));
2849 if (rc) {
2850 gcry_free(inbuf);
2851 gcry_free(iv);
2852 return rc;
2857 if ((rc = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2858 if (!ctx) {
2859 gcry_cipher_close(gh);
2860 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2862 else
2863 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
2865 gcry_free(inbuf);
2866 gcry_free(iv);
2867 return rc;
2870 rc = decrypt_xml(gh, inbuf, insize, NULL, 0);
2872 if (rc) {
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 iter++;
2886 pth_yield(NULL);
2889 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2890 rc = assuan_write_status(ctx, "DECRYPT",
2891 print_fmt(str, sizeof(str), "%i %i", file_header.iter, file_header.iter));
2893 if (rc) {
2894 gcry_free(inbuf);
2895 gcry_free(iv);
2896 return rc;
2900 gcry_free(iv);
2902 decompress:
2903 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zrc) == FALSE) {
2905 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2907 if (zrc == Z_MEM_ERROR) {
2908 gcry_free(inbuf);
2909 return gpg_error_from_errno(ENOMEM);
2911 else if (zrc != Z_DATA_ERROR) {
2912 gcry_free(inbuf);
2914 if (!ctx)
2915 gcry_cipher_close(gh);
2917 return EPWMD_BADKEY;
2920 else {
2921 gcry_free(inbuf);
2922 inbuf = outbuf;
2923 insize = outsize;
2926 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2927 gcry_free(inbuf);
2929 if (!ctx)
2930 gcry_cipher_close(gh);
2932 return EPWMD_BADKEY;
2935 if (ctx) {
2936 client->xml = inbuf;
2937 client->len = insize;
2939 else {
2940 gcry_cipher_close(gh);
2941 gcry_free(inbuf);
2944 return 0;
2948 * This is called after every Assuan command.
2950 void command_finalize(assuan_context_t ctx, gint rc)
2952 struct client_s *client = assuan_get_pointer(ctx);
2954 if (!client->is_lock_cmd)
2955 unlock_file_mutex(client);