Fixed a null pointer reference which could lead to a segfault.
[pwmd.git] / src / commands.c
blob4e7ed28887cc11729cb3b65735079cc20bf68efd
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;
74 if (client->state != STATE_OPEN)
75 return EPWMD_NO_FILE;
77 if (stat(client->filename, &st) == 0 && client->mtime) {
78 if (client->mtime != st.st_mtime)
79 return EPWMD_FILE_MODIFIED;
82 return 0;
85 static gboolean encrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
86 void *inbuf, gsize insize)
88 gpg_error_t rc;
90 if ((rc = gcry_cipher_encrypt(gh, outbuf, outsize, inbuf, insize))) {
91 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
92 return FALSE;
95 return TRUE;
98 static gpg_error_t decrypt_xml(gcry_cipher_hd_t gh, void *outbuf, gsize outsize,
99 void *inbuf, gsize insize)
101 gpg_error_t rc;
103 if ((rc = gcry_cipher_decrypt(gh, outbuf, outsize, inbuf, insize)))
104 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
106 return rc;
109 static gpg_error_t parse_xml(assuan_context_t ctx)
111 struct client_s *client = assuan_get_pointer(ctx);
113 client->doc = xmlReadMemory(client->xml, client->len, NULL, "UTF-8", XML_PARSE_NOBLANKS);
115 if (!client->doc)
116 return EPWMD_LIBXML_ERROR;
118 return 0;
121 void unlock_file_mutex(struct client_s *client)
123 pth_mutex_t *m;
125 #ifdef WITH_PINENTRY
126 if (client->has_lock == FALSE || client->pinentry->status != PINENTRY_NONE)
127 #else
128 if (client->has_lock == FALSE)
129 #endif
130 return;
132 CACHE_LOCK(client->ctx);
134 if (cache_get_mutex(client->md5file, &m) == FALSE) {
135 CACHE_UNLOCK;
136 return;
139 CACHE_UNLOCK;
140 pth_mutex_release(m);
141 client->has_lock = FALSE;
144 gpg_error_t lock_file_mutex(struct client_s *client)
146 pth_mutex_t *m;
148 if (client->has_lock == TRUE)
149 return 0;
151 CACHE_LOCK(client->ctx);
153 if (cache_get_mutex(client->md5file, &m) == FALSE) {
154 CACHE_UNLOCK;
155 return 0;
158 CACHE_UNLOCK;
160 if (pth_mutex_acquire(m, TRUE, NULL) == FALSE) {
161 if (errno == EBUSY) {
162 if (client->ctx)
163 assuan_write_status(client->ctx, "LOCKED", N_("Waiting for lock"));
165 pth_mutex_acquire(m, FALSE, NULL);
167 else {
168 gint e = errno;
169 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(errno));
170 return gpg_error_from_errno(e);
174 client->has_lock = TRUE;
175 return 0;
178 void free_client(struct client_s *client)
180 if (client->doc)
181 xmlFreeDoc(client->doc);
183 if (client->xml)
184 gcry_free(client->xml);
186 if (client->filename)
187 g_free(client->filename);
189 if (client->gh)
190 gcry_cipher_close(client->gh);
193 static void cleanup_client(struct client_s *client)
195 assuan_context_t ctx = client->ctx;
196 #ifdef WITH_PINENTRY
197 struct pinentry_s *pin = client->pinentry;
198 #endif
200 unlock_file_mutex(client);
201 CACHE_LOCK(client->ctx);
202 cache_decr_refcount(client->md5file);
205 * This may be a new file so don't use a cache slot. save_command() will
206 * set this to FALSE on success.
208 if (client->new == TRUE)
209 cache_clear(client->md5file, 1);
211 free_client(client);
212 memset(client, 0, sizeof(struct client_s));
213 client->state = STATE_CONNECTED;
214 client->ctx = ctx;
215 client->freed = TRUE;
216 #ifdef WITH_PINENTRY
217 client->pinentry = pin;
218 #endif
219 CACHE_UNLOCK;
222 gboolean do_decompress(assuan_context_t ctx, gpointer in, gint insize,
223 gpointer *out, glong *outsize, gint *error)
225 z_stream z;
226 gint ret;
227 gpointer pout;
228 gz_header h;
229 gchar buf[17];
230 gpg_error_t rc;
231 gchar str[ASSUAN_LINELENGTH];
233 z.zalloc = z_alloc;
234 z.zfree = z_free;
235 z.next_in = in;
236 z.avail_in = insize;
237 z.avail_out = zlib_bufsize;
238 z.next_out = pout = g_malloc(zlib_bufsize);
240 if (!pout) {
241 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
242 *error = Z_MEM_ERROR;
243 return FALSE;
246 ret = inflateInit2(&z, 47);
248 if (ret != Z_OK) {
249 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
250 g_free(pout);
251 return FALSE;
254 memset(&h, 0, sizeof(gz_header));
255 h.comment = (guchar *)buf;
256 h.comm_max = sizeof(buf);
257 ret = inflateGetHeader(&z, &h);
259 if (ret != Z_OK) {
260 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
261 g_free(pout);
262 inflateEnd(&z);
263 return FALSE;
266 ret = inflate(&z, Z_BLOCK);
268 if (ret != Z_OK) {
269 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
270 g_free(pout);
271 inflateEnd(&z);
272 return FALSE;
275 if (h.comment)
276 insize = atoi((gchar *)h.comment);
278 do {
279 gpointer p;
281 ret = inflate(&z, Z_FINISH);
283 switch (ret) {
284 case Z_OK:
285 break;
286 case Z_BUF_ERROR:
287 if (!z.avail_out) {
288 p = g_realloc(pout, z.total_out + zlib_bufsize);
290 if (!p) {
291 ret = Z_MEM_ERROR;
292 goto fail;
295 pout = p;
296 z.next_out = pout + z.total_out;
297 z.avail_out = zlib_bufsize;
299 if (ctx) {
300 rc = assuan_write_status(ctx, "DECOMPRESS",
301 print_fmt(str, sizeof(str), "%i %i", z.total_out, insize));
303 if (rc) {
304 ret = rc;
305 goto fail;
309 break;
310 case Z_STREAM_END:
311 break;
312 default:
313 goto fail;
314 break;
317 pth_yield(NULL);
318 } while (ret != Z_STREAM_END);
320 if (ctx) {
321 rc = assuan_write_status(ctx, "DECOMPRESS",
322 print_fmt(str, sizeof(str), "%i %i", z.total_out, insize));
324 if (rc) {
325 ret = rc;
326 goto fail;
330 *out = pout;
331 *outsize = z.total_out;
332 inflateEnd(&z);
333 return TRUE;
335 fail:
336 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
337 *error = ret;
338 g_free(pout);
339 inflateEnd(&z);
340 return FALSE;
343 gpg_error_t read_file_header(const gchar *filename, file_header_t *fh)
345 gint fd;
346 gsize len;
348 fd = open(filename, O_RDONLY);
350 if (fd == -1)
351 return gpg_error_from_errno(errno);
353 len = pth_read(fd, fh, sizeof(file_header_t));
354 close(fd);
356 if (len != sizeof(file_header_t))
357 return gpg_error_from_errno(errno);
359 return 0;
362 static gpg_error_t open_command_finalize(assuan_context_t ctx, guchar shakey[],
363 gboolean cached)
365 struct client_s *client = assuan_get_pointer(ctx);
366 gpg_error_t error;
367 struct stat st;
368 gint fd;
369 gint timeout;
371 if ((fd = open_file(client->filename, &st)) == -1) {
372 /* New file. */
373 if (errno == ENOENT) {
374 if (shakey[0])
375 goto update_cache;
377 goto done;
380 error = errno;
381 log_write("%s: %s", client->filename, strerror(errno));
382 cleanup_client(client);
383 memset(shakey, 0, sizeof(shakey));
384 return send_syserror(ctx, error);
387 error = try_xml_decrypt(ctx, fd, st, shakey);
388 close(fd);
390 if (error) {
391 memset(shakey, 0, sizeof(shakey));
392 cleanup_client(client);
393 return send_error(ctx, error);
396 update_cache:
397 CACHE_LOCK(client->ctx);
399 if (cached == FALSE) {
400 if (cache_update_key(client->md5file, shakey) == FALSE) {
401 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
402 cleanup_client(client);
403 CACHE_UNLOCK;
404 return send_error(ctx, EPWMD_MAX_SLOTS);
407 timeout = get_key_file_integer(client->filename, "cache_timeout");
408 cache_reset_timeout(client->md5file, timeout);
410 else
411 cache_set_timeout(client->md5file, -2);
413 CACHE_UNLOCK;
415 done:
416 memset(shakey, 0, sizeof(shakey));
417 error = parse_xml(ctx);
419 if (client->xml) {
420 gcry_free(client->xml);
421 client->xml = NULL;
424 if (!error) {
425 if (client->new == FALSE)
426 send_cache_status_all();
428 client->state = STATE_OPEN;
431 return send_error(ctx, error);
434 static int open_command(assuan_context_t ctx, char *line)
436 struct stat st;
437 guchar shakey[gcrykeysize];
438 gboolean cached = FALSE;
439 gpg_error_t error;
440 struct client_s *client = assuan_get_pointer(ctx);
441 gchar **req;
442 gchar *filename = NULL;
443 file_header_t file_header;
445 memset(shakey, 0, sizeof(shakey));
447 if ((req = split_input_line(line, " ", 2)) != NULL)
448 filename = req[0];
450 if (!filename || !*filename) {
451 g_strfreev(req);
452 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
455 if (valid_filename(filename) == FALSE) {
456 g_strfreev(req);
457 return send_error(ctx, EPWMD_INVALID_FILENAME);
460 if (client->state == STATE_OPEN)
461 cleanup_client(client);
463 gcry_md_hash_buffer(GCRY_MD_MD5, client->md5file, filename, strlen(filename));
464 CACHE_LOCK(client->ctx);
466 if (cache_has_file(client->md5file) == FALSE) {
467 if (cache_add_file(client->md5file, NULL) == FALSE) {
468 g_strfreev(req);
469 CACHE_UNLOCK;
470 return send_error(ctx, EPWMD_MAX_SLOTS);
474 cache_incr_refcount(client->md5file);
475 CACHE_UNLOCK;
476 error = lock_file_mutex(client);
478 if (error) {
479 g_strfreev(req);
480 return send_error(ctx, error);
483 client->freed = FALSE;
485 if ((error = gcry_cipher_open(&client->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
486 g_strfreev(req);
487 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
488 cleanup_client(client);
489 return send_error(ctx, error);
492 if (stat(filename, &st) == 0) {
493 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
494 log_write("%s: %s", filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
495 g_strfreev(req);
496 cleanup_client(client);
497 return send_error(ctx, EPWMD_INVALID_FILENAME);
500 client->mtime = st.st_mtime;
503 client->filename = g_strdup(filename);
505 if (!client->filename) {
506 memset(shakey, 0, sizeof(shakey));
507 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
508 cleanup_client(client);
509 g_strfreev(req);
510 return send_syserror(ctx, ENOMEM);
513 #ifdef WITH_PINENTRY
514 client->pinentry->filename = g_strdup(client->filename);
516 if (!client->pinentry->filename) {
517 memset(shakey, 0, sizeof(shakey));
518 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
519 cleanup_client(client);
520 g_strfreev(req);
521 return send_syserror(ctx, ENOMEM);
523 #endif
526 * New files don't need a key.
528 if (access(filename, R_OK|W_OK) != 0) {
529 if (errno != ENOENT) {
530 error = errno;
531 log_write("%s: %s", filename, strerror(errno));
532 g_strfreev(req);
533 cleanup_client(client);
534 return send_syserror(ctx, error);
537 if ((client->xml = new_document()) == NULL) {
538 log_write("%s", strerror(ENOMEM));
539 g_strfreev(req);
540 cleanup_client(client);
541 return send_syserror(ctx, ENOMEM);
544 client->len = xmlStrlen(client->xml);
545 client->new = TRUE;
546 client->filename = g_strdup(filename);
548 if (!client->filename) {
549 g_strfreev(req);
550 cleanup_client(client);
551 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
552 return send_syserror(ctx, ENOMEM);
555 memset(shakey, 0, sizeof(shakey));
557 if (req[1] && *req[1])
558 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
560 g_strfreev(req);
561 return open_command_finalize(ctx, shakey, cached);
564 error = read_file_header(filename, &file_header);
566 if (error) {
567 g_strfreev(req);
568 cleanup_client(client);
569 return send_error(ctx, error);
572 if (file_header.iter == -1)
573 goto done;
575 CACHE_LOCK(client->ctx);
576 cached = cache_get_key(client->md5file, shakey);
577 CACHE_UNLOCK;
579 if (cached == FALSE) {
581 * No key specified and no matching filename found in the cache. Use
582 * pinentry to retrieve the key. Cannot return assuan_process_done()
583 * here otherwise the command will be interrupted. The event loop in
584 * client_thread() will poll the file descriptor waiting for it to
585 * become ready to read a pinentry_key_s which will contain the
586 * entered key or error. It will then call open_command_finalize() to
587 * to finish the command.
589 if (!req[1] || !*req[1]) {
590 #ifdef WITH_PINENTRY
591 if (get_key_file_boolean(filename, "enable_pinentry") == FALSE) {
592 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
593 goto done;
596 g_strfreev(req);
597 client->pinentry->which = PINENTRY_OPEN;
598 error = pinentry_fork(ctx);
600 if (error) {
601 cleanup_client(client);
602 return send_error(ctx, error);
605 client->pinentry->cb = open_command_finalize;
606 client->pinentry->status = PINENTRY_INIT;
607 return 0;
608 #else
609 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
610 goto done;
611 #endif
614 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, req[1], strlen(req[1]));
617 done:
618 g_strfreev(req);
619 return open_command_finalize(ctx, shakey, cached);
622 gboolean do_compress(assuan_context_t ctx, gint level, gpointer data,
623 gint size, gpointer *out, glong *outsize, gint *error)
625 z_stream z;
626 gpointer pout, pin;
627 gint ret;
628 gz_header h;
629 gchar buf[17];
630 gint cmd = Z_NO_FLUSH;
631 gpg_error_t rc;
632 gchar str[ASSUAN_LINELENGTH];
634 z.zalloc = z_alloc;
635 z.zfree = z_free;
636 z.next_in = pin = data;
637 z.avail_in = size < zlib_bufsize ? size : zlib_bufsize;
638 z.avail_out = zlib_bufsize;
639 z.next_out = pout = g_malloc(zlib_bufsize);
641 if (!pout) {
642 log_write("%s(%i): %s", __FUNCTION__, __LINE__, strerror(ENOMEM));
643 *error = Z_MEM_ERROR;
644 return FALSE;
647 ret = deflateInit2(&z, level, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY);
649 if (ret != Z_OK) {
650 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
651 *error = ret;
652 g_free(pout);
653 return FALSE;
656 memset(&h, 0, sizeof(gz_header));
657 snprintf(buf, sizeof(buf), "%i", size);
658 h.comment = (guchar *)buf;
659 ret = deflateSetHeader(&z, &h);
661 if (ret != Z_OK) {
662 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
663 *error = ret;
664 g_free(pout);
665 deflateEnd(&z);
666 return FALSE;
669 do {
670 gpointer p;
672 ret = deflate(&z, cmd);
674 switch (ret) {
675 case Z_OK:
676 break;
677 case Z_BUF_ERROR:
678 if (!z.avail_out) {
679 p = g_realloc(pout, z.total_out + zlib_bufsize);
681 if (!p) {
682 ret = Z_MEM_ERROR;
683 goto fail;
686 pout = p;
687 z.next_out = pout + z.total_out;
688 z.avail_out = zlib_bufsize;
691 if (!z.avail_in && z.total_in < size) {
692 if (z.total_in + zlib_bufsize > size)
693 z.avail_in = size - z.total_in;
694 else
695 z.avail_in = zlib_bufsize;
697 if (ctx) {
698 rc = assuan_write_status(ctx, "COMPRESS",
699 print_fmt(str, sizeof(str), "%i %i", z.total_in, size));
701 if (rc) {
702 ret = rc;
703 goto fail;
708 if (z.total_in >= size)
709 cmd = Z_FINISH;
711 break;
712 case Z_STREAM_END:
713 break;
714 default:
715 goto fail;
718 pth_yield(NULL);
719 } while (ret != Z_STREAM_END);
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 ret = rc;
727 goto fail;
731 *out = pout;
732 *outsize = z.total_out;
733 deflateEnd(&z);
734 return TRUE;
736 fail:
737 log_write("%s(%i): %s", __FUNCTION__, __LINE__, z.msg);
738 *error = ret;
739 g_free(pout);
740 deflateEnd(&z);
741 return FALSE;
744 gpg_error_t do_xml_encrypt(struct client_s *client, gcry_cipher_hd_t gh,
745 const gchar *filename, gpointer data, size_t insize, guchar *shakey,
746 gint iter)
748 gsize len = insize;
749 gint fd;
750 gpointer inbuf;
751 guchar tkey[gcrykeysize];
752 gchar *p;
753 gint error;
754 gpg_error_t rc;
755 guint iter_progress = 0, n_iter = 0, xiter = 0;
756 gchar tmp[FILENAME_MAX];
757 file_header_t file_header;
758 gchar str[ASSUAN_LINELENGTH];
760 if (iter == -1) {
762 * cache_file_count() needs both .used == TRUE and a valid key in
763 * order for it to count as a used cache entry. Fixes CACHE status
764 * messages.
766 memset(shakey, '!', gcrykeysize);
767 inbuf = data;
768 file_header.iter = iter;
769 goto write_file;
772 if (insize / gcryblocksize) {
773 len = (insize / gcryblocksize) * gcryblocksize;
775 if (insize % gcryblocksize)
776 len += gcryblocksize;
780 * Resize the existing xml buffer to the block size required by gcrypt
781 * rather than duplicating it and wasting memory.
783 inbuf = gcry_realloc(data, len);
785 if (!inbuf)
786 return gpg_error_from_errno(ENOMEM);
788 insize = len;
789 gcry_create_nonce(file_header.iv, sizeof(file_header.iv));
790 memcpy(tkey, shakey, sizeof(tkey));
791 tkey[0] ^= 1;
793 if ((rc = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
794 gcry_free(inbuf);
795 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
796 return rc;
799 file_header.iter = iter;
801 if (client)
802 iter_progress = get_key_file_integer("global", "iteration_progress");
804 if (client && iter_progress && file_header.iter >= iter_progress) {
805 error = assuan_write_status(client->ctx, "ENCRYPT", "0");
807 if (error) {
808 gcry_free(inbuf);
809 return error;
813 while (xiter < file_header.iter) {
814 if (client && iter_progress > 0 && xiter >= iter_progress) {
815 if (!(xiter % iter_progress)) {
816 error = assuan_write_status(client->ctx, "ENCRYPT",
817 print_fmt(str, sizeof(str), "%i", ++n_iter * iter_progress));
819 if (error) {
820 gcry_free(inbuf);
821 return error;
826 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
827 sizeof(file_header.iv)))) {
828 gcry_free(inbuf);
829 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
830 return rc;
833 if (encrypt_xml(gh, inbuf, insize, NULL, 0)
834 == FALSE) {
835 gcry_free(inbuf);
836 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
837 return rc;
840 xiter++;
841 pth_yield(NULL);
844 if ((rc = gcry_cipher_setiv(gh, file_header.iv,
845 sizeof(file_header.iv)))) {
846 gcry_free(inbuf);
847 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
848 return rc;
851 if ((rc = gcry_cipher_setkey(gh, shakey, gcrykeysize))) {
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) == FALSE) {
858 gcry_free(inbuf);
859 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
860 return rc;
863 if (client && iter_progress && file_header.iter >= iter_progress) {
864 error = assuan_write_status(client->ctx, "ENCRYPT",
865 print_fmt(str, sizeof(str), "%i", file_header.iter));
867 if (error) {
868 gcry_free(inbuf);
869 return error;
873 write_file:
874 if (filename) {
875 g_snprintf(tmp, sizeof(tmp), ".%s.tmp", filename);
877 if ((fd = open(tmp, O_WRONLY|O_CREAT|O_TRUNC, 0600)) == -1) {
878 error = errno;
879 gcry_free(inbuf);
880 p = strrchr(tmp, '/');
881 p++;
882 log_write("%s: %s", p, strerror(errno));
883 return gpg_error_from_errno(error);
886 else
888 * xml_import() from command line.
890 fd = STDOUT_FILENO;
892 len = pth_write(fd, &file_header, sizeof(file_header_t));
894 if (len != sizeof(file_header)) {
895 len = errno;
897 if (filename)
898 close(fd);
900 gcry_free(inbuf);
901 return gpg_error_from_errno(len);
904 len = pth_write(fd, inbuf, insize);
906 if (len != insize) {
907 len = errno;
909 if (filename)
910 close(fd);
912 gcry_free(inbuf);
913 return gpg_error_from_errno(len);
916 if (fsync(fd) == -1) {
917 len = errno;
918 close(fd);
919 gcry_free(inbuf);
920 return gpg_error_from_errno(len);
923 if (filename) {
924 struct stat st;
925 mode_t mode = 0;
927 close(fd);
929 if (stat(filename, &st) == 0)
930 mode = st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
932 if (rename(tmp, filename) == -1) {
933 len = errno;
934 gcry_free(inbuf);
935 return gpg_error_from_errno(len);
938 if (mode)
939 chmod(filename, mode);
942 gcry_free(inbuf);
943 return 0;
946 static gpg_error_t save_command_finalize(assuan_context_t ctx,
947 guchar shakey[], gboolean cached)
949 struct client_s *client = assuan_get_pointer(ctx);
950 gpointer xmlbuf;
951 xmlChar *p;
952 gint len;
953 gint iter;
954 gint timeout;
955 gpointer outbuf;
956 glong outsize = 0;
957 gint zerror;
958 gpg_error_t error;
959 struct stat st;
961 xmlDocDumpFormatMemory(client->doc, &p, &len, 0);
962 xmlbuf = p;
964 iter = get_key_file_integer(client->filename, "compression_level");
966 if (iter < 0)
967 iter = 0;
969 if (do_compress(ctx, iter, xmlbuf, len, &outbuf, &outsize, &zerror) == FALSE) {
970 memset(shakey, 0, sizeof(shakey));
971 xmlFree(xmlbuf);
973 if (zerror == Z_MEM_ERROR) {
974 return send_syserror(ctx, ENOMEM);
976 else
977 return send_error(ctx, GPG_ERR_COMPR_ALGO);
979 else {
980 gcry_free(xmlbuf);
981 xmlbuf = outbuf;
982 len = outsize;
985 iter = get_key_file_integer(client->filename, "iterations");
986 error = do_xml_encrypt(client, client->gh, client->filename, xmlbuf, len, shakey, iter);
988 if (error) {
989 memset(shakey, 0, sizeof(shakey));
990 return send_error(ctx, error);
993 stat(client->filename, &st);
994 client->mtime = st.st_mtime;
995 timeout = get_key_file_integer(client->filename, "cache_timeout");
996 CACHE_LOCK(client->ctx);
998 if (cached) {
999 memset(shakey, 0, sizeof(shakey));
1000 cache_reset_timeout(client->md5file, timeout);
1001 CACHE_UNLOCK;
1003 if (client->new == TRUE)
1004 send_cache_status_all();
1006 client->new = FALSE;
1007 return send_error(ctx, 0);
1010 if (cache_update_key(client->md5file, shakey) == FALSE) {
1011 memset(shakey, 0, sizeof(shakey));
1012 log_write("%s(%i): %s", __FILE__, __LINE__, pwmd_strerror(EPWMD_MAX_SLOTS));
1013 CACHE_UNLOCK;
1014 return send_error(ctx, EPWMD_MAX_SLOTS);
1017 client->new = FALSE;
1018 memset(shakey, 0, sizeof(shakey));
1019 cache_reset_timeout(client->md5file, timeout);
1020 CACHE_UNLOCK;
1021 send_cache_status_all();
1022 return send_error(ctx, 0);
1025 static int save_command(assuan_context_t ctx, char *line)
1027 gboolean cached = FALSE;
1028 guchar shakey[gcrykeysize];
1029 struct stat st;
1030 struct client_s *client = assuan_get_pointer(ctx);
1031 gpg_error_t error;
1033 memset(shakey, 0, sizeof(shakey));
1034 error = file_modified(client);
1036 if (error) {
1037 log_write("%s: %s", client->filename ? client->filename : "",
1038 pwmd_strerror(error));
1039 return send_error(ctx, error);
1042 error = lock_file_mutex(client);
1044 if (error)
1045 return send_error(ctx, error);
1047 if (stat(client->filename, &st) == -1 && errno != ENOENT)
1048 return send_syserror(ctx, errno);
1050 if (errno != ENOENT && !S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
1051 log_write("%s: %s", client->filename, pwmd_strerror(EPWMD_INVALID_FILENAME));
1052 return send_error(ctx, EPWMD_INVALID_FILENAME);
1055 if (get_key_file_integer(client->filename, "iterations") == -1)
1056 goto done;
1058 if (!line || !*line) {
1059 guchar tmp[sizeof(shakey)];
1060 CACHE_LOCK(ctx);
1062 memset(tmp, '!', sizeof(tmp));
1064 if (cache_get_key(client->md5file, shakey) == FALSE ||
1065 memcmp(shakey, tmp, sizeof(shakey)) == 0) {
1066 CACHE_UNLOCK;
1067 #ifdef WITH_PINENTRY
1068 if (get_key_file_boolean(client->filename, "enable_pinentry") == FALSE) {
1069 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1070 goto done;
1073 client->pinentry->which = PINENTRY_SAVE;
1074 error = pinentry_fork(ctx);
1076 if (error)
1077 return send_error(ctx, error);
1079 client->pinentry->cb = save_command_finalize;
1080 client->pinentry->status = PINENTRY_INIT;
1081 return 0;
1082 #else
1083 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, "", 1);
1084 goto done;
1085 #endif
1087 else {
1088 CACHE_UNLOCK;
1089 cached = TRUE;
1092 else {
1093 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, line, strlen(line));
1094 memset(line, 0, strlen(line));
1097 done:
1098 return save_command_finalize(ctx, shakey, cached);
1101 static int delete_command(assuan_context_t ctx, char *line)
1103 struct client_s *client = assuan_get_pointer(ctx);
1104 gchar **req;
1105 gpg_error_t error;
1106 xmlNodePtr n;
1108 error = file_modified(client);
1110 if (error) {
1111 log_write("%s: %s", client->filename ? client->filename : "",
1112 pwmd_strerror(error));
1113 return send_error(ctx, error);
1116 if (strchr(line, '\t'))
1117 req = split_input_line(line, "\t", -1);
1118 else
1119 req = split_input_line(line, " ", -1);
1121 if (!req || !*req)
1122 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1124 n = find_account(client->doc, &req, &error, NULL, 0);
1126 if (!n) {
1127 g_strfreev(req);
1128 return send_error(ctx, error);
1132 * No sub-node defined. Remove the entire node (account).
1134 if (!req[1]) {
1135 if (n) {
1136 xmlUnlinkNode(n);
1137 xmlFreeNode(n);
1140 g_strfreev(req);
1141 return send_error(ctx, 0);
1144 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1145 g_strfreev(req);
1147 if (!n)
1148 return send_error(ctx, error);
1150 if (n) {
1151 xmlUnlinkNode(n);
1152 xmlFreeNode(n);
1155 return send_error(ctx, 0);
1159 * Don't return with assuan_process_done() here. This has been called from
1160 * assuan_process_next() and the command should be finished in
1161 * client_thread().
1163 static int store_command_finalize(gpointer data, gint rc, guchar *line,
1164 gsize len)
1166 assuan_context_t ctx = data;
1167 struct client_s *client = assuan_get_pointer(ctx);
1168 gchar **req;
1169 guchar *result = line;
1170 xmlNodePtr n;
1171 gpg_error_t error = file_modified(client);
1173 if (rc) {
1174 if (line)
1175 #ifndef MEM_DEBUG
1176 xfree(line);
1177 #else
1178 free(line);
1179 #endif
1180 return rc;
1183 req = split_input_line((gchar *)result, "\t", 0);
1184 #ifndef MEM_DEBUG
1185 xfree(line);
1186 #else
1187 free(line);
1188 #endif
1190 if (rc) {
1191 if (req)
1192 g_strfreev(req);
1194 return rc;
1197 if (!req || !*req)
1198 return EPWMD_COMMAND_SYNTAX;
1200 if (valid_xml_element((xmlChar *)*req) == FALSE) {
1201 g_strfreev(req);
1202 return EPWMD_INVALID_ELEMENT;
1205 if (valid_element_path(req+1, TRUE) == FALSE) {
1206 g_strfreev(req);
1207 return EPWMD_INVALID_ELEMENT;
1210 again:
1211 n = find_account(client->doc, &req, &error, NULL, 0);
1213 if (error && error == EPWMD_ELEMENT_NOT_FOUND) {
1214 error = new_account(client->doc, *req);
1216 if (error) {
1217 g_strfreev(req);
1218 return error;
1221 goto again;
1224 if (!n) {
1225 g_strfreev(req);
1226 return error;
1229 if (req[1]) {
1230 if (!n->children)
1231 create_elements_cb(n, req+1, &error, NULL);
1232 else
1233 find_elements(client->doc, n->children, req+1, &error,
1234 NULL, NULL, create_elements_cb, FALSE, 0, NULL);
1237 g_strfreev(req);
1238 client->inquire_status = INQUIRE_DONE;
1239 return error;
1242 static int store_command(assuan_context_t ctx, char *line)
1244 struct client_s *client = assuan_get_pointer(ctx);
1245 gpg_error_t error = file_modified(client);
1247 if (error) {
1248 log_write("%s: %s", client->filename ? client->filename : "",
1249 pwmd_strerror(error));
1250 return send_error(ctx, error);
1253 error = assuan_inquire_ext(ctx, "STORE", 0, store_command_finalize, ctx);
1255 if (error)
1256 return send_error(ctx, error);
1258 /* Don't return with assuan_process_done() here. This is an INQUIRE. */
1259 client->inquire_status = INQUIRE_BUSY;
1260 return 0;
1263 static int get_command(assuan_context_t ctx, char *line)
1265 struct client_s *client = assuan_get_pointer(ctx);
1266 gchar **req;
1267 gpg_error_t error;
1268 xmlNodePtr n;
1270 error = file_modified(client);
1272 if (error) {
1273 log_write("%s: %s", client->filename ? client->filename : "",
1274 pwmd_strerror(error));
1275 return send_error(ctx, error);
1278 req = split_input_line(line, "\t", -1);
1280 if (!req || !*req) {
1281 g_strfreev(req);
1282 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1285 n = find_account(client->doc, &req, &error, NULL, 0);
1287 if (!n) {
1288 g_strfreev(req);
1289 return send_error(ctx, error);
1292 if (req[1])
1293 n = find_elements(client->doc, n->children, req+1, &error, NULL, NULL, NULL, FALSE, 0, NULL);
1295 g_strfreev(req);
1297 if (error)
1298 return send_error(ctx, error);
1300 if (!n || !n->children)
1301 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1303 n = find_text_node(n->children);
1305 if (!n || !n->content || !*n->content)
1306 return send_error(ctx, EPWMD_EMPTY_ELEMENT);
1308 error = assuan_send_data(ctx, n->content, xmlStrlen(n->content));
1309 return send_error(ctx, error);
1312 static xmlNodePtr realpath_elements_cb(xmlNodePtr node, gchar **req,
1313 gpg_error_t *error, void *data)
1315 gchar *account = *(gchar **)data;
1317 if (account)
1318 g_free(account);
1320 account = g_strdup(req[0]);
1322 if (!account) {
1323 *error = gpg_error_from_errno(ENOMEM);
1324 *(gchar **)data = NULL;
1325 return NULL;
1328 *(gchar **)data = account;
1329 return node;
1332 static int realpath_command(assuan_context_t ctx, char *line)
1334 gpg_error_t error;
1335 struct client_s *client = assuan_get_pointer(ctx);
1336 xmlChar *p;
1337 gchar **req;
1338 gchar *result, *t;
1339 gint i;
1340 xmlNodePtr n;
1341 GString *string;
1342 gchar *rp_account = NULL;
1344 error = file_modified(client);
1346 if (error) {
1347 log_write("%s: %s", client->filename ? client->filename : "",
1348 pwmd_strerror(error));
1349 return send_error(ctx, error);
1352 if (strchr(line, '\t') != NULL) {
1353 if ((req = split_input_line(line, "\t", 0)) == NULL)
1354 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1356 else {
1357 if ((req = split_input_line(line, " ", 0)) == NULL)
1358 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1361 n = find_account(client->doc, &req, &error, NULL, 0);
1363 if (!n) {
1364 g_strfreev(req);
1365 return send_error(ctx, error);
1368 rp_account = g_strdup(req[0]);
1370 if (!rp_account) {
1371 g_strfreev(req);
1372 return send_syserror(ctx, ENOMEM);
1375 if (req[1]) {
1376 n = find_elements(client->doc, n->children, req+1, &error,
1377 NULL, realpath_elements_cb, NULL, FALSE, 0, &rp_account);
1379 if (!n) {
1380 g_free(rp_account);
1381 g_strfreev(req);
1382 return send_error(ctx, error);
1386 p = xmlGetNodePath(n);
1387 result = element_path_to_req(rp_account, p);
1389 if (!result) {
1390 g_free(result);
1391 g_free(rp_account);
1392 g_strfreev(req);
1393 xmlFree(p);
1394 return send_syserror(ctx, ENOMEM);
1397 string = g_string_new(result);
1398 g_free(result);
1399 g_free(rp_account);
1400 g_strfreev(req);
1401 xmlFree(p);
1402 i = 0;
1404 again:
1405 for (t = string->str + i; *t; t++, i++) {
1406 if ((!i && *t != '!') || *t == '\t') {
1407 string = g_string_insert_c(string, !i ? i++ : ++i, '!');
1408 goto again;
1412 error = assuan_send_data(ctx, string->str, string->len);
1413 g_string_free(string, TRUE);
1414 return send_error(ctx, error);
1417 static int list_command(assuan_context_t ctx, char *line)
1419 struct client_s *client = assuan_get_pointer(ctx);
1420 gpg_error_t rc;
1421 struct element_list_s *elements = NULL;
1422 gchar *tmp;
1424 if (disable_list_and_dump == TRUE)
1425 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
1427 rc = file_modified(client);
1429 if (rc) {
1430 log_write("%s: %s", client->filename, pwmd_strerror(rc));
1431 return send_error(ctx, rc);
1434 if (!*line) {
1435 GString *str;
1437 rc = list_accounts(client->doc, &str);
1439 if (rc)
1440 return send_error(ctx, rc);
1442 rc = assuan_send_data(ctx, str->str, str->len);
1443 g_string_free(str, TRUE);
1444 return send_error(ctx, rc);
1447 elements = g_malloc0(sizeof(struct element_list_s));
1449 if (!elements) {
1450 rc = gpg_err_code_from_errno(ENOMEM);
1451 goto fail;
1454 rc = create_path_list(client->doc, elements, line);
1456 if (rc)
1457 goto fail;
1459 if (elements) {
1460 gint total = g_slist_length(elements->list);
1461 gint i;
1462 GString *str;
1464 if (!total) {
1465 rc = EPWMD_EMPTY_ELEMENT;
1466 goto fail;
1469 str = g_string_new(NULL);
1471 if (!str) {
1472 rc = gpg_err_code_from_errno(ENOMEM);
1473 goto fail;
1476 for (i = 0; i < total; i++) {
1477 tmp = g_slist_nth_data(elements->list, i);
1478 g_string_append_printf(str, "%s%s", tmp, i+1 == total ? "" : "\n");
1481 rc = assuan_send_data(ctx, str->str, str->len);
1482 g_string_free(str, TRUE);
1484 else
1485 rc = EPWMD_EMPTY_ELEMENT;
1487 fail:
1488 if (elements) {
1489 gint total = g_slist_length(elements->list);
1490 gint i;
1492 for (i = 0; i < total; i++) {
1493 tmp = g_slist_nth_data(elements->list, i);
1494 g_free(tmp);
1497 g_slist_free(elements->list);
1499 if (elements->prefix)
1500 g_free(elements->prefix);
1502 g_free(elements);
1505 return send_error(ctx, rc);
1508 static gpg_error_t add_attribute(xmlNodePtr node, const gchar *name,
1509 const gchar *value)
1511 xmlAttrPtr a;
1513 if ((a = xmlHasProp(node, (xmlChar *)name)) == NULL) {
1514 a = xmlNewProp(node, (xmlChar *)name, (xmlChar *)value);
1516 if (!a)
1517 return EPWMD_LIBXML_ERROR;
1519 else
1520 xmlNodeSetContent(a->children, (xmlChar *)value);
1522 return 0;
1526 * req[0] - element path
1528 static int attribute_list(assuan_context_t ctx, gchar **req)
1530 struct client_s *client = assuan_get_pointer(ctx);
1531 gchar **attrlist = NULL;
1532 gint i = 0;
1533 gchar **path = NULL;
1534 xmlAttrPtr a;
1535 xmlNodePtr n, an;
1536 gchar *line;
1537 gpg_error_t error;
1539 if (!req || !req[0])
1540 return EPWMD_COMMAND_SYNTAX;
1542 if ((path = split_input_line(req[0], "\t", 0)) == NULL) {
1544 * The first argument may be only an account.
1546 if ((path = split_input_line(req[0], " ", 0)) == NULL)
1547 return EPWMD_COMMAND_SYNTAX;
1550 n = find_account(client->doc, &path, &error, NULL, 0);
1552 if (!n) {
1553 g_strfreev(path);
1554 return error;
1557 if (path[1]) {
1558 n = find_elements(client->doc, n->children, path+1, &error,
1559 NULL, NULL, NULL, FALSE, 0, NULL);
1561 if (!n) {
1562 g_strfreev(path);
1563 return error;
1567 g_strfreev(path);
1569 for (a = n->properties; a; a = a->next) {
1570 gchar **pa;
1572 if ((pa = g_realloc(attrlist, (i + 2) * sizeof(gchar *))) == NULL) {
1573 if (attrlist)
1574 g_strfreev(attrlist);
1576 error = errno;
1577 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(errno));
1578 return gpg_error_from_errno(error);
1581 attrlist = pa;
1582 an = a->children;
1583 attrlist[i] = g_strdup_printf("%s\t%s", (gchar *)a->name, (gchar *)an->content);
1585 if (!attrlist[i]) {
1586 g_strfreev(attrlist);
1587 return gpg_error_from_errno(ENOMEM);
1590 attrlist[++i] = NULL;
1593 if (!attrlist)
1594 return EPWMD_EMPTY_ELEMENT;
1596 line = g_strjoinv("\n", attrlist);
1598 if (!line) {
1599 g_strfreev(attrlist);
1600 return gpg_error_from_errno(ENOMEM);
1603 error = assuan_send_data(ctx, line, strlen(line));
1604 g_free(line);
1605 g_strfreev(attrlist);
1606 return error;
1610 * req[0] - attribute
1611 * req[1] - element path
1613 static int attribute_delete(struct client_s *client, gchar **req)
1615 xmlAttrPtr a;
1616 xmlNodePtr n;
1617 gchar **path = NULL;
1618 gpg_error_t error;
1620 if (!req || !req[0] || !req[1])
1621 return EPWMD_COMMAND_SYNTAX;
1623 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1625 * The first argument may be only an account.
1627 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1628 return EPWMD_COMMAND_SYNTAX;
1632 * Don't remove the "name" attribute for the account element. To remove an
1633 * account use DELETE <account>.
1635 if (!path[1] && xmlStrEqual((xmlChar *)req[0], (xmlChar *)"name")) {
1636 error = EPWMD_ATTR_SYNTAX;
1637 goto fail;
1640 n = find_account(client->doc, &path, &error, NULL, 0);
1642 if (!n)
1643 goto fail;
1645 if (path[1]) {
1646 n = find_elements(client->doc, n->children, path+1, &error,
1647 NULL, NULL, NULL, FALSE, 0, NULL);
1649 if (!n)
1650 goto fail;
1653 g_strfreev(path);
1655 if ((a = xmlHasProp(n, (xmlChar *)req[0])) == NULL)
1656 return EPWMD_ATTR_NOT_FOUND;
1658 if (xmlRemoveProp(a) == -1)
1659 return EPWMD_LIBXML_ERROR;
1661 return 0;
1663 fail:
1664 g_strfreev(path);
1665 return error;
1669 * Creates a "target" attribute. When other commands encounter an element with
1670 * this attribute, the element path is modified to the target value. If the
1671 * source element path doesn't exist when using 'ATTR SET target', it is
1672 * created, but the destination element path must exist.
1674 * req[0] - source element path
1675 * req[1] - destination element path
1677 static gpg_error_t target_attribute(struct client_s *client, gchar **req)
1679 gchar **src, **dst, *line = NULL;
1680 gpg_error_t error;
1681 xmlNodePtr n;
1682 gchar **src_orig = NULL;
1684 if (!req || !req[0] || !req[1])
1685 return EPWMD_COMMAND_SYNTAX;
1687 if ((src = split_input_line(req[0], "\t", 0)) == NULL) {
1689 * The first argument may be only an account.
1691 if ((src = split_input_line(req[0], " ", 0)) == NULL)
1692 return EPWMD_COMMAND_SYNTAX;
1695 if (valid_element_path(src, FALSE) == FALSE) {
1696 g_strfreev(src);
1697 return EPWMD_INVALID_ELEMENT;
1700 if ((dst = split_input_line(req[1], "\t", 0)) == NULL) {
1702 * The first argument may be only an account.
1704 if ((dst = split_input_line(req[1], " ", 0)) == NULL) {
1705 error = EPWMD_COMMAND_SYNTAX;
1706 goto fail;
1710 n = find_account(client->doc, &dst, &error, NULL, 0);
1713 * Make sure the destination element path exists.
1715 if (!n)
1716 goto fail;
1718 if (dst[1]) {
1719 n = find_elements(client->doc, n->children, dst+1, &error,
1720 NULL, NULL, NULL, FALSE, 0, NULL);
1722 if (!n)
1723 goto fail;
1726 again:
1727 src_orig = g_strdupv(src);
1728 n = find_account(client->doc, &src, &error, NULL, 0);
1730 if (!n) {
1731 if (error == EPWMD_ELEMENT_NOT_FOUND) {
1732 error = new_account(client->doc, src[0]);
1734 if (error)
1735 goto fail;
1737 goto again;
1739 else
1740 goto fail;
1743 if (src[1]) {
1744 if (!n->children)
1745 n = create_target_elements_cb(n, src+1, &error, NULL);
1746 else
1747 n = find_elements(client->doc, n->children, src+1, &error,
1748 NULL, NULL, create_target_elements_cb, FALSE, 0, NULL);
1750 if (!n)
1751 goto fail;
1754 * Reset the position of the element tree now that the elements
1755 * have been created.
1757 g_strfreev(src);
1758 src = src_orig;
1759 src_orig = NULL;
1760 n = find_account(client->doc, &src, &error, NULL, 0);
1762 if (!n)
1763 goto fail;
1765 n = find_elements(client->doc, n->children, src+1, &error,
1766 NULL, NULL, NULL, FALSE, 0, NULL);
1768 if (!n)
1769 goto fail;
1772 line = g_strjoinv("\t", dst);
1773 error = add_attribute(n, "target", line);
1775 fail:
1776 g_free(line);
1777 g_strfreev(src);
1778 g_strfreev(dst);
1779 g_strfreev(src_orig);
1780 return error;
1784 * req[0] - account name
1785 * req[1] - new name
1787 static gpg_error_t name_attribute(struct client_s *client, gchar **req)
1789 gpg_error_t error;
1790 gchar **tmp;
1791 xmlNodePtr n;
1793 tmp = g_strdupv(req);
1795 if (!tmp)
1796 return gpg_error_from_errno(ENOMEM);
1798 n = find_account(client->doc, &tmp, &error, NULL, 0);
1799 g_strfreev(tmp);
1801 if (!n)
1802 return error;
1804 if (g_utf8_collate(req[0], req[1]) == 0)
1805 return 0;
1808 * Will not overwrite an existing account.
1810 tmp = g_strdupv(req+1);
1812 if (!tmp)
1813 return gpg_error_from_errno(ENOMEM);
1815 n = find_account(client->doc, &tmp, &error, NULL, 0);
1816 g_strfreev(tmp);
1818 if (error && error != EPWMD_ELEMENT_NOT_FOUND)
1819 return error;
1821 if (n)
1822 return EPWMD_ACCOUNT_EXISTS;
1825 * Whitespace not allowed in account names.
1827 if (contains_whitespace(req[1]) == TRUE)
1828 return EPWMD_ATTR_SYNTAX;
1830 tmp = g_strdupv(req);
1832 if (!tmp)
1833 return gpg_error_from_errno(ENOMEM);
1835 n = find_account(client->doc, &tmp, &error, NULL, 0);
1836 g_strfreev(tmp);
1838 if (!n)
1839 return EPWMD_ELEMENT_NOT_FOUND;
1841 return add_attribute(n, "name", req[1]);
1845 * req[0] - attribute
1846 * req[1] - element path
1848 static int attribute_get(assuan_context_t ctx, gchar **req)
1850 struct client_s *client = assuan_get_pointer(ctx);
1851 xmlNodePtr n;
1852 xmlChar *a;
1853 gchar **path= NULL;
1854 gpg_error_t error;
1856 if (!req || !req[0] || !req[1])
1857 return EPWMD_COMMAND_SYNTAX;
1859 if (strchr(req[1], '\t')) {
1860 if ((path = split_input_line(req[1], "\t", 0)) == NULL)
1861 return EPWMD_COMMAND_SYNTAX;
1863 else {
1864 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1865 return EPWMD_COMMAND_SYNTAX;
1868 n = find_account(client->doc, &path, &error, NULL, 0);
1870 if (!n)
1871 goto fail;
1873 if (path[1]) {
1874 n = find_elements(client->doc, n->children, path+1, &error,
1875 NULL, NULL, NULL, FALSE, 0, NULL);
1877 if (!n)
1878 goto fail;
1881 g_strfreev(path);
1883 if ((a = xmlGetProp(n, (xmlChar *)req[0])) == NULL)
1884 return EPWMD_ATTR_NOT_FOUND;
1886 error = assuan_send_data(ctx, a, xmlStrlen(a));
1887 xmlFree(a);
1888 return error;
1890 fail:
1891 g_strfreev(path);
1892 return error;
1896 * req[0] - attribute
1897 * req[1] - element path
1898 * req[2] - value
1900 static int attribute_set(struct client_s *client, gchar **req)
1902 gchar **path = NULL;
1903 gpg_error_t error;
1904 xmlNodePtr n;
1906 if (!req || !req[0] || !req[1] || !req[2])
1907 return EPWMD_COMMAND_SYNTAX;
1910 * Reserved attribute names.
1912 if (g_utf8_collate(req[0], "name") == 0) {
1914 * Only reserved for the account element. Not the rest of the
1915 * document.
1917 if (strchr(req[1], '\t') == NULL)
1918 return name_attribute(client, req + 1);
1920 else if (g_utf8_collate(req[0], "target") == 0)
1921 return target_attribute(client, req + 1);
1923 if ((path = split_input_line(req[1], "\t", 0)) == NULL) {
1925 * The first argument may be only an account.
1927 if ((path = split_input_line(req[1], " ", 0)) == NULL)
1928 return EPWMD_COMMAND_SYNTAX;
1931 n = find_account(client->doc, &path, &error, NULL, 0);
1933 if (!n)
1934 goto fail;
1936 if (path[1]) {
1937 n = find_elements(client->doc, n->children, path+1, &error,
1938 NULL, NULL, NULL, FALSE, 0, NULL);
1940 if (!n)
1941 goto fail;
1944 g_strfreev(path);
1945 return add_attribute(n, req[0], req[2]);
1947 fail:
1948 g_strfreev(path);
1949 return error;
1953 * req[0] - command
1954 * req[1] - attribute name or element path if command is LIST
1955 * req[2] - element path
1956 * req[2] - element path or value
1958 static int attr_command(assuan_context_t ctx, char *line)
1960 struct client_s *client = assuan_get_pointer(ctx);
1961 gchar **req = split_input_line(line, " ", 4);
1962 gpg_error_t error = 0;
1964 error = file_modified(client);
1966 if (error) {
1967 log_write("%s: %s", client->filename ? client->filename : "",
1968 pwmd_strerror(error));
1969 g_strfreev(req);
1970 return send_error(ctx, error);
1973 if (!req || !req[0] || !req[1]) {
1974 g_strfreev(req);
1975 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
1978 if (g_ascii_strcasecmp(req[0], "SET") == 0)
1979 error = attribute_set(client, req+1);
1980 else if (g_ascii_strcasecmp(req[0], "GET") == 0)
1981 error = attribute_get(ctx, req+1);
1982 else if (g_ascii_strcasecmp(req[0], "DELETE") == 0)
1983 error = attribute_delete(client, req+1);
1984 else if (g_ascii_strcasecmp(req[0], "LIST") == 0)
1985 error = attribute_list(ctx, req+1);
1986 else
1987 error = EPWMD_COMMAND_SYNTAX;
1989 g_strfreev(req);
1990 return send_error(ctx, error);
1993 static int iscached_command(assuan_context_t ctx, char *line)
1995 gchar **req = split_input_line(line, " ", 0);
1996 guchar md5file[16];
1998 if (!req || !*req) {
1999 g_strfreev(req);
2000 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2003 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2004 g_strfreev(req);
2005 CACHE_LOCK(ctx);
2007 if (cache_iscached(md5file) == FALSE) {
2008 CACHE_UNLOCK;
2009 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2012 CACHE_UNLOCK;
2013 return send_error(ctx, 0);
2016 gpg_error_t send_cache_status(assuan_context_t ctx)
2018 gchar *tmp;
2019 struct client_s *client = assuan_get_pointer(ctx);
2020 gchar buf[ASSUAN_LINELENGTH];
2022 CACHE_LOCK(client->ctx);
2023 tmp = print_fmt(buf, sizeof(buf), "%i %i",
2024 cache_file_count(),
2025 (cache_size / sizeof(file_cache_t)) - cache_file_count());
2026 CACHE_UNLOCK;
2028 return assuan_write_status(ctx, "CACHE", buf);
2031 static int clearcache_command(assuan_context_t ctx, char *line)
2033 struct client_s *client = assuan_get_pointer(ctx);
2034 gchar **req = split_input_line(line, " ", 0);
2035 guchar md5file[16];
2037 CACHE_LOCK(ctx);
2039 if (!req || !*req) {
2040 g_strfreev(req);
2041 cache_clear(client->md5file, 2);
2042 CACHE_UNLOCK;
2043 return send_error(ctx, 0);
2046 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[0], strlen(req[0]));
2047 g_strfreev(req);
2049 if (cache_clear(md5file, 1) == FALSE) {
2050 CACHE_UNLOCK;
2051 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2054 CACHE_UNLOCK;
2055 return send_error(ctx, 0);
2058 static int cachetimeout_command(assuan_context_t ctx, char *line)
2060 guchar md5file[16];
2061 glong timeout;
2062 gchar **req = split_input_line(line, " ", 0);
2063 gchar *p;
2064 struct client_s *client = assuan_get_pointer(ctx);
2066 if (!req || !*req || !req[1]) {
2067 g_strfreev(req);
2068 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2071 errno = 0;
2072 timeout = strtol(req[0], &p, 10);
2074 if (errno != 0 || *p != 0) {
2075 g_strfreev(req);
2076 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2079 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, req[1], strlen(req[1]));
2080 g_strfreev(req);
2081 CACHE_LOCK(client->ctx);
2083 if (cache_set_timeout(md5file, timeout) == FALSE) {
2084 CACHE_UNLOCK;
2085 return send_error(ctx, EPWMD_CACHE_NOT_FOUND);
2088 CACHE_UNLOCK;
2089 return send_error(ctx, 0);
2092 static int dump_command(assuan_context_t ctx, char *line)
2094 xmlChar *xml;
2095 gssize len;
2096 struct client_s *client = assuan_get_pointer(ctx);
2097 gpg_error_t error;
2099 if (disable_list_and_dump == TRUE)
2100 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2102 error = file_modified(client);
2104 if (error) {
2105 log_write("%s: %s", client->filename ? client->filename : "",
2106 pwmd_strerror(error));
2107 return send_error(ctx, error);
2110 xmlDocDumpFormatMemory(client->doc, &xml, &len, 1);
2111 error = assuan_send_data(ctx, xml, len);
2112 xmlFree(xml);
2113 return send_error(ctx, error);
2116 static int getconfig_command(assuan_context_t ctx, gchar *line)
2118 struct client_s *client = assuan_get_pointer(ctx);
2119 gpg_error_t error = 0;
2120 gchar *p, *tmp;
2122 if (strcmp(line, "key") == 0 || strcmp(line, "key_file") == 0)
2123 return send_error(ctx, GPG_ERR_NOT_IMPLEMENTED);
2125 p = get_key_file_string(client->filename ? client->filename : "global", line);
2127 if (!p)
2128 return send_error(ctx, EPWMD_COMMAND_SYNTAX);
2130 tmp = expand_homedir(p);
2131 g_free(p);
2132 p = tmp;
2133 error = assuan_send_data(ctx, p, strlen(p));
2134 g_free(p);
2135 return send_error(ctx, error);
2138 void cleanup_assuan(assuan_context_t ctx)
2140 struct client_s *cl = assuan_get_pointer(ctx);
2142 if (cl)
2143 cleanup_client(cl);
2146 static void reset_notify(assuan_context_t ctx)
2148 struct client_s *cl = assuan_get_pointer(ctx);
2150 if (cl)
2151 cleanup_client(cl);
2154 static gpg_error_t parse_client_option(assuan_context_t ctx, const gchar *line)
2156 gchar name[32] = {0}, value[256] = {0};
2158 if (sscanf(line, " %31[a-zA-Z] = %255c", name, value) != 2)
2159 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_SYNTAX);
2161 if (g_strcasecmp(name, (gchar *)"NAME") == 0) {
2162 pth_attr_t attr = pth_attr_of(pth_self());
2164 log_write("OPTION CLIENT %s", line);
2165 pth_attr_set(attr, PTH_ATTR_NAME, value);
2166 pth_attr_destroy(attr);
2168 else
2169 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2171 return 0;
2174 static int option_handler(assuan_context_t ctx, const gchar *name,
2175 const gchar *value)
2177 #ifdef WITH_PINENTRY
2178 struct client_s *client = assuan_get_pointer(ctx);
2179 #endif
2181 if (!value || !*value)
2182 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2184 if (g_strcasecmp(name, (gchar *)"client") == 0)
2185 return parse_client_option(ctx, value);
2187 #ifdef WITH_PINENTRY
2188 if (g_strcasecmp(name, (gchar *)"ttyname") == 0) {
2189 g_free(client->pinentry->ttyname);
2190 client->pinentry->ttyname = g_strdup(value);
2192 else if (g_strcasecmp(name, (gchar *)"ttytype") == 0) {
2193 g_free(client->pinentry->ttytype);
2194 client->pinentry->ttytype = g_strdup(value);
2196 else if (g_strcasecmp(name, (gchar *)"display") == 0) {
2197 g_free(client->pinentry->display);
2198 client->pinentry->display = g_strdup(value);
2200 else if (g_strcasecmp(name, (gchar *)"path") == 0) {
2201 g_free(client->pinentry->path);
2202 client->pinentry->path = g_strdup(value);
2204 else if (g_strcasecmp(name, (gchar *)"title") == 0) {
2205 g_free(client->pinentry->title);
2206 client->pinentry->title = g_strdup(value);
2208 else if (g_strcasecmp(name, (gchar *)"prompt") == 0) {
2209 g_free(client->pinentry->prompt);
2210 client->pinentry->prompt = g_strdup(value);
2212 else if (g_strcasecmp(name, (gchar *)"desc") == 0) {
2213 g_free(client->pinentry->desc);
2214 client->pinentry->desc = g_strdup(value);
2216 #if 0
2217 /* Need to wait for pinentry to support a --timeout option so it can
2218 * terminate itself. Cannot do it here because assuan_pipe_connect() calls
2219 * execv() which replaces the pid of the fork()ed thread from
2220 * pinentry_fork(). So pinentry will become a real process after the
2221 * thread terminates and won't be able to be kill()ed from pwmd.
2223 else if (g_strcasecmp(name, (gchar *)"timeout") == 0) {
2224 gchar *p = NULL;
2225 gint n = strtol(value, &p, 10);
2227 if (*p || n < 0)
2228 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2230 client->pinentry->timeout = n;
2232 #endif
2233 else if (g_strcasecmp(name, (gchar *)"pinentry") == 0) {
2234 gchar *p = NULL;
2235 gint n = strtol(value, &p, 10);
2237 if (*p || n < 0 || n > 1)
2238 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_INV_VALUE);
2240 g_key_file_set_boolean(keyfileh, client->filename ? client->filename : "global",
2241 "enable_pinentry", n == 0 ? FALSE : TRUE);
2243 else
2244 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_UNKNOWN_OPTION);
2245 #else
2246 return gpg_err_make(PWMD_ERR_SOURCE, GPG_ERR_NOT_IMPLEMENTED);
2247 #endif
2249 log_write("OPTION %s=%s", name, value);
2250 return 0;
2253 gpg_error_t register_commands(assuan_context_t ctx)
2255 static struct {
2256 const gchar *name;
2257 gint (*handler)(assuan_context_t, gchar *line);
2258 } table[] = {
2259 { "OPEN", open_command },
2260 { "SAVE", save_command },
2261 { "LIST", list_command },
2262 { "REALPATH", realpath_command },
2263 { "STORE", store_command },
2264 { "DELETE", delete_command },
2265 { "GET", get_command },
2266 { "ATTR", attr_command },
2267 { "ISCACHED", iscached_command },
2268 { "CLEARCACHE", clearcache_command },
2269 { "CACHETIMEOUT", cachetimeout_command },
2270 { "GETCONFIG", getconfig_command },
2271 { "DUMP", dump_command },
2272 { "INPUT", NULL },
2273 { "OUTPUT", NULL },
2274 { NULL, NULL }
2276 gint i, rc;
2278 for (i=0; table[i].name; i++) {
2279 rc = assuan_register_command (ctx, table[i].name, table[i].handler);
2281 if (rc)
2282 return rc;
2285 rc = assuan_register_bye_notify(ctx, cleanup_assuan);
2287 if (rc)
2288 return rc;
2290 rc = assuan_register_option_handler(ctx, option_handler);
2292 if (rc)
2293 return rc;
2295 return assuan_register_reset_notify(ctx, reset_notify);
2298 gpg_error_t try_xml_decrypt(assuan_context_t ctx, gint fd, struct stat st,
2299 guchar *key)
2301 guchar *iv;
2302 void *inbuf;
2303 gsize insize, len;
2304 guchar tkey[gcrykeysize];
2305 struct client_s *client = ctx ? assuan_get_pointer(ctx) : NULL;
2306 gcry_cipher_hd_t gh;
2307 guint iter = 0, n_iter = 0;
2308 gint iter_progress;
2309 void *outbuf = NULL;
2310 gint zerror = 0;
2311 glong outsize = 0;
2312 gpg_error_t error;
2313 file_header_t file_header;
2314 gchar str[ASSUAN_LINELENGTH];
2316 if (!ctx) {
2317 error = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
2319 if (error)
2320 return error;
2322 else
2323 gh = client->gh;
2325 lseek(fd, 0, SEEK_SET);
2326 insize = st.st_size - sizeof(file_header_t);
2327 iv = gcry_malloc(gcryblocksize);
2329 if (!iv) {
2330 if (!ctx)
2331 gcry_cipher_close(gh);
2333 return gpg_error_from_errno(ENOMEM);
2336 len = pth_read(fd, &file_header, sizeof(file_header_t));
2338 if (len != sizeof(file_header_t)) {
2339 len = errno;
2341 if (!ctx)
2342 gcry_cipher_close(gh);
2344 gcry_free(iv);
2345 errno = len;
2346 return gpg_error_from_errno(errno);
2349 /* No encryption iterations. This is a plain (gzipped) file. */
2350 if (file_header.iter == -1) {
2352 * cache_file_count() needs both .used == TRUE and a valid key in
2353 * order for it to count as a used cache entry. Fixes CACHE status
2354 * messages.
2356 memset(key, '!', gcrykeysize);
2357 insize = st.st_size - sizeof(file_header_t);
2360 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
2361 inbuf = gcry_malloc(insize);
2363 if (!inbuf) {
2364 if (!ctx)
2365 gcry_cipher_close(gh);
2367 gcry_free(iv);
2368 return gpg_error_from_errno(ENOMEM);
2371 len = pth_read(fd, inbuf, insize);
2373 if (len != insize) {
2374 len = errno;
2376 if (!ctx)
2377 gcry_cipher_close(gh);
2379 gcry_free(iv);
2380 errno = len;
2381 return gpg_error_from_errno(errno);
2384 if (file_header.iter == -1)
2385 goto decompress;
2387 memcpy(tkey, key, sizeof(tkey));
2388 tkey[0] ^= 1;
2390 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2391 if (!ctx) {
2392 gcry_cipher_close(gh);
2393 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2395 else
2396 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2398 gcry_free(inbuf);
2399 gcry_free(iv);
2400 return error;
2403 if ((error = gcry_cipher_setkey(gh, key, gcrykeysize))) {
2404 if (!ctx) {
2405 gcry_cipher_close(gh);
2406 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2408 else
2409 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2411 gcry_free(inbuf);
2412 gcry_free(iv);
2414 if (!ctx)
2415 gcry_cipher_close(gh);
2417 return error;
2420 iter_progress = get_key_file_integer("global", "iteration_progress");
2422 if (ctx && iter_progress > 0 && file_header.iter >= iter_progress) {
2423 error = assuan_write_status(client->ctx, "DECRYPT", "0");
2425 if (error) {
2426 gcry_free(inbuf);
2427 gcry_free(iv);
2428 return error;
2432 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2434 if (error) {
2435 gcry_free(inbuf);
2436 gcry_free(iv);
2437 return error;
2440 if ((error = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
2441 if (!ctx) {
2442 gcry_cipher_close(gh);
2443 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2445 else
2446 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2448 gcry_free(inbuf);
2449 gcry_free(iv);
2450 return error;
2453 while (iter < file_header.iter) {
2454 if (ctx && iter_progress > 0 && iter >= iter_progress) {
2455 if (!(iter % iter_progress)) {
2456 error = assuan_write_status(ctx, "DECRYPT",
2457 print_fmt(str, sizeof(str), "%i", ++n_iter * iter_progress));
2459 if (error) {
2460 gcry_free(inbuf);
2461 gcry_free(iv);
2462 return error;
2467 if ((error = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
2468 if (!ctx) {
2469 gcry_cipher_close(gh);
2470 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2472 else
2473 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2475 gcry_free(inbuf);
2476 gcry_free(iv);
2477 return error;
2480 error = decrypt_xml(gh, inbuf, insize, NULL, 0);
2482 if (error) {
2483 if (!ctx) {
2484 gcry_cipher_close(gh);
2485 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2487 else
2488 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(error));
2490 gcry_free(inbuf);
2491 gcry_free(iv);
2492 return error;
2495 iter++;
2496 pth_yield(NULL);
2499 if (ctx && iter_progress && file_header.iter >= iter_progress) {
2500 error = assuan_write_status(ctx, "DECRYPT",
2501 print_fmt(str, sizeof(str), "%i", file_header.iter));
2503 if (error) {
2504 gcry_free(inbuf);
2505 gcry_free(iv);
2506 return error;
2510 gcry_free(iv);
2512 decompress:
2513 if (do_decompress(ctx, inbuf, insize, &outbuf, &outsize, &zerror) == FALSE) {
2515 * Z_DATA_ERROR may be returned if this file hasn't been compressed yet.
2517 if (zerror == Z_MEM_ERROR) {
2518 gcry_free(inbuf);
2519 return gpg_error_from_errno(ENOMEM);
2521 else if (zerror != Z_DATA_ERROR) {
2522 gcry_free(inbuf);
2524 if (!ctx)
2525 gcry_cipher_close(gh);
2527 return EPWMD_BADKEY;
2530 else {
2531 gcry_free(inbuf);
2532 inbuf = outbuf;
2533 insize = outsize;
2536 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
2537 gcry_free(inbuf);
2539 if (!ctx)
2540 gcry_cipher_close(gh);
2542 return EPWMD_BADKEY;
2545 if (ctx) {
2546 client->xml = inbuf;
2547 client->len = insize;
2549 else {
2550 gcry_cipher_close(gh);
2551 gcry_free(inbuf);
2554 return 0;
2558 * This is called after every Assuan command.
2560 void command_finalize(assuan_context_t ctx, gint error)
2562 struct client_s *client = assuan_get_pointer(ctx);
2564 unlock_file_mutex(client);