check the return of pth_mutex_acquire and pth_mutex_release in lock.h
[pwmd.git] / src / pwmd.c
blob208b41176a5c34ee9552876873c37643c1f6b32f
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2009 Ben Kibbey <bjk@luxsci.net>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include <err.h>
27 #include <errno.h>
28 #include <ctype.h>
29 #include <string.h>
30 #include <sys/un.h>
31 #include <signal.h>
32 #include <stdarg.h>
33 #include <string.h>
34 #include <sys/wait.h>
35 #include <fcntl.h>
36 #include <pwd.h>
37 #include <glib.h>
38 #include <glib/gprintf.h>
39 #include <sys/mman.h>
40 #include <termios.h>
41 #include <assert.h>
42 #include <syslog.h>
43 #include <zlib.h>
44 #include <gcrypt.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <netdb.h>
48 #include <sys/time.h>
49 #include <sys/resource.h>
51 #ifdef TM_IN_SYS_TIME
52 #include <sys/time.h>
53 #else
54 #include <time.h>
55 #endif
57 #include "mem.h"
58 #include "xml.h"
59 #include "common.h"
61 #ifdef WITH_PINENTRY
62 #include "pinentry.h"
63 #endif
65 #ifdef WITH_GNUTLS
66 #include "tls.h"
67 #endif
68 #include "commands.h"
69 #include "pwmd_error.h"
70 #include "cache.h"
71 #include "misc.h"
72 #include "pwmd.h"
73 #include "lock.h"
75 GCRY_THREAD_OPTION_PTH_IMPL;
77 static void clear_rcfile_keys()
79 gsize n;
80 gchar **groups;
81 gchar **p;
83 groups = g_key_file_get_groups(keyfileh, &n);
85 for (p = groups; *p; p++) {
86 GError *rc = NULL;
88 if (g_key_file_has_key(keyfileh, *p, "key", &rc) == TRUE)
89 g_key_file_set_string(keyfileh, *p, "key", "");
92 g_strfreev(groups);
95 static void *reload_rcfile_thread(void *arg)
97 gboolean b = disable_list_and_dump;
98 GKeyFile *k;
99 pth_attr_t attr = pth_attr_of(pth_self());
101 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
102 pth_attr_destroy(attr);
103 pth_mutex_acquire(&rcfile_mutex, FALSE, NULL);
104 log_write(N_("reloading configuration file '%s'"), rcfile);
105 k = parse_rcfile(FALSE);
107 if (!k)
108 goto done;
110 g_key_file_free(keyfileh);
111 keyfileh = k;
112 parse_rcfile_keys();
113 clear_rcfile_keys();
114 disable_list_and_dump = b;
115 #ifdef WITH_GNUTLS
116 startStopTcp(FALSE);
117 #endif
118 startStopKeepAlive(FALSE);
119 send_status_all(STATUS_CONFIG);
120 done:
121 pth_mutex_release(&rcfile_mutex);
122 return NULL;
125 static void reload_rcfile()
127 pth_t tid;
128 pth_attr_t attr = pth_attr_new();
130 pth_attr_init(attr);
131 pth_attr_set(attr, PTH_ATTR_JOINABLE, 0);
132 tid = pth_spawn(attr, reload_rcfile_thread, NULL);
133 pth_attr_destroy(attr);
136 gpg_error_t send_syserror(assuan_context_t ctx, gint e)
138 gpg_error_t n = gpg_error_from_errno(e);
140 pth_cancel_point();
141 return assuan_process_done(ctx, assuan_set_error(ctx, n, gpg_strerror(n)));
144 gpg_error_t send_error(assuan_context_t ctx, gpg_error_t e)
146 gpg_err_code_t n = gpg_err_code(e);
147 gpg_error_t code = gpg_err_make(PWMD_ERR_SOURCE, n);
148 struct client_s *client = assuan_get_pointer(ctx);
150 pth_cancel_point();
152 if (!e)
153 return assuan_process_done(ctx, 0);
155 if (!ctx) {
156 log_write("%s", pwmd_strerror(e));
157 return e;
160 if (n == EPWMD_LIBXML_ERROR) {
161 xmlErrorPtr xe = client->xml_error;
163 if (!xe)
164 xe = xmlGetLastError();
166 e = assuan_process_done(ctx, assuan_set_error(ctx, code, xe->message));
167 log_write("%s", xe->message);
169 if (xe == client->xml_error)
170 xmlResetError(xe);
171 else
172 xmlResetLastError();
174 client->xml_error = NULL;
175 return e;
178 return assuan_process_done(ctx, assuan_set_error(ctx, code, pwmd_strerror(e)));
181 void log_write(const gchar *fmt, ...)
183 gchar *args, *line;
184 va_list ap;
185 struct tm *tm;
186 time_t now;
187 gchar tbuf[21];
188 gint fd = -1;
189 gchar *name;
190 gchar buf[255];
191 pth_t tid = pth_self();
192 pth_attr_t attr;
194 if ((!logfile && !isatty(STDERR_FILENO) && log_syslog == FALSE) || !fmt)
195 return;
197 if (!cmdline && logfile) {
198 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
199 warn("%s", logfile);
200 return;
204 va_start(ap, fmt);
206 if (g_vasprintf(&args, fmt, ap) == -1) {
207 if (logfile)
208 close(fd);
210 va_end(ap);
211 return;
214 va_end(ap);
216 if (cmdline) {
217 fprintf(stderr, "%s\n", args);
218 fflush(stderr);
219 g_free(args);
220 return;
223 attr = pth_attr_of(tid);
225 if (pth_attr_get(attr, PTH_ATTR_NAME, &name) == FALSE)
226 name = "unknown";
228 pth_attr_destroy(attr);
229 name = print_fmt(buf, sizeof(buf), "%s(%p): ", name, tid);
231 if (!cmdline && log_syslog == TRUE)
232 syslog(LOG_INFO, "%s%s", name, args);
234 time(&now);
235 tm = localtime(&now);
236 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
237 tbuf[sizeof(tbuf) - 1] = 0;
239 if (args[strlen(args)-1] == '\n')
240 args[strlen(args)-1] = 0;
242 line = g_strdup_printf("%s %i %s%s\n", tbuf, getpid(), name, args);
243 g_free(args);
245 if (!line) {
246 if (logfile)
247 close(fd);
249 return;
252 if (logfile) {
253 write(fd, line, strlen(line));
254 fsync(fd);
255 close(fd);
258 if (isatty(STDERR_FILENO)) {
259 fprintf(stderr, "%s", line);
260 fflush(stderr);
263 g_free(line);
266 static void usage(gchar *pn)
268 g_fprintf(stderr, N_(
269 "Usage: %s [-hvDnP] [-f <rcfile>] [-C <filename>] "
270 "[-I <filename> [-i <iter>]]\n "
271 "[-k <keyfile>] [-o <outfile>] [file1] [...]\n"
272 " -n run as a foreground process\n"
273 " -f load the specified rcfile (~/.pwmd/config)\n"
274 " -C convert a version 1 data file to version 2\n"
275 " -I import an XML file\n"
276 " -i encrypt with the specified number of iterations when importing\n"
277 " (config default in the \"global\" section)\n"
278 " -k obtain the key from the specified file\n"
279 " -o output file for use with the -C and -I options\n"
280 " -D disable use of the LIST and DUMP commands\n"
281 " -P disable pinentry\n"
282 " -v version\n"
283 " -h this help text\n"
284 ), pn);
285 exit(EXIT_FAILURE);
288 static int gcry_SecureCheck(const void *ptr)
290 return 1;
293 static void setup_gcrypt()
295 gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
296 gcry_check_version(GCRYPT_VERSION);
298 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
299 xfree);
301 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
302 NULL) != 0)
303 errx(EXIT_FAILURE, N_("Required AES cipher not supported by libgcrypt."));
305 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
306 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
309 static gint new_connection(struct client_s *cl)
311 gpg_error_t rc;
312 gchar ver[ASSUAN_LINELENGTH];
314 rc = assuan_init_socket_server_ext(&cl->ctx, cl->thd->fd, 2);
316 if (rc)
317 goto fail;
319 assuan_set_pointer(cl->ctx, cl);
320 g_snprintf(ver, sizeof(ver), "%s", PACKAGE_STRING);
321 assuan_set_hello_line(cl->ctx, ver);
322 rc = register_commands(cl->ctx);
324 if (rc)
325 goto fail;
327 rc = assuan_accept(cl->ctx);
329 if (rc)
330 goto fail;
332 return 0;
334 fail:
335 log_write("%s", gpg_strerror(rc));
336 close(cl->thd->fd);
337 cl->thd->fd = -1;
338 return 1;
341 static void xml_error_cb(void *data, xmlErrorPtr e)
343 struct client_s *client = data;
346 * Keep the first reported error as the one to show in the error
347 * description. Reset in send_error().
349 if (client->xml_error)
350 return;
352 xmlCopyError(e, client->xml_error);
355 static void cleanup_crypto_handler(void *arg)
357 struct client_crypto_s **a = (struct client_crypto_s **)arg;
358 struct client_crypto_s *cr = *a;
360 if (cr->fh) {
361 if (cr->fh->fd != -1 ||
362 (cmdline == TRUE && cr->fh->fd != STDOUT_FILENO))
363 close(cr->fh->fd);
365 if (cr->fh->doc)
366 gcry_free(cr->fh->doc);
368 g_free(cr->fh);
371 if (cr->gh)
372 gcry_cipher_close(cr->gh);
374 g_free(cr);
375 *a = NULL;
378 void cleanup_crypto(struct client_crypto_s **c)
380 struct client_crypto_s *cr = *c;
382 if (!cr)
383 return;
385 if (cr->iv)
386 gcry_free(cr->iv);
388 if (cr->key)
389 gcry_free(cr->key);
391 if (cr->tkey)
392 gcry_free(cr->tkey);
394 if (cr->tkey2)
395 gcry_free(cr->tkey2);
397 if (cr->inbuf)
398 gcry_free(cr->inbuf);
400 if (cr->outbuf)
401 gcry_free(cr->outbuf);
403 cleanup_crypto_handler(c);
407 * This is called after a child_thread terminates. Set with
408 * pth_cleanup_push().
410 static void cleanup_cb(void *arg)
412 struct client_thread_s *cn = arg;
413 struct client_s *cl = cn->cl;
415 MUTEX_LOCK(&cn_mutex);
416 cn_thread_list = g_slist_remove(cn_thread_list, cn);
417 MUTEX_UNLOCK(&cn_mutex);
419 if (cn->msg_tid) {
420 pth_cancel(cn->msg_tid);
421 pth_join(cn->msg_tid, NULL);
424 for (;;) {
425 struct status_msg_s *m = g_slist_nth_data(cn->msg_list, 0);
427 if (!m)
428 break;
430 cn->msg_list = g_slist_remove(cn->msg_list, m);
431 g_free(m);
434 if (cl && cl->freed == FALSE)
435 cleanup_client(cl);
437 #ifdef WITH_GNUTLS
438 if (cn->tls) {
439 gnutls_deinit(cn->tls->ses);
441 if (cn->tls->fp)
442 g_free(cn->tls->fp);
444 g_free(cn->tls);
446 #endif
448 if (cl && cl->ctx)
449 assuan_deinit_server(cl->ctx);
450 else if (cl && cl->thd && cl->thd->fd != -1)
451 close(cl->thd->fd);
453 #ifdef WITH_PINENTRY
454 if (cl && cl->pinentry)
455 cleanup_pinentry(cl->pinentry);
456 #endif
458 if (cl->crypto)
459 cleanup_crypto(&cl->crypto);
461 g_free(cl);
462 g_free(cn);
463 log_write(N_("exiting, fd=%i"), cn->fd);
464 send_status_all(STATUS_CLIENTS);
468 * Called every time a connection is made from init_new_connection(). This is
469 * the thread entry point.
471 static void *client_thread(void *data)
473 struct client_thread_s *thd = data;
474 struct client_s *cl = g_malloc0(sizeof(struct client_s));
475 gpg_error_t rc;
476 pth_attr_t attr = pth_attr_of(pth_self());
478 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
479 pth_attr_destroy(attr);
482 * Prevent a race condition with init_new_connection() if this thread
483 * fails (returns) for some reason before init_new_connection() releases
484 * the cn_mutex.
486 MUTEX_LOCK(&cn_mutex);
487 MUTEX_UNLOCK(&cn_mutex);
488 pth_cleanup_push(cleanup_cb, thd);
490 if (!cl) {
491 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
492 goto fail;
495 thd->cl = cl;
497 #ifdef WITH_GNUTLS
499 * Do the TLS handshake before anything else.
501 if (thd->remote) {
502 gchar *prio = get_key_file_string("global", "cipher_suite");
504 thd->tls = tls_init(thd->fd, prio);
505 g_free(prio);
507 if (!thd->tls) {
508 close(thd->fd);
509 thd->fd = -1;
510 goto fail;
513 pth_cancel_point();
515 #endif
517 cl->thd = thd;
519 if (new_connection(cl))
520 goto fail;
522 #ifdef WITH_PINENTRY
523 cl->pinentry = pinentry_init();
525 if (!cl->pinentry) {
526 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
527 goto fail;
530 #ifdef WITH_GNUTLS
531 /* Require the client to explicity set OPTION PINENTRY since the DISPLAY
532 * might be automatically set from the client. Connections to the X11
533 * server usually aren't encrypted and the client might unintentionally
534 * send the passphrase in the clear.
536 if (thd->remote)
537 cl->pinentry->enable = FALSE;
538 #endif
539 #endif
541 #ifdef HAVE_MLOCKALL
542 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
543 log_write("mlockall(): %s", strerror(errno));
544 goto fail;
546 #endif
548 pth_mutex_init(&thd->msg_list_mutex);
549 attr = pth_attr_new();
550 pth_attr_init(attr);
551 thd->msg_tid = pth_spawn(attr, client_msg_thread, thd);
552 pth_attr_destroy(attr);
553 pth_yield(thd->msg_tid);
554 rc = send_status(cl->ctx, STATUS_CACHE, NULL);
556 if (rc) {
557 log_write("%s", gpg_strerror(rc));
558 goto fail;
561 send_status_all(STATUS_CLIENTS);
562 xmlSetStructuredErrorFunc(cl, xml_error_cb);
564 for (;;) {
565 #ifdef WITH_PINENTRY
566 pth_event_t pev = NULL;
567 #endif
568 pth_status_t st;
569 pth_event_t ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE,
570 cl->thd->fd);
572 #ifdef WITH_PINENTRY
573 if (cl->pinentry->status == PINENTRY_RUNNING) {
574 pev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE, cl->pinentry->fd);
575 ev = pth_event_concat(ev, pev, NULL);
577 #endif
579 pth_wait(ev);
580 pth_event_isolate(ev);
581 st = pth_event_status(ev);
583 if (st == PTH_STATUS_OCCURRED) {
584 rc = assuan_process_next(cl->ctx);
585 pth_cancel_point();
587 if (rc) {
588 cl->inquire_status = INQUIRE_INIT;
590 if (gpg_err_code(rc) == GPG_ERR_EOF)
591 goto done;
593 log_write("assuan_process_next(): %s", gpg_strerror(rc));
594 rc = send_error(cl->ctx, gpg_err_make(PWMD_ERR_SOURCE, rc));
596 if (rc) {
597 log_write("assuan_process_done(): %s", gpg_strerror(rc));
598 goto done;
601 else {
602 #ifdef WITH_PINENTRY
603 if (cl->pinentry->pid && cl->pinentry->status == PINENTRY_INIT)
604 cl->pinentry->status = PINENTRY_RUNNING;
605 #endif
607 switch (cl->inquire_status) {
608 case INQUIRE_BUSY:
609 case INQUIRE_INIT:
610 break;
611 case INQUIRE_DONE:
612 cl->inquire_status = INQUIRE_INIT;
613 rc = assuan_process_done(cl->ctx, 0);
614 pth_cancel_point();
615 break;
620 pth_event_free(ev, PTH_FREE_THIS);
622 #ifdef WITH_PINENTRY
623 if (pev) {
624 pth_event_isolate(pev);
625 st = pth_event_status(pev);
626 pth_event_free(pev, PTH_FREE_THIS);
629 rc = pinentry_iterate(cl,
630 pev && cl->pinentry->fd != -1 && st == PTH_STATUS_OCCURRED);
631 #endif
635 * Client cleanup (including XML data) is done in cleanup_cb() from
636 * the cleanup thread.
638 done:
639 fail:
640 pth_cleanup_pop(1);
641 pth_exit(PTH_CANCELED);
642 return NULL;
645 static void setup_logging(GKeyFile *kf)
647 gboolean n = g_key_file_get_boolean(kf, "global", "enable_logging", NULL);
649 if (n == TRUE) {
650 gchar *p = g_key_file_get_string(kf, "global", "log_path", NULL);
652 if (*p == '~') {
653 gchar buf[PATH_MAX];
655 p++;
656 g_snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
657 g_free(p);
659 if (logfile)
660 g_free(logfile);
662 logfile = g_strdup(buf);
664 else {
665 if (logfile)
666 g_free(logfile);
668 logfile = p;
672 log_syslog = g_key_file_get_boolean(kf, "global", "syslog", NULL);
676 * Make sure all settings are set to either the specified setting or a
677 * default.
679 static void set_rcfile_defaults(GKeyFile *kf)
681 gchar buf[PATH_MAX];
683 if (g_key_file_has_key(kf, "global", "socket_path", NULL) == FALSE) {
684 g_snprintf(buf, sizeof(buf), "~/.pwmd/socket");
685 g_key_file_set_string(kf, "global", "socket_path", buf);
688 if (g_key_file_has_key(kf, "global", "data_directory", NULL) == FALSE) {
689 g_snprintf(buf, sizeof(buf), "~/.pwmd/data");
690 g_key_file_set_string(kf, "global", "data_directory", buf);
693 if (g_key_file_has_key(kf, "global", "backup", NULL) == FALSE)
694 g_key_file_set_boolean(kf, "global", "backup", TRUE);
696 if (g_key_file_has_key(kf, "global", "log_path", NULL) == FALSE) {
697 g_snprintf(buf, sizeof(buf), "~/.pwmd/log");
698 g_key_file_set_string(kf, "global", "log_path", buf);
701 if (g_key_file_has_key(kf, "global", "enable_logging", NULL) == FALSE)
702 g_key_file_set_boolean(kf, "global", "enable_logging", FALSE);
704 #ifdef HAVE_MLOCKALL
705 if (g_key_file_has_key(kf, "global", "disable_mlockall", NULL) == FALSE)
706 g_key_file_set_boolean(kf, "global", "disable_mlockall", TRUE);
707 #endif
709 if (g_key_file_has_key(kf, "global", "cache_timeout", NULL) == FALSE)
710 g_key_file_set_integer(kf, "global", "cache_timeout", -1);
712 if (g_key_file_has_key(kf, "global", "iterations", NULL) == FALSE ||
713 g_key_file_get_integer(kf, "global", "iterations", 0) < 0)
714 g_key_file_set_integer(kf, "global", "iterations", 1);
716 if (g_key_file_has_key(kf, "global", "disable_list_and_dump", NULL) == FALSE)
717 g_key_file_set_boolean(kf, "global", "disable_list_and_dump", FALSE);
719 if (g_key_file_has_key(kf, "global", "iteration_progress", NULL) == FALSE)
720 g_key_file_set_integer(kf, "global", "iteration_progress", 0);
722 if (g_key_file_has_key(kf, "global", "compression_level", NULL) == FALSE)
723 g_key_file_set_integer(kf, "global", "compression_level", 6);
725 if (g_key_file_has_key(kf, "global", "recursion_depth", NULL) == FALSE)
726 g_key_file_set_integer(kf, "global", "recursion_depth", DEFAULT_RECURSION_DEPTH);
728 if (g_key_file_has_key(kf, "global", "zlib_bufsize", NULL) == FALSE)
729 g_key_file_set_integer(kf, "global", "zlib_bufsize", DEFAULT_ZLIB_BUFSIZE);
731 zlib_bufsize = (uInt)g_key_file_get_integer(kf, "global", "zlib_bufsize", NULL);
733 max_recursion_depth = g_key_file_get_integer(kf, "global", "recursion_depth", NULL);
734 disable_list_and_dump = g_key_file_get_boolean(kf, "global", "disable_list_and_dump", NULL);
736 #ifdef HAVE_MLOCKALL
737 disable_mlock = g_key_file_get_boolean(kf, "global", "disable_mlockall", NULL);
738 #endif
740 if (g_key_file_has_key(kf, "global", "syslog", NULL) == FALSE)
741 g_key_file_set_boolean(kf, "global", "syslog", FALSE);
743 if (g_key_file_has_key(kf, "global", "keepalive", NULL) == FALSE)
744 g_key_file_set_integer(kf, "global", "keepalive", DEFAULT_KEEPALIVE_TO);
746 #ifdef WITH_PINENTRY
747 if (g_key_file_has_key(kf, "global", "enable_pinentry", NULL) == FALSE)
748 g_key_file_set_boolean(kf, "global", "enable_pinentry", TRUE);
750 if (g_key_file_has_key(kf, "global", "pinentry_timeout", NULL) == FALSE)
751 g_key_file_set_integer(kf, "global", "pinentry_timeout", 20);
752 #endif
754 #ifdef WITH_GNUTLS
755 if (g_key_file_has_key(kf, "global", "tcp_port", NULL) == FALSE)
756 g_key_file_set_integer(kf, "global", "tcp_port", 6466);
758 if (g_key_file_has_key(kf, "global", "enable_tcp", NULL) == FALSE)
759 g_key_file_set_boolean(kf, "global", "enable_tcp", FALSE);
761 if (g_key_file_has_key(kf, "global", "tcp_require_key", NULL) == FALSE)
762 g_key_file_set_boolean(kf, "global", "tcp_require_key", FALSE);
764 if (g_key_file_has_key(kf, "global", "tcp_wait", NULL) == FALSE)
765 g_key_file_set_boolean(kf, "global", "tcp_wait", 3);
767 if (g_key_file_has_key(kf, "global", "cipher_suite", NULL) == FALSE)
768 g_key_file_set_string(kf, "global", "cipher_suite", "SECURE256");
770 if (g_key_file_has_key(kf, "global", "tcp_use_crl", NULL) == FALSE)
771 g_key_file_set_boolean(kf, "global", "tcp_use_crl", FALSE);
772 #endif
774 setup_logging(kf);
777 static GKeyFile *parse_rcfile(gboolean specified)
779 GKeyFile *kf = g_key_file_new();
780 GError *rc = NULL;
782 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &rc) == FALSE) {
783 log_write("%s: %s", rcfile, rc->message);
785 if (cmdline && specified) {
786 g_clear_error(&rc);
787 return NULL;
790 if (rc->code == G_FILE_ERROR_NOENT) {
791 g_clear_error(&rc);
792 set_rcfile_defaults(kf);
793 return kf;
796 g_clear_error(&rc);
797 return NULL;
800 set_rcfile_defaults(kf);
801 return kf;
804 static gchar *do_get_password(const gchar *prompt)
806 gchar buf[LINE_MAX] = {0}, *p;
807 struct termios told, tnew;
808 gchar *key;
810 if (tcgetattr(STDIN_FILENO, &told) == -1) {
811 log_write("tcgetattr(): %s", strerror(errno));
812 return NULL;
815 memcpy(&tnew, &told, sizeof(struct termios));
816 tnew.c_lflag &= ~(ECHO);
817 tnew.c_lflag |= ICANON|ECHONL;
819 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
820 log_write("tcsetattr(): %s", strerror(errno));
821 tcsetattr(STDIN_FILENO, TCSANOW, &told);
822 return NULL;
825 fprintf(stderr, "%s", prompt);
827 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
828 tcsetattr(STDIN_FILENO, TCSANOW, &told);
829 return NULL;
832 tcsetattr(STDIN_FILENO, TCSANOW, &told);
833 p[strlen(p) - 1] = 0;
835 if (!buf[0]) {
836 key = gcry_malloc(1);
837 key[0] = 0;
839 else {
840 key = gcry_malloc(strlen(p) + 1);
841 sprintf(key, "%s", p);
844 memset(&buf, 0, sizeof(buf));
845 return key;
848 /* Only used when "enable_pinentry" is "false" or -P. */
849 static gpg_error_t get_input(const gchar *filename,
850 struct client_crypto_s *crypto, guchar *key, pinentry_cmd_t which)
852 gchar *prompt;
854 if (which == PINENTRY_SAVE) {
855 prompt = g_strdup_printf(N_("New passphrase for file %s: "), filename);
856 crypto->tkey = do_get_password(prompt);
857 g_free(prompt);
859 if (!crypto->tkey) {
860 log_write(N_("%s: Skipping file"), filename);
861 return GPG_ERR_BAD_PASSPHRASE;
864 prompt = g_strdup_printf(N_("Repeat passphrase: "));
865 crypto->tkey2 = do_get_password(prompt);
866 g_free(prompt);
868 if (!crypto->tkey2) {
869 log_write(N_("%s: Skipping file"), filename);
870 return GPG_ERR_BAD_PASSPHRASE;
873 if (strcmp(crypto->tkey, crypto->tkey2)) {
874 log_write(N_("%s: Passphrase mismatch"), filename);
875 return EPWMD_BADKEY;
878 gcry_md_hash_buffer(GCRY_MD_SHA256, key, crypto->tkey,
879 strlen(crypto->tkey) ? strlen(crypto->tkey) : 1);
880 return 0;
883 prompt = g_strdup_printf(N_("Passphrase required for %s: "), filename);
885 if ((crypto->tkey = do_get_password(prompt)) == NULL) {
886 log_write(N_("%s: Skipping file"), filename);
887 g_free(prompt);
888 return GPG_ERR_BAD_PASSPHRASE;
891 gcry_md_hash_buffer(GCRY_MD_SHA256, key, crypto->tkey,
892 strlen(crypto->tkey) ? strlen(crypto->tkey) : 1);
893 g_free(prompt);
894 return 0;
898 * inbuf must have been allocated with gcry_malloc().
900 gpg_error_t export_common(const gchar *filename, struct client_crypto_s *crypto,
901 gpointer inbuf, gulong insize)
903 gpg_error_t rc;
904 gint level, zrc;
905 gulong outsize;
906 gpointer outbuf;
908 level = get_key_file_integer(filename, "compression_level");
910 if (level < 0)
911 level = 0;
913 if (do_compress(NULL, level, inbuf, insize, &outbuf, &outsize, &zrc)
914 == FALSE) {
915 return zrc == Z_MEM_ERROR ? GPG_ERR_ENOMEM : GPG_ERR_COMPR_ALGO;
918 crypto->inbuf = outbuf;
919 crypto->insize = outsize;
920 rc = do_xml_encrypt(NULL, crypto, filename);
921 return rc;
924 static gpg_error_t get_password(const gchar *filename,
925 struct client_crypto_s *crypto, guchar *md5file, guchar *key,
926 pinentry_cmd_t which)
928 #ifdef WITH_PINENTRY
929 gpg_error_t rc = 0;
931 if (g_key_file_get_boolean(keyfileh, "global", "enable_pinentry", NULL)
932 == FALSE) {
933 #endif
934 return get_input(filename, crypto, key, which);
935 #ifdef WITH_PINENTRY
937 else {
938 gchar *result = NULL;
939 struct pinentry_s *pin = g_malloc0(sizeof(struct pinentry_s));
941 set_pinentry_defaults(pin);
942 pin->which = which;
943 pin->filename = g_strdup(filename);
944 rc = pinentry_getpin(pin, &result);
946 if (rc) {
947 xfree(result);
948 goto done;
951 gcry_md_hash_buffer(GCRY_MD_SHA256, key, result, strlen(result) ? strlen(result) : 1);
952 xfree(result);
953 cleanup_pinentry(pin);
956 done:
957 return rc;
958 #endif
961 static gboolean _getline(const gchar *file, gchar **result, gpg_error_t *rc)
963 FILE *fp;
964 gchar buf[LINE_MAX] = {0}, *p;
965 gchar *str = NULL;
966 gint len;
968 *rc = 0;
970 if ((fp = fopen(file, "r")) == NULL) {
971 *rc = gpg_error_from_syserror();
972 return FALSE;
975 p = fgets(buf, sizeof(buf), fp);
976 fclose(fp);
977 len = strlen(buf);
979 if (len && buf[len - 1] == '\n')
980 buf[--len] = 0;
982 str = gcry_malloc(len + 1);
984 if (!str) {
985 *rc = gpg_error_from_errno(ENOMEM);
986 return FALSE;
989 memcpy(str, buf, len ? len : 1);
990 str[len] = 0;
991 memset(&buf, 0, sizeof(buf));
992 *result = str;
993 return TRUE;
996 static gchar *parse_rcfile_keyfile(const gchar *filename, gboolean import,
997 gpg_error_t *rc)
999 GError *rv = NULL;
1000 gchar *t, *file = NULL, *str;
1002 *rc = GPG_ERR_UNKNOWN_ERRNO;
1004 if (import == FALSE) {
1005 if (g_key_file_has_key(keyfileh, filename, "key_file", &rv) == TRUE) {
1006 file = g_key_file_get_string(keyfileh, filename, "key_file", &rv);
1008 if (!file) {
1009 if (rv) {
1010 log_write("%s: key_file: %s", rcfile, rv->message);
1011 g_clear_error(&rv);
1014 return NULL;
1017 t = expand_homedir(file);
1019 if (!t) {
1020 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1021 *rc = gpg_error_from_errno(ENOMEM);
1022 return NULL;
1025 g_free(file);
1026 file = t;
1029 else {
1030 /* -I or -C. The filename is a key file. */
1031 file = g_strdup(filename);
1033 if (!file) {
1034 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1035 *rc = gpg_error_from_errno(ENOMEM);
1036 return NULL;
1040 if (rv) {
1041 log_write("%s: key_file: %s", rcfile, rv->message);
1042 g_clear_error(&rv);
1043 return NULL;
1046 if (!file)
1047 return NULL;
1049 if (_getline(file, &str, rc) == FALSE) {
1050 log_write("%s: %s: %s", filename, file, pwmd_strerror(*rc));
1051 g_free(file);
1052 return NULL;
1055 g_free(file);
1056 *rc = 0;
1057 return str;
1060 static gboolean xml_import(const gchar *filename, const gchar *outfile,
1061 const gchar *keyfile, guint64 iter)
1063 xmlDocPtr doc;
1064 gint fd;
1065 struct stat st;
1066 gint len;
1067 xmlChar *xmlbuf;
1068 xmlChar *xml;
1069 gpg_error_t rc;
1070 struct client_crypto_s *crypto;
1072 if (stat(filename, &st) == -1) {
1073 log_write("%s: %s", filename, strerror(errno));
1074 return FALSE;
1077 crypto = init_client_crypto();
1079 if (!crypto)
1080 return FALSE;
1082 crypto->key = gcry_malloc(gcrykeysize);
1083 memset(crypto->key, 0, gcrykeysize);
1085 if (!crypto->key) {
1086 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1087 goto fail;
1090 log_write(N_("Importing XML from '%s'. Output will be written to '%s' ..."),
1091 filename, outfile);
1093 if (iter && keyfile) {
1094 crypto->tkey = parse_rcfile_keyfile(keyfile, TRUE, &rc);
1096 if (!crypto->tkey)
1097 goto fail;
1099 gcry_md_hash_buffer(GCRY_MD_SHA256, crypto->key, crypto->tkey,
1100 strlen(crypto->tkey) ? strlen(crypto->tkey) : 1);
1102 else if (iter) {
1103 rc = get_password(outfile, crypto, NULL, crypto->key, PINENTRY_SAVE);
1105 if (rc)
1106 goto fail;
1109 if ((fd = open(filename, O_RDONLY)) == -1) {
1110 log_write("%s: %s", filename, strerror(errno));
1111 goto fail;
1114 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
1115 close(fd);
1116 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1117 goto fail;
1120 if (pth_read(fd, xmlbuf, st.st_size) == -1) {
1121 rc = errno;
1122 close(fd);
1123 errno = rc;
1124 log_write("%s: %s", filename, strerror(errno));
1125 goto fail;
1128 close(fd);
1129 xmlbuf[st.st_size] = 0;
1132 * Make sure the document validates.
1134 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
1135 log_write("xmlReadDoc() failed");
1136 gcry_free(xmlbuf);
1137 goto fail;
1140 gcry_free(xmlbuf);
1141 xmlDocDumpMemory(doc, &xml, &len);
1142 xmlFreeDoc(doc);
1144 if (!iter)
1145 memset(crypto->key, '!', gcrykeysize);
1147 crypto->fh = g_malloc0(sizeof(file_header_internal_t));
1149 if (!crypto->fh) {
1150 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1151 goto fail;
1154 crypto->fh->fh2.iter = iter;
1155 rc = export_common(outfile, crypto, xml, len);
1156 xmlFree(xml);
1158 if (rc) {
1159 send_error(NULL, rc);
1160 goto fail;
1163 cleanup_crypto(&crypto);
1164 return TRUE;
1166 fail:
1167 cleanup_crypto(&crypto);
1168 return FALSE;
1171 gchar *get_key_file_string(const gchar *section, const gchar *what)
1173 gchar *val = NULL;
1174 GError *grc = NULL;
1176 MUTEX_LOCK(&rcfile_mutex);
1178 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
1179 val = g_key_file_get_string(keyfileh, section, what, &grc);
1181 if (grc) {
1182 log_write("%s(%i): %s", __FILE__, __LINE__, grc->message);
1183 g_clear_error(&grc);
1186 else {
1187 if (g_key_file_has_key(keyfileh, "global", what, NULL) == TRUE) {
1188 val = g_key_file_get_string(keyfileh, "global", what, &grc);
1190 if (grc) {
1191 log_write("%s(%i): %s", __FILE__, __LINE__, grc->message);
1192 g_clear_error(&grc);
1197 MUTEX_UNLOCK(&rcfile_mutex);
1198 return val;
1201 gint get_key_file_integer(const gchar *section, const gchar *what)
1203 gint val = -1;
1204 GError *grc = NULL;
1206 MUTEX_LOCK(&rcfile_mutex);
1208 if (g_key_file_has_key(keyfileh, section ? section : "global", what, NULL) == TRUE) {
1209 val = g_key_file_get_integer(keyfileh, section ? section : "global", what, &grc);
1211 if (grc) {
1212 log_write("%s(%i): %s", __FILE__, __LINE__, grc->message);
1213 g_clear_error(&grc);
1216 else {
1217 if (g_key_file_has_key(keyfileh, "global", what, NULL) == TRUE) {
1218 val = g_key_file_get_integer(keyfileh, "global", what, &grc);
1220 if (grc) {
1221 log_write("%s(%i): %s", __FILE__, __LINE__, grc->message);
1222 g_clear_error(&grc);
1227 MUTEX_UNLOCK(&rcfile_mutex);
1228 return val;
1231 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
1233 gboolean val = FALSE;
1234 GError *grc = NULL;
1236 MUTEX_LOCK(&rcfile_mutex);
1238 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
1239 val = g_key_file_get_boolean(keyfileh, section, what, &grc);
1241 if (grc) {
1242 log_write("%s(%i): %s", __FILE__, __LINE__, grc->message);
1243 g_clear_error(&grc);
1246 else {
1247 if (g_key_file_has_key(keyfileh, "global", what, NULL) == TRUE) {
1248 val = g_key_file_get_boolean(keyfileh, "global", what, &grc);
1250 if (grc) {
1251 log_write("%s(%i): %s", __FILE__, __LINE__, grc->message);
1252 g_clear_error(&grc);
1257 MUTEX_UNLOCK(&rcfile_mutex);
1258 return val;
1261 static gboolean parse_rcfile_keys()
1263 gsize n;
1264 gchar **groups;
1265 gchar **p;
1266 gchar *str;
1268 groups = g_key_file_get_groups(keyfileh, &n);
1270 for (p = groups; *p; p++) {
1271 GError *rc = NULL;
1273 if (g_key_file_has_key(keyfileh, *p, "key", &rc) == TRUE) {
1274 str = g_key_file_get_string(keyfileh, *p, "key", &rc);
1276 if (!str) {
1277 if (rc) {
1278 log_write("%s: key: %s", rcfile, rc->message);
1279 g_clear_error(&rc);
1281 continue;
1284 do_cache_push(*p, str);
1285 g_free(str);
1286 continue;
1289 if (rc) {
1290 log_write("%s: key: %s", rcfile, rc->message);
1291 g_clear_error(&rc);
1292 continue;
1295 gpg_error_t ret;
1296 str = parse_rcfile_keyfile(*p, FALSE, &ret);
1298 if (!str)
1299 continue;
1301 do_cache_push(*p, str);
1302 gcry_free(str);
1305 g_strfreev(groups);
1306 return TRUE;
1309 static gboolean do_cache_push(const gchar *filename, const gchar *password)
1311 guchar md5file[16];
1312 gint timeout;
1313 const gchar *p = filename;
1314 struct client_crypto_s *crypto;
1315 gpg_error_t rc;
1317 while (isspace(*p))
1318 p++;
1320 if (!*p)
1321 return FALSE;
1323 if (valid_filename(p) == FALSE) {
1324 log_write(N_("%s: Invalid characters in filename"), p);
1325 return FALSE;
1328 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
1330 if (access(p, R_OK|W_OK) != 0) {
1331 log_write("%s: %s", p, strerror(errno));
1332 return FALSE;
1335 crypto = init_client_crypto();
1337 if (!crypto)
1338 return FALSE;
1340 crypto->fh = read_file_header(filename, FALSE, &rc);
1342 if (!crypto->fh) {
1343 log_write("%s: %s", p, pwmd_strerror(rc));
1344 cleanup_crypto(&crypto);
1345 return FALSE;
1348 crypto->key = gcry_malloc(gcrykeysize);
1350 if (!crypto->key) {
1351 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1352 cleanup_crypto(&crypto);
1353 return FALSE;
1356 log_write(N_("Adding '%s' to the file cache ..."), filename);
1358 if (crypto->fh->fh2.iter <= 0) {
1359 memset(crypto->key, '!', gcrykeysize);
1360 goto try_decrypt;
1363 if (!password) {
1364 rc = get_password(p, crypto, md5file, crypto->key, PINENTRY_OPEN);
1366 if (rc) {
1367 send_error(NULL, rc);
1368 cleanup_crypto(&crypto);
1369 return FALSE;
1372 gcry_free(crypto->fh->doc);
1373 crypto->fh->doc = NULL;
1375 else
1376 gcry_md_hash_buffer(GCRY_MD_SHA256, crypto->key, password,
1377 strlen(password) ? strlen(password) : 1);
1379 try_decrypt:
1380 rc = try_xml_decrypt(NULL, crypto->key, crypto, NULL, NULL);
1382 if (rc) {
1383 log_write("%s: %s", filename, pwmd_strerror(rc));
1384 cleanup_crypto(&crypto);
1385 return FALSE;
1388 if (cache_update_key(md5file, crypto->key) == FALSE) {
1389 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1390 cleanup_crypto(&crypto);
1391 return FALSE;
1394 timeout = get_key_file_integer(p, "cache_timeout");
1395 cache_set_timeout(md5file, timeout);
1396 log_write(N_("File '%s' now cached"), filename);
1397 cleanup_crypto(&crypto);
1398 return TRUE;
1401 static void init_new_connection(gint fd, gchar *addr)
1403 pth_attr_t attr;
1404 struct client_thread_s *new;
1406 new = g_malloc0(sizeof(struct client_thread_s));
1408 if (!new) {
1409 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1410 close(fd);
1411 return;
1414 MUTEX_LOCK(&cn_mutex);
1415 new->fd = fd;
1417 #ifdef WITH_GNUTLS
1418 if (addr)
1419 new->remote = TRUE;
1420 #endif
1422 attr = pth_attr_new();
1423 pth_attr_init(attr);
1424 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
1425 new->tid = pth_spawn(attr, client_thread, new);
1426 pth_attr_destroy(attr);
1428 if (!new->tid) {
1429 g_free(new);
1430 close(fd);
1431 log_write("pth_spawn() failed");
1432 MUTEX_UNLOCK(&cn_mutex);
1433 return;
1436 cn_thread_list = g_slist_append(cn_thread_list, new);
1437 MUTEX_UNLOCK(&cn_mutex);
1439 if (addr)
1440 log_write(N_("new connection: tid=%p, fd=%i, addr=%s"), new->tid, fd,
1441 addr);
1442 else
1443 log_write(N_("new connection: tid=%p, fd=%i"), new->tid, fd);
1446 #ifdef WITH_GNUTLS
1447 /* From Beej's Guide to Network Programming. It's a good tutorial. */
1448 static void *get_in_addr(struct sockaddr *sa)
1450 if (sa->sa_family == AF_INET)
1451 return &(((struct sockaddr_in*)sa)->sin_addr);
1453 return &(((struct sockaddr_in6*)sa)->sin6_addr);
1456 static void *tcp_accept_thread(void *arg)
1458 gint sockfd = (gint)arg;
1459 pth_attr_t attr = pth_attr_of(pth_self());
1461 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
1462 pth_attr_destroy(attr);
1464 for (;;) {
1465 struct sockaddr_storage raddr;
1466 socklen_t slen = sizeof(raddr);
1467 gint fd = -1;
1468 gulong n;
1469 gchar *t;
1471 fd = pth_accept(sockfd, (struct sockaddr *)&raddr, &slen);
1472 pth_cancel_point();
1474 if (fd == -1) {
1475 if (errno != EAGAIN) {
1476 if (!quit) // probably EBADF
1477 log_write("accept(): %s", strerror(errno));
1479 break;
1482 continue;
1485 if (quit)
1486 break;
1488 if (fd >= 0) {
1489 gchar s[INET6_ADDRSTRLEN];
1491 inet_ntop(raddr.ss_family, get_in_addr((struct sockaddr *)&raddr),
1492 s, sizeof s);
1493 init_new_connection(fd, s);
1496 t = get_key_file_string("global", "tcp_wait");
1497 n = strtol(t, NULL, 10);
1498 g_free(t);
1500 if (n < 0)
1501 n = 0;
1503 pth_usleep(n*100000);
1506 /* Just in case accept() failed for some reason other than EBADF */
1507 quit = 1;
1508 pth_exit(PTH_CANCELED);
1509 return NULL;
1511 #endif
1513 static void *accept_thread(void *arg)
1515 gint sockfd = (gint)arg;
1516 pth_attr_t attr = pth_attr_of(pth_self());
1518 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
1519 pth_attr_destroy(attr);
1521 for (;;) {
1522 socklen_t slen = sizeof(struct sockaddr_un);
1523 struct sockaddr_un raddr;
1524 gint fd = -1;
1526 fd = pth_accept(sockfd, (struct sockaddr *)&raddr, &slen);
1527 pth_cancel_point();
1529 if (fd == -1) {
1530 if (errno != EAGAIN) {
1531 if (!quit) // probably EBADF
1532 log_write("accept(): %s", strerror(errno));
1534 break;
1537 continue;
1540 if (fd >= 0)
1541 init_new_connection(fd, NULL);
1544 /* Just in case accept() failed for some reason other than EBADF */
1545 quit = 1;
1546 pth_exit(PTH_CANCELED);
1547 return NULL;
1550 static void *adjust_cache_timer_thread(void *arg)
1552 pth_attr_t attr = pth_attr_of(pth_self());
1554 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
1555 pth_attr_destroy(attr);
1557 for (;;) {
1558 pth_sleep(1);
1559 pth_cancel_point();
1560 CACHE_LOCK(NULL);
1561 cache_adjust_timer();
1562 CACHE_UNLOCK;
1565 return NULL;
1568 static void keepalive_cleanup(void *arg)
1570 pth_event_t ev = arg;
1572 pth_event_free(ev, PTH_FREE_ALL);
1575 static void *keepalive_thread(void *arg)
1577 struct timespec ts;
1578 gint to = (gint)arg;
1579 pth_mutex_t m;
1580 pth_cond_t cond;
1581 pth_attr_t attr = pth_attr_of(pth_self());
1583 pth_attr_set(attr, PTH_ATTR_NAME, __FUNCTION__);
1584 pth_attr_destroy(attr);
1585 pth_cond_init(&cond);
1586 pth_mutex_init(&m);
1587 pth_mutex_acquire(&m, FALSE, NULL);
1589 for (;;) {
1590 pth_event_t ev = pth_event(PTH_EVENT_TIME, pth_timeout(to, 0));
1591 pth_cleanup_push(pth_mutex_release, (void *)&m);
1592 pth_cleanup_push(keepalive_cleanup, ev);
1593 clock_gettime(CLOCK_REALTIME, &ts);
1594 pth_cond_await(&cond, &m, ev);
1595 pth_cancel_point();
1596 send_status_all(STATUS_KEEPALIVE);
1597 pth_cleanup_pop(1);
1598 pth_cleanup_pop(0);
1601 return NULL;
1604 static void startStopKeepAlive(gboolean term)
1606 gint n = get_key_file_integer("global", "keepalive");
1608 if (keepalive_tid)
1609 pth_cancel(keepalive_tid);
1611 keepalive_tid = 0;
1613 if (term)
1614 return;
1616 if (n > 0) {
1617 pth_attr_t attr = pth_attr_new();
1618 pth_attr_init(attr);
1619 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
1620 keepalive_tid = pth_spawn(attr, keepalive_thread, (void *)n);
1621 pth_yield(keepalive_tid);
1622 pth_attr_destroy(attr);
1626 static void server_loop(gint sockfd, gchar **socketpath)
1628 pth_t accept_tid;
1629 guint n;
1630 sigset_t sigset;
1631 gint n_clients = 0;
1632 pth_attr_t attr;
1633 pth_t cache_timeout_tid;
1635 sigemptyset(&sigset);
1637 /* Termination */
1638 sigaddset(&sigset, SIGTERM);
1639 sigaddset(&sigset, SIGINT);
1641 /* Clears the file cache. */
1642 sigaddset(&sigset, SIGUSR1);
1644 /* Configuration file reloading. */
1645 sigaddset(&sigset, SIGHUP);
1647 /* Caught in client_thread(). Sends a cache status message. */
1648 sigaddset(&sigset, SIGUSR2);
1650 /* Ignored everywhere. When a client disconnects abnormally this signal
1651 * gets raised. It isn't needed though because client_thread() will check
1652 * for rcs even after the client disconnects. */
1653 signal(SIGPIPE, SIG_IGN);
1654 sigprocmask(SIG_BLOCK, &sigset, NULL);
1656 log_write(N_("%s started for user %s"), PACKAGE_STRING, g_get_user_name());
1657 attr = pth_attr_new();
1658 pth_attr_init(attr);
1659 accept_tid = pth_spawn(attr, accept_thread, (void *)sockfd);
1660 pth_yield(accept_tid);
1661 startStopKeepAlive(FALSE);
1662 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
1663 cache_timeout_tid = pth_spawn(attr, adjust_cache_timer_thread, NULL);
1664 pth_yield(cache_timeout_tid);
1665 pth_attr_destroy(attr);
1667 do {
1668 gint sig;
1670 pth_sigwait(&sigset, &sig);
1671 log_write(N_("caught signal %i (%s)"), sig, strsignal(sig));
1673 /* Caught a signal. */
1674 switch (sig) {
1675 case SIGHUP:
1676 reload_rcfile();
1677 break;
1678 case SIGABRT:
1679 CACHE_LOCK(NULL);
1680 cache_clear(NULL, 2);
1681 CACHE_UNLOCK;
1682 #ifndef MEM_DEBUG
1683 xpanic();
1684 #endif
1685 exit(EXIT_FAILURE);
1686 case SIGUSR1:
1687 CACHE_LOCK(NULL);
1688 log_write(N_("clearing file cache"));
1689 cache_clear(NULL, 2);
1690 CACHE_UNLOCK;
1691 break;
1692 default:
1693 quit = 1;
1694 break;
1696 } while (!quit);
1699 * We're out of the main server loop. This happens when a signal was sent
1700 * to terminate the daemon. We'll wait for all clients to disconnect
1701 * before exiting and ignore any following signals.
1703 shutdown(sockfd, SHUT_RDWR);
1704 close(sockfd);
1705 pth_cancel(accept_tid);
1706 pth_join(accept_tid, NULL);
1707 #ifdef WITH_GNUTLS
1708 startStopTcp(TRUE);
1709 #endif
1710 unlink(*socketpath);
1711 g_free(*socketpath);
1712 *socketpath = NULL;
1713 MUTEX_LOCK(&cn_mutex);
1714 n = g_slist_length(cn_thread_list);
1715 MUTEX_UNLOCK(&cn_mutex);
1717 if (n)
1718 log_write(N_("waiting for all clients to disconnect"));
1720 while (n) {
1721 if (n != n_clients) {
1722 log_write(N_("%i clients remain"), n);
1723 n_clients = n;
1726 pth_sleep(1);
1727 MUTEX_LOCK(&cn_mutex);
1728 n = g_slist_length(cn_thread_list);
1729 MUTEX_UNLOCK(&cn_mutex);
1732 startStopKeepAlive(TRUE);
1733 pth_cancel(cache_timeout_tid);
1734 cache_free();
1738 * Called from pinentry_fork() in the child process.
1740 void free_client_list()
1742 gint i, t = g_slist_length(cn_thread_list);
1744 for (i = 0; i < t; i++) {
1745 struct client_thread_s *cn = g_slist_nth_data(cn_thread_list, i);
1747 free_client(cn->cl);
1750 cache_free();
1753 struct client_crypto_s *init_client_crypto()
1755 struct client_crypto_s *new = g_malloc0(sizeof(struct client_crypto_s));
1756 gpg_error_t rc;
1758 if (!new) {
1759 log_write("%s(%i): %s", __FILE__, __LINE__, strerror(ENOMEM));
1760 return NULL;
1763 rc = gcry_cipher_open(&new->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0);
1765 if (rc) {
1766 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(rc));
1767 g_free(new);
1768 return NULL;
1771 return new;
1774 static gpg_error_t convert_file(const gchar *filename, const gchar *keyfile,
1775 const gchar *outfile)
1777 gpg_error_t rc;
1778 guchar md5file[16];
1779 guint64 iter;
1780 struct client_crypto_s *crypto = init_client_crypto();
1782 if (!crypto)
1783 return GPG_ERR_ENOMEM;
1785 crypto->key = gcry_malloc(gcrykeysize);
1787 if (!crypto->key) {
1788 cleanup_crypto(&crypto);
1789 return GPG_ERR_ENOMEM;
1792 log_write(N_("Converting version 1 data file '%s' to version 2 ..."),
1793 filename);
1794 crypto->fh = read_file_header(filename, TRUE, &rc);
1796 if (!crypto->fh)
1797 goto done;
1799 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, filename, strlen(filename));
1801 /* The header in version 1 had a bug where the iterations were off-by-one.
1802 * So 0 iterations was really -1 in the header. This is fixed in v2 of the
1803 * header.
1805 if (crypto->fh->fh1.iter >= 0) {
1806 if (keyfile) {
1807 crypto->tkey = parse_rcfile_keyfile(keyfile, TRUE, &rc);
1809 if (!crypto->tkey)
1810 goto done;
1812 gcry_md_hash_buffer(GCRY_MD_SHA256, crypto->key, crypto->tkey,
1813 strlen(crypto->tkey) ? strlen(crypto->tkey) : 1);
1815 else {
1816 rc = get_password(filename, crypto, md5file, crypto->key,
1817 PINENTRY_OPEN);
1819 if (rc)
1820 goto done;
1824 rc = try_xml_decrypt(NULL, crypto->key, crypto, &crypto->fh->doc,
1825 &crypto->fh->len);
1827 if (rc)
1828 goto done;
1830 rc = convert_xml((gchar **)&crypto->fh->doc, &crypto->fh->len);
1832 if (rc) {
1833 log_write("%s: %s", filename, pwmd_strerror(rc));
1834 goto done;
1837 crypto->fh->v1 = FALSE;
1838 iter = crypto->fh->fh1.iter;
1839 memset(&crypto->fh->fh2, 0, sizeof(crypto->fh->fh2));
1840 /* Keep the iterations and key from the original file. */
1841 crypto->fh->fh2.iter = iter+1; // Bugfix for v1 data files.
1842 rc = export_common(outfile, crypto, crypto->fh->doc, crypto->fh->len);
1844 done:
1845 if (rc)
1846 send_error(NULL, rc);
1848 /* fh->doc is freed from do_xml_decrypt() via the inbuf pointer. */
1849 cleanup_crypto(&crypto);
1850 return rc;
1853 #ifdef WITH_GNUTLS
1854 static gboolean startStopTcp(gboolean term)
1856 struct addrinfo hints, *servinfo, *p;
1857 gint port = get_key_file_integer("global", "tcp_port");
1858 gchar buf[7];
1859 gint n;
1861 if (term || get_key_file_boolean("global", "enable_tcp") == FALSE) {
1862 if (tcpSockFd != -1) {
1863 pth_cancel(tcpAcceptTid);
1864 pth_join(tcpAcceptTid, NULL);
1865 shutdown(tcpSockFd, SHUT_RDWR);
1866 close(tcpSockFd);
1867 tcpSockFd = -1;
1869 /* A client may still be connected. */
1870 if (!quit)
1871 deinitTlsParams();
1874 return TRUE;
1877 if (tcpSockFd != -1)
1878 return TRUE;
1880 memset(&hints, 0, sizeof(hints));
1881 hints.ai_family = AF_UNSPEC;
1882 hints.ai_socktype = SOCK_STREAM;
1883 hints.ai_flags = AI_PASSIVE;
1885 if ((n = getaddrinfo(NULL, print_fmt(buf, sizeof(buf), "%i", port),
1886 &hints, &servinfo)) == -1) {
1887 log_write("getaddrinfo(): %s", gai_strerror(n));
1888 return FALSE;
1891 for(p = servinfo; p != NULL; p = p->ai_next) {
1892 if ((tcpSockFd = socket(p->ai_family, p->ai_socktype,
1893 p->ai_protocol)) == -1) {
1894 log_write("socket(): %s", strerror(errno));
1895 continue;
1898 n = 1;
1900 if (setsockopt(tcpSockFd, SOL_SOCKET, SO_REUSEADDR, &n,
1901 sizeof(int)) == -1) {
1902 log_write("setsockopt(): %s", strerror(errno));
1903 freeaddrinfo(servinfo);
1904 goto fail;
1907 if (bind(tcpSockFd, p->ai_addr, p->ai_addrlen) == -1) {
1908 close(tcpSockFd);
1909 log_write("bind(): %s", strerror(errno));
1910 continue;
1913 n++;
1914 break;
1917 freeaddrinfo(servinfo);
1919 if (!p) {
1920 log_write("%s", N_("could not bind"));
1921 goto fail;
1924 if (g_key_file_has_key(keyfileh, "global", "tcp_interface", NULL)) {
1925 gchar *tmp = get_key_file_string("global", "tcp_interface");
1927 if (setsockopt(tcpSockFd, SOL_SOCKET, SO_BINDTODEVICE, tmp, 1)
1928 == -1) {
1929 log_write("setsockopt(): %s", strerror(errno));
1930 g_free(tmp);
1931 goto fail;
1934 g_free(tmp);
1937 if (!initTlsParams())
1938 goto fail;
1940 if (listen(tcpSockFd, 0) == -1) {
1941 log_write("listen(): %s", strerror(errno));
1942 goto fail;
1945 tcpAcceptTid = pth_spawn(NULL, tcp_accept_thread, (void *)tcpSockFd);
1946 pth_yield(tcpAcceptTid);
1947 return TRUE;
1949 fail:
1950 deinitTlsParams();
1952 if (tcpSockFd != -1)
1953 close(tcpSockFd);
1955 tcpSockFd = -1;
1956 return FALSE;
1958 #endif
1960 int main(int argc, char *argv[])
1962 gint opt;
1963 struct sockaddr_un addr;
1964 struct passwd *pw = getpwuid(getuid());
1965 gchar buf[PATH_MAX];
1966 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
1967 gchar *socketarg = NULL;
1968 gchar *datadir = NULL;
1969 gboolean n;
1970 gchar *p;
1971 gchar **cache_push = NULL;
1972 guint64 iter = 0;
1973 gchar *import = NULL, *keyfile = NULL;
1974 guint64 cmd_iterations = -1;
1975 gint default_timeout;
1976 gboolean rcfile_spec = FALSE;
1977 gint estatus = EXIT_FAILURE;
1978 gint sockfd;
1979 gchar *outfile = NULL;
1980 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
1981 gint do_unlink = 1;
1982 gboolean secure = FALSE;
1983 guint ptotal = 0;
1984 gint background = 1;
1985 gchar *convert = NULL;
1986 #ifdef WITH_PINENTRY
1987 gboolean disable_pinentry = FALSE;
1988 #endif
1989 #ifdef WITH_GNUTLS
1990 struct assuan_io_hooks io_hooks = {read_hook, write_hook};
1991 #endif
1992 #if 0
1993 #ifndef DEBUG
1994 #ifdef HAVE_SETRLIMIT
1995 struct rlimit rl;
1997 rl.rlim_cur = rl.rlim_max = 0;
1999 if (setrlimit(RLIMIT_CORE, &rl) != 0)
2000 err(EXIT_FAILURE, "setrlimit()");
2001 #endif
2002 #endif
2003 #endif
2005 #ifdef ENABLE_NLS
2006 setlocale(LC_ALL, "");
2007 bindtextdomain("pwmd", LOCALEDIR);
2008 textdomain("pwmd");
2009 #endif
2011 #ifndef MEM_DEBUG
2012 xmem_init();
2013 #endif
2014 setup_gcrypt();
2015 gpg_err_init();
2016 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
2017 g_mem_set_vtable(&mtable);
2018 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
2019 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
2020 xmlInitMemory();
2021 #ifdef WITH_GNUTLS
2022 gnutls_global_set_mem_functions(xmalloc, xmalloc, gcry_SecureCheck,
2023 xrealloc, xfree);
2024 gnutls_global_init();
2025 gnutls_global_set_log_function(tls_log);
2026 gnutls_global_set_log_level(1);
2027 assuan_set_io_hooks(&io_hooks);
2028 #endif
2029 xmlInitGlobals();
2030 xmlInitParser();
2031 xmlXPathInit();
2032 g_snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
2034 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
2035 err(EXIT_FAILURE, "%s", buf);
2037 g_snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
2039 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
2040 err(EXIT_FAILURE, "%s", buf);
2042 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
2043 cmdline = TRUE;
2044 #ifdef WITH_GNUTLS
2045 tcpSockFd = -1;
2046 #endif
2048 while ((opt = getopt(argc, argv, "Po:C:nI:i:k:hvf:D")) != EOF) {
2049 switch (opt) {
2050 #ifdef WITH_PINENTRY
2051 case 'P':
2052 disable_pinentry = TRUE;
2053 break;
2054 #endif
2055 case 'o':
2056 outfile = optarg;
2057 break;
2058 case 'C':
2059 convert = optarg;
2060 break;
2061 case 'n':
2062 background = 0;
2063 break;
2064 case 'D':
2065 secure = TRUE;
2066 break;
2067 case 'I':
2068 import = optarg;
2069 break;
2070 case 'i':
2071 cmd_iterations = strtol(optarg, NULL, 10);
2072 break;
2073 case 'k':
2074 keyfile = optarg;
2075 break;
2076 case 'f':
2077 g_free(rcfile);
2078 rcfile = g_strdup(optarg);
2079 rcfile_spec = TRUE;
2080 break;
2081 case 'v':
2082 printf(N_("%s\nCopyright (c) %s\nReleased under the terms of the GPL v2. Use at your own risk.\n\nCompile time features:\n%s"), PACKAGE_STRING,
2083 PACKAGE_BUGREPORT,
2084 #ifdef WITH_PINENTRY
2085 "+WITH_PINENTRY\n"
2086 #else
2087 "-WITH_PINENTRY\n"
2088 #endif
2089 #ifdef WITH_QUALITY
2090 "+WITH_QUALITY\n"
2091 #else
2092 "-WITH_QUALITY\n"
2093 #endif
2094 #ifdef WITH_GNUTLS
2095 "+WITH_GNUTLS\n"
2096 #else
2097 "-WITH_GNUTLS\n"
2098 #endif
2099 #ifdef DEBUG
2100 "+DEBUG\n"
2101 #else
2102 "-DEBUG\n"
2103 #endif
2104 #ifdef MEM_DEBUG
2105 "+MEM_DEBUG\n"
2106 #else
2107 "-MEM_DEBUG\n"
2108 #endif
2110 exit(EXIT_SUCCESS);
2111 case 'h':
2112 default:
2113 usage(argv[0]);
2117 pth_mutex_init(&cn_mutex);
2118 pth_mutex_init(&cache_mutex);
2119 pth_mutex_init(&rcfile_mutex);
2120 #ifdef WITH_PINENTRY
2121 pth_mutex_init(&pin_mutex);
2122 #endif
2124 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
2125 exit(EXIT_FAILURE);
2127 #ifdef WITH_PINENTRY
2128 if (disable_pinentry == TRUE)
2129 g_key_file_set_boolean(keyfileh, "global", "enable_pinentry", FALSE);
2130 #endif
2132 if (g_key_file_has_key(keyfileh, "global", "syslog", NULL) == TRUE)
2133 log_syslog = g_key_file_get_boolean(keyfileh, "global", "syslog", NULL);
2135 if (log_syslog == TRUE)
2136 openlog("pwmd", LOG_NDELAY|LOG_PID, LOG_DAEMON);
2138 if (g_key_file_has_key(keyfileh, "global", "priority", NULL)) {
2139 iter = g_key_file_get_integer(keyfileh, "global", "priority", NULL);
2140 errno = 0;
2142 if (setpriority(PRIO_PROCESS, 0, iter) == -1) {
2143 log_write("setpriority(): %s", strerror(errno));
2144 goto do_exit;
2148 #ifdef HAVE_MLOCKALL
2149 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
2150 log_write("mlockall(): %s", strerror(errno));
2151 goto do_exit;
2153 #endif
2155 if (convert) {
2156 if (!outfile)
2157 usage(argv[0]);
2159 opt = convert_file(convert, keyfile, outfile);
2160 g_key_file_free(keyfileh);
2161 g_free(rcfile);
2162 exit(opt ? EXIT_FAILURE : EXIT_SUCCESS);
2165 if (import) {
2166 if (!outfile)
2167 usage(argv[0]);
2169 if (cmd_iterations == -1)
2170 cmd_iterations = (guint64)get_key_file_integer("global", "iterations");
2172 opt = xml_import(import, outfile, keyfile, cmd_iterations);
2173 g_key_file_free(keyfileh);
2174 g_free(rcfile);
2175 exit(opt == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
2178 g_key_file_set_list_separator(keyfileh, ',');
2180 if ((p = g_key_file_get_string(keyfileh, "global", "socket_path", NULL)) == NULL)
2181 errx(EXIT_FAILURE, N_("%s: socket_path not defined"), rcfile);
2183 if (*p == '~') {
2184 p++;
2185 g_snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
2186 g_free(p);
2187 socketarg = g_strdup(buf);
2189 else
2190 socketarg = p;
2192 if ((p = g_key_file_get_string(keyfileh, "global", "data_directory", NULL)) == NULL)
2193 errx(EXIT_FAILURE, N_("%s: data_directory not defined"), rcfile);
2195 datadir = expand_homedir(p);
2196 g_free(p);
2198 if (secure == FALSE && g_key_file_has_key(keyfileh, "global", "disable_list_and_dump", NULL) == TRUE) {
2199 n = g_key_file_get_boolean(keyfileh, "global", "disable_list_and_dump", NULL);
2200 disable_list_and_dump = n;
2202 else
2203 disable_list_and_dump = secure;
2205 if (g_key_file_has_key(keyfileh, "global", "cache_timeout", NULL) == TRUE)
2206 default_timeout = g_key_file_get_integer(keyfileh, "global", "cache_timeout", NULL);
2207 else
2208 default_timeout = -1;
2210 setup_logging(keyfileh);
2212 if (g_key_file_has_key(keyfileh, "global", "cache_push", NULL) == TRUE)
2213 cache_push = g_key_file_get_string_list(keyfileh, "global", "cache_push", NULL, NULL);
2215 if (argc != optind) {
2216 if (cache_push)
2217 ptotal = g_strv_length(cache_push);
2219 for (; optind < argc; optind++) {
2220 if (strv_printf(&cache_push, "%s", argv[optind]) == FALSE)
2221 errx(EXIT_FAILURE, "%s", strerror(ENOMEM));
2225 if (strchr(socketarg, '/') == NULL) {
2226 socketdir = g_get_current_dir();
2227 socketname = g_strdup(socketarg);
2228 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
2230 else {
2231 socketname = g_strdup(strrchr(socketarg, '/'));
2232 socketname++;
2233 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
2234 socketdir = g_strdup(socketarg);
2235 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
2238 if (chdir(datadir)) {
2239 log_write("%s: %s", datadir, strerror(errno));
2240 unlink(socketpath);
2241 goto do_exit;
2244 if (parse_rcfile_keys() == FALSE)
2245 goto do_exit;
2247 clear_rcfile_keys();
2250 * Set the cache entry for a file. Prompts for the password.
2252 if (cache_push) {
2253 for (opt = 0; cache_push[opt]; opt++)
2254 do_cache_push(cache_push[opt], NULL);
2256 g_strfreev(cache_push);
2257 log_write(background ? N_("Done. Daemonizing...") : N_("Done. Waiting for connections..."));
2261 * bind() doesn't like the full pathname of the socket or any non alphanum
2262 * characters so change to the directory where the socket is wanted then
2263 * create it then change to datadir.
2265 if (chdir(socketdir)) {
2266 log_write("%s: %s", socketdir, strerror(errno));
2267 goto do_exit;
2270 g_free(socketdir);
2272 if ((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
2273 log_write("socket(): %s", strerror(errno));
2274 goto do_exit;
2277 addr.sun_family = AF_UNIX;
2278 g_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
2280 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
2281 log_write("bind(): %s", strerror(errno));
2283 if (errno == EADDRINUSE)
2284 log_write(N_("Either there is another pwmd running or '%s' is a \n"
2285 "stale socket. Please remove it manually."), socketpath);
2287 do_unlink = 0;
2288 goto do_exit;
2291 if (g_key_file_has_key(keyfileh, "global", "socket_perms", NULL) == TRUE) {
2292 gchar *t = g_key_file_get_string(keyfileh, "global", "socket_perms", NULL);
2293 mode_t mode = strtol(t, NULL, 8);
2294 mode_t mask = umask(0);
2296 g_free(t);
2298 if (chmod(socketname, mode) == -1) {
2299 log_write("%s: %s", socketname, strerror(errno));
2300 close(sockfd);
2301 unlink(socketpath);
2302 umask(mask);
2303 goto do_exit;
2306 umask(mask);
2309 g_free(--socketname);
2311 if (chdir(datadir)) {
2312 log_write("%s: %s", datadir, strerror(errno));
2313 close(sockfd);
2314 unlink(socketpath);
2315 goto do_exit;
2318 g_free(datadir);
2320 if (listen(sockfd, 0) == -1) {
2321 log_write("listen(): %s", strerror(errno));
2322 goto do_exit;
2325 cmdline = FALSE;
2327 #ifdef WITH_GNUTLS
2328 if (startStopTcp(FALSE) == FALSE)
2329 goto do_exit;
2330 #endif
2332 if (background) {
2333 switch (fork()) {
2334 case -1:
2335 log_write("fork(): %s", strerror(errno));
2336 goto do_exit;
2337 case 0:
2338 close(0);
2339 close(1);
2340 close(2);
2341 setsid();
2342 break;
2343 default:
2344 exit(EXIT_SUCCESS);
2348 server_loop(sockfd, &socketpath);
2349 estatus = EXIT_SUCCESS;
2351 do_exit:
2352 if (socketpath && do_unlink) {
2353 unlink(socketpath);
2354 g_free(socketpath);
2357 #ifdef WITH_GNUTLS
2358 startStopTcp(TRUE);
2359 gnutls_global_deinit();
2360 #endif
2362 g_key_file_free(keyfileh);
2363 g_free(rcfile);
2364 xmlCleanupParser();
2365 xmlCleanupGlobals();
2367 if (estatus == EXIT_SUCCESS)
2368 log_write(N_("pwmd exiting normally"));
2370 #if defined(DEBUG) && !defined(MEM_DEBUG)
2371 xdump();
2372 #endif
2373 exit(estatus);