Thread attributes are copied in pth_spawn() so it's safe to reuse the
[pwmd.git] / src / pwmd.c
bloba8c4c125a8b82a34dbbde6b3917faf6a589de66c
1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
2 /*
3 Copyright (C) 2006-2007 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 <ctype.h>
25 #include <string.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 #include <signal.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <sys/wait.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <glib.h>
35 #include <glib/gprintf.h>
36 #include <gcrypt.h>
37 #include <sys/mman.h>
38 #include <termios.h>
39 #include <assert.h>
41 #ifdef HAVE_CONFIG_H
42 #include <config.h>
43 #endif
45 #ifdef HAVE_SETRLIMIT
46 #include <sys/time.h>
47 #include <sys/resource.h>
48 #endif
50 #ifdef HAVE_ZLIB_H
51 #include <zlib.h>
52 #endif
54 #ifndef MEM_DEBUG
55 #include "mem.h"
56 #endif
58 #include "xml.h"
59 #include "common.h"
60 #include "commands.h"
61 #include "pwmd_error.h"
62 #include "cache.h"
63 #include "pwmd.h"
65 static void clear_rcfile_key()
67 gsize n;
68 gchar **groups;
69 gchar **p;
71 groups = g_key_file_get_groups(keyfileh, &n);
73 for (p = groups; *p; p++) {
74 GError *error = NULL;
76 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE)
77 g_key_file_set_string(keyfileh, *p, "key", "");
80 g_strfreev(groups);
83 static void reload_rcfile()
85 log_write(N_("reloading configuration file '%s'"), rcfile);
86 g_key_file_free(keyfileh);
87 keyfileh = parse_rcfile(0);
88 clear_rcfile_key();
91 gpg_error_t send_syserror(assuan_context_t ctx, gint e)
93 gpg_error_t n = gpg_error_from_errno(e);
95 return assuan_process_done(ctx, assuan_set_error(ctx, n, gpg_strerror(n)));
98 gpg_error_t send_error(assuan_context_t ctx, gpg_error_t e)
100 gpg_err_code_t n = gpg_err_code(e);
101 gpg_error_t code = gpg_err_make(GPG_ERR_SOURCE_USER_1, n);
103 if (!e)
104 return assuan_process_done(ctx, 0);
106 if (!ctx) {
107 log_write("%s\n", pwmd_strerror(e));
108 return e;
111 if (n == EPWMD_LIBXML_ERROR) {
112 xmlErrorPtr xml_error = xmlGetLastError();
113 return assuan_process_done(ctx, assuan_set_error(ctx, code, xml_error->message));
116 return assuan_process_done(ctx, assuan_set_error(ctx, code, pwmd_strerror(e)));
119 void log_write(const gchar *fmt, ...)
121 gchar *args, *line;
122 va_list ap;
123 struct tm *tm;
124 time_t now;
125 gchar tbuf[21];
126 gint fd = -1;
128 if ((!logfile && !isatty(STDERR_FILENO)) || !fmt)
129 return;
131 if (logfile) {
132 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
133 warn(N_("logfile"));
134 return;
138 va_start(ap, fmt);
140 if (g_vasprintf(&args, fmt, ap) == -1) {
141 va_end(ap);
142 return;
145 va_end(ap);
146 time(&now);
147 tm = localtime(&now);
148 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
149 tbuf[sizeof(tbuf) - 1] = 0;
150 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
152 if (!line) {
153 if (logfile)
154 close(fd);
156 g_free(args);
157 return;
160 if (logfile) {
161 write(fd, line, strlen(line));
162 fsync(fd);
163 close(fd);
166 if (isatty(STDERR_FILENO)) {
167 fprintf(stderr, "%s", line);
168 fflush(stderr);
171 g_free(line);
172 g_free(args);
175 static void usage(gchar *pn)
177 g_printf(N_(
178 "Usage: %s [-hvDb] [-f <rcfile>] [-I <filename>] [file1] [...]\n"
179 " -b run as a background process\n"
180 " -f load the specified rcfile (~/.pwmd/config)\n"
181 " -I import an XML file and write the encrypted data to stdout\n"
182 " -D disable use of the LIST and DUMP commands\n"
183 " -v version\n"
184 " -h this help text\n"
185 ), pn);
186 exit(EXIT_SUCCESS);
189 #ifndef MEM_DEBUG
190 static int gcry_SecureCheck(const void *ptr)
192 return 1;
194 #endif
196 static void setup_gcrypt()
198 gcry_check_version(NULL);
200 #ifndef MEM_DEBUG
201 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
202 xfree);
203 #endif
205 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
206 NULL) != 0)
207 errx(EXIT_FAILURE, N_("Required AES cipher not supported by libgcrypt."));
209 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
210 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
214 * This is called after every Assuan command.
216 static void command_finalize(assuan_context_t ctx, gint error)
218 MUTEX_UNLOCK;
221 static assuan_context_t new_connection(gint fd)
223 gpg_error_t rc;
224 gchar ver[ASSUAN_LINELENGTH];
225 assuan_context_t ctx;
227 rc = assuan_init_socket_server_ext(&ctx, fd, 2);
229 if (rc)
230 goto fail;
232 g_snprintf(ver, sizeof(ver), "%s", PACKAGE_STRING);
233 assuan_set_hello_line(ctx, ver);
234 assuan_register_post_cmd_notify(ctx, command_finalize);
235 rc = register_commands(ctx);
237 if (rc)
238 goto fail;
240 rc = assuan_accept(ctx);
242 if (rc)
243 goto fail;
245 return ctx;
247 fail:
248 assuan_deinit_server(ctx);
249 log_write("%s", gpg_strerror(rc));
250 return NULL;
253 void send_cache_status_all()
255 guint i, t;
257 for (t = g_slist_length(cn_thread_list), i = 0; i < t; i++) {
258 struct client_thread_s *cn = g_slist_nth_data(cn_thread_list, i);
260 if (pth_self() == cn->tid)
261 continue;
263 pth_raise(cn->tid, SIGUSR2);
268 * Called every time a connection is made via pth_spawn(). This is the thread
269 * entry point.
271 static void *doit(void *data)
273 gint fd = (gint)data;
274 pth_event_t ev;
275 struct client_s *cl = g_malloc0(sizeof(struct client_s));
276 sigset_t set;
279 * This is a "child" thread. Don't catch any signals. Let the master
280 * thread take care of signals in server_loop().
282 sigemptyset(&set);
283 sigaddset(&set, SIGTERM);
284 sigaddset(&set, SIGINT);
285 sigaddset(&set, SIGUSR1);
286 sigaddset(&set, SIGHUP);
287 pth_sigmask(SIG_BLOCK, &set, NULL);
288 sigemptyset(&set);
289 sigaddset(&set, SIGUSR2);
290 sigaddset(&set, SIGPIPE);
292 cl->ctx = new_connection(fd);
293 cl->fd = fd;
295 if (!cl->ctx)
296 goto fail;
298 assuan_set_pointer(cl->ctx, cl);
300 #ifdef HAVE_MLOCKALL
301 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
302 log_write("mlockall(): %s", strerror(errno));
303 pth_exit(NULL);
305 #endif
307 send_cache_status(cl->ctx);
308 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE, cl->fd);
310 for (;;) {
311 gpg_error_t rc;
312 gint sig = 0;
314 pth_sigwait_ev(&set, &sig, ev);
316 if (sig > 0) {
317 switch (sig) {
318 case SIGUSR2:
319 send_cache_status(cl->ctx);
320 continue;
321 default:
322 break;
326 if (pth_event_occurred(ev)) {
327 rc = assuan_process_next(cl->ctx);
329 if (rc) {
330 if (gpg_err_code(rc) == GPG_ERR_EOF)
331 goto done;
333 log_write("assuan_process_next(): %s", gpg_strerror(rc));
334 rc = assuan_process_done(cl->ctx, rc);
336 if (rc) {
337 log_write("assuan_process_done(): %s", gpg_strerror(rc));
338 goto done;
344 done:
345 pth_event_free(ev, PTH_FREE_ALL);
347 if (cl->freed == FALSE)
348 cleanup_assuan(cl->ctx);
350 fail:
351 assuan_deinit_server(cl->ctx);
352 g_free(cl);
353 pth_exit(NULL);
354 return NULL;
358 * Make sure all settings are set to either the specified setting or a
359 * default.
361 static void set_rcfile_defaults(GKeyFile *kf)
363 gchar buf[PATH_MAX];
365 if (g_key_file_has_key(kf, "default", "socket_path", NULL) == FALSE) {
366 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
367 g_key_file_set_string(kf, "default", "socket_path", buf);
370 if (g_key_file_has_key(kf, "default", "data_directory", NULL) == FALSE) {
371 snprintf(buf, sizeof(buf), "~/.pwmd/data");
372 g_key_file_set_string(kf, "default", "data_directory", buf);
375 if (g_key_file_has_key(kf, "default", "log_path", NULL) == FALSE) {
376 snprintf(buf, sizeof(buf), "~/.pwmd/log");
377 g_key_file_set_string(kf, "default", "log_path", buf);
380 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == FALSE)
381 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
383 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == FALSE)
384 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
386 #ifdef HAVE_MLOCKALL
387 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == FALSE)
388 g_key_file_set_boolean(kf, "default", "disable_mlockall", TRUE);
389 #endif
391 if (g_key_file_has_key(kf, "default", "cache_timeout", NULL) == FALSE)
392 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
394 if (g_key_file_has_key(kf, "default", "iterations", NULL) == FALSE)
395 g_key_file_set_integer(kf, "default", "iterations", 0);
397 if (g_key_file_has_key(kf, "default", "disable_list_and_dump", NULL) == FALSE)
398 g_key_file_set_boolean(kf, "default", "disable_list_and_dump", FALSE);
400 if (g_key_file_has_key(kf, "default", "iteration_progress", NULL) == FALSE)
401 g_key_file_set_integer(kf, "default", "iteration_progress", 0);
403 if (g_key_file_has_key(kf, "default", "compression_level", NULL) == FALSE)
404 g_key_file_set_integer(kf, "default", "compression_level", 6);
406 if (g_key_file_has_key(kf, "default", "recursion_depth", NULL) == FALSE)
407 g_key_file_set_integer(kf, "default", "recursion_depth", DEFAULT_RECURSION_DEPTH);
409 if (g_key_file_has_key(kf, "default", "zlib_bufsize", NULL) == FALSE)
410 g_key_file_set_integer(kf, "default", "zlib_bufsize", DEFAULT_ZLIB_BUFSIZE);
412 zlib_bufsize = g_key_file_get_integer(kf, "default", "zlib_bufsize", NULL);
414 max_recursion_depth = g_key_file_get_integer(kf, "default", "recursion_depth", NULL);
415 disable_list_and_dump = g_key_file_get_boolean(kf, "default", "disable_list_and_dump", NULL);
417 #ifdef HAVE_MLOCKALL
418 disable_mlock = g_key_file_get_boolean(kf, "default", "disable_mlockall", NULL);
419 #endif
422 static GKeyFile *parse_rcfile(int cmdline)
424 GKeyFile *kf = g_key_file_new();
425 GError *error = NULL;
427 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &error) == FALSE) {
428 log_write("%s: %s", rcfile, error->message);
430 if (cmdline)
431 exit(EXIT_FAILURE);
433 if (error->code == G_FILE_ERROR_NOENT) {
434 g_clear_error(&error);
435 set_rcfile_defaults(kf);
436 return kf;
439 g_clear_error(&error);
440 return NULL;
442 else
443 set_rcfile_defaults(kf);
445 return kf;
448 static gchar *get_password(const gchar *prompt)
450 gchar buf[LINE_MAX], *p;
451 struct termios told, tnew;
452 gchar *key;
454 if (tcgetattr(STDIN_FILENO, &told) == -1)
455 err(EXIT_FAILURE, "tcgetattr()");
457 memcpy(&tnew, &told, sizeof(struct termios));
458 tnew.c_lflag &= ~(ECHO);
459 tnew.c_lflag |= ICANON|ECHONL;
461 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
462 tcsetattr(STDIN_FILENO, TCSANOW, &told);
463 err(EXIT_FAILURE, "tcsetattr()");
466 fprintf(stderr, "%s", prompt);
468 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
469 tcsetattr(STDIN_FILENO, TCSANOW, &told);
470 return NULL;
473 tcsetattr(STDIN_FILENO, TCSANOW, &told);
474 p[strlen(p) - 1] = 0;
476 if (!p || !*p)
477 return NULL;
479 key = gcry_malloc(strlen(p) + 1);
480 sprintf(key, "%s", p);
481 memset(&buf, 0, sizeof(buf));
482 return key;
485 static gboolean do_try_xml_decrypt(const gchar *filename, guchar *key)
487 int fd;
488 struct stat st;
489 gpg_error_t error;
491 if ((fd = open_file(filename, &st)) == -1) {
492 warn("%s", filename);
493 return FALSE;
496 if (st.st_size == 0) {
497 warnx(N_("%s: skipping empty file"), filename);
498 close(fd);
499 return FALSE;
502 error = try_xml_decrypt(NULL, fd, st, key);
503 close(fd);
504 return error ? FALSE : TRUE;
507 static gboolean get_input(const gchar *filename, guchar *key)
509 gint try = 0;
510 gchar *password;
511 gchar *prompt;
513 prompt = g_strdup_printf(N_("Password for '%s': "), filename);
515 again:
516 if ((password = get_password(prompt)) == NULL) {
517 warnx(N_("%s: skipping file"), filename);
518 g_free(prompt);
519 return FALSE;
522 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
523 gcry_free(password);
525 if (do_try_xml_decrypt(filename, key) == FALSE) {
526 if (try++ == 2) {
527 warnx(N_("%s: invalid password, skipping"), filename);
528 g_free(prompt);
529 return FALSE;
531 else {
532 warnx(N_("%s: invalid password"), filename);
533 goto again;
537 g_free(prompt);
538 return TRUE;
541 static gboolean xml_import(const gchar *filename, gint iter)
543 xmlDocPtr doc;
544 gint fd;
545 struct stat st;
546 gint len;
547 xmlChar *xmlbuf;
548 xmlChar *xml;
549 gchar *key = NULL;
550 gchar *key2 = NULL;
551 guchar shakey[gcrykeysize];
552 gcry_cipher_hd_t gh;
553 gpg_error_t error;
554 gint level;
555 glong outsize;
556 gpointer outbuf;
557 gint zerror;
559 #ifdef HAVE_MLOCKALL
560 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1)
561 err(EXIT_FAILURE, "mlockall()");
562 #endif
564 if (stat(filename, &st) == -1) {
565 warn("%s", filename);
566 return FALSE;
569 #ifndef MEM_DEBUG
570 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
571 xmlInitMemory();
572 #endif
574 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
575 send_error(NULL, gcryerrno);
576 gcry_cipher_close(gh);
577 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
578 return FALSE;
581 if ((key = get_password(N_("New password: "))) == NULL) {
582 fprintf(stderr, "%s\n", N_("Invalid password."));
583 gcry_cipher_close(gh);
584 return FALSE;
587 if ((key2 = get_password(N_("Verify password: "))) == NULL) {
588 fprintf(stderr, "%s\n", N_("Passwords do not match."));
589 gcry_free(key);
590 gcry_cipher_close(gh);
591 return FALSE;
594 if (g_utf8_collate(key, key2) != 0) {
595 fprintf(stderr, "%s\n", N_("Passwords do not match."));
596 gcry_free(key);
597 gcry_free(key2);
598 gcry_cipher_close(gh);
599 return FALSE;
602 gcry_free(key2);
604 if ((fd = open(filename, O_RDONLY)) == -1) {
605 gcry_free(key);
606 warn("%s", filename);
607 gcry_cipher_close(gh);
608 return FALSE;
611 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
612 gcry_free(key);
613 close(fd);
614 log_write("%s", strerror(ENOMEM));
615 gcry_cipher_close(gh);
616 return FALSE;
619 if (read(fd, xmlbuf, st.st_size) == -1) {
620 error = errno;
621 close(fd);
622 gcry_free(key);
623 gcry_cipher_close(gh);
624 errno = error;
625 err(EXIT_FAILURE, "read()");
628 close(fd);
629 xmlbuf[st.st_size] = 0;
632 * Make sure the document validates.
634 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
635 log_write("xmlReadDoc()");
636 close(fd);
637 gcry_free(key);
638 gcry_free(xmlbuf);
639 gcry_cipher_close(gh);
640 return FALSE;
643 gcry_free(xmlbuf);
644 xmlDocDumpMemory(doc, &xml, &len);
645 xmlFreeDoc(doc);
647 level = get_key_file_integer(filename, "compression_level");
649 if (level < 0)
650 level = 0;
652 if (do_compress(NULL, level, xml, len, &outbuf, &outsize, &zerror) == FALSE) {
653 memset(shakey, 0, sizeof(shakey));
654 gcry_free(xml);
656 if (zerror == Z_MEM_ERROR)
657 warnx("%s", strerror(ENOMEM));
658 else
659 warnx("do_compress() failed");
661 gcry_cipher_close(gh);
662 return FALSE;
664 else {
665 gcry_free(xml);
666 xml = outbuf;
667 len = outsize;
670 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
671 gcry_free(key);
672 error = do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter);
673 gcry_cipher_close(gh);
675 if (error) {
676 memset(shakey, 0, sizeof(shakey));
677 warnx("%s", gpg_strerror(error));
678 return FALSE;
681 memset(shakey, 0, sizeof(shakey));
682 return TRUE;
685 gchar *get_key_file_string(const gchar *section, const gchar *what)
687 gchar *val = NULL;
688 GError *gerror = NULL;
690 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
691 val = g_key_file_get_string(keyfileh, section, what, &gerror);
693 if (gerror) {
694 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
695 g_clear_error(&gerror);
698 else {
699 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
700 val = g_key_file_get_string(keyfileh, "default", what, &gerror);
702 if (gerror) {
703 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
704 g_clear_error(&gerror);
709 return val;
712 gint get_key_file_integer(const gchar *section, const gchar *what)
714 gint val = -1;
715 GError *gerror = NULL;
717 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
718 val = g_key_file_get_integer(keyfileh, section, what, &gerror);
720 if (gerror) {
721 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
722 g_clear_error(&gerror);
725 else {
726 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
727 val = g_key_file_get_integer(keyfileh, "default", what, &gerror);
729 if (gerror) {
730 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
731 g_clear_error(&gerror);
736 return val;
739 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
741 gboolean val = FALSE;
742 GError *gerror = NULL;
744 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
745 val = g_key_file_get_boolean(keyfileh, section, what, &gerror);
747 if (gerror) {
748 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
749 g_clear_error(&gerror);
752 else {
753 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
754 val = g_key_file_get_boolean(keyfileh, "default", what, &gerror);
756 if (gerror) {
757 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
758 g_clear_error(&gerror);
763 return val;
766 gchar *expand_homedir(gchar *str)
768 gchar *p = str;
770 if (*p++ == '~')
771 return g_strdup_printf("%s%s", g_get_home_dir(), p);
773 return g_strdup(str);
776 static gchar *_getline(const gchar *file)
778 FILE *fp;
779 gchar buf[LINE_MAX], *p;
780 gchar *str = NULL;
782 if ((fp = fopen(file, "r")) == NULL) {
783 warn("%s", file);
784 return NULL;
787 if ((p = fgets(buf, sizeof(buf), fp)) == NULL) {
788 warnx(N_("%s: empty file?"), file);
789 return NULL;
792 fclose(fp);
794 if (buf[strlen(buf) - 1] == '\n')
795 buf[strlen(buf) - 1] = 0;
797 str = gcry_malloc(strlen(p) + 1);
798 memcpy(str, p, strlen(p));
799 str[strlen(p)] = 0;
800 memset(&buf, 0, sizeof(buf));
801 return str;
804 static gboolean parse_keyfile_key()
806 gsize n;
807 gchar **groups;
808 gchar **p;
809 gchar *str;
811 groups = g_key_file_get_groups(keyfileh, &n);
813 for (p = groups; *p; p++) {
814 GError *error = NULL;
816 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE) {
817 str = g_key_file_get_string(keyfileh, *p, "key", &error);
819 if (!str) {
820 if (error) {
821 warnx("%s", error->message);
822 g_clear_error(&error);
825 continue;
828 do_cache_push(*p, str);
829 g_free(str);
830 continue;
833 if (error) {
834 warnx("%s", error->message);
835 g_clear_error(&error);
836 continue;
839 if (g_key_file_has_key(keyfileh, *p, "key_file", &error) == TRUE) {
840 gchar *t;
841 gchar *file = g_key_file_get_string(keyfileh, *p, "key_file", &error);
843 if (!file) {
844 if (error) {
845 warnx("%s", error->message);
846 g_clear_error(&error);
849 continue;
852 t = expand_homedir(file);
853 g_free(file);
854 file = t;
856 if ((str = _getline(file)) == NULL) {
857 g_free(file);
858 continue;
861 g_free(file);
862 do_cache_push(*p, str);
863 gcry_free(str);
864 continue;
867 if (error) {
868 warnx("%s", error->message);
869 g_clear_error(&error);
873 g_strfreev(groups);
874 return TRUE;
877 static gboolean do_cache_push(const gchar *filename, const gchar *password)
879 guchar *md5file;
880 guchar *key;
881 gint timeout;
882 const gchar *p = filename;
884 while (isspace(*p))
885 p++;
887 if (!*p)
888 return FALSE;
890 if (valid_filename(p) == FALSE) {
891 warnx(N_("%s: invalid characters in filename"), p);
892 return FALSE;
895 md5file = gcry_malloc(16);
896 key = gcry_malloc(gcrykeysize);
897 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
899 if (cache_iscached(md5file) == TRUE) {
900 warnx(N_("%s: file already cached, skipping"), p);
901 gcry_free(md5file);
902 gcry_free(key);
903 return FALSE;
906 if (access(p, R_OK|W_OK) != 0) {
907 gcry_free(md5file);
908 gcry_free(key);
910 if (errno != ENOENT) {
911 warn("%s", p);
912 return FALSE;
915 warn("%s", p);
916 return TRUE;
919 if (!password) {
920 if (get_input(p, key) == FALSE) {
921 gcry_free(key);
922 gcry_free(md5file);
923 return FALSE;
926 else {
927 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
929 if (do_try_xml_decrypt(filename, key) == FALSE) {
930 warnx(N_("%s: invalid password, skipping"), filename);
931 gcry_free(key);
932 gcry_free(md5file);
933 return FALSE;
937 if (cache_add_file(md5file, key) == FALSE) {
938 warnx("%s: %s", p, pwmd_strerror(EPWMD_MAX_SLOTS));
939 gcry_free(key);
940 gcry_free(md5file);
941 return FALSE;
944 timeout = get_key_file_integer(p, "cache_timeout");
945 cache_set_timeout(md5file, timeout);
946 warnx(N_("%s: file added to the cache"), filename);
947 gcry_free(key);
948 gcry_free(md5file);
949 return TRUE;
952 static GSList *remove_connection(GSList *list, struct client_thread_s *cn)
954 gpointer value;
956 pth_join(cn->tid, &value);
957 close(cn->fd);
958 pth_event_isolate(cn->ev);
959 pth_event_free(cn->ev, PTH_FREE_THIS);
960 log_write(N_("client exited: fd=%i"), cn->fd);
961 list = g_slist_remove(list, cn);
962 g_free(cn);
963 return list;
967 * Can't pth_event_concat() to an empty event.
969 static int event_ring_hack(void *data)
971 return FALSE;
975 * See if any thread has entered the DEAD queue and remove it.
977 static GSList *cleanup_dead_queue(GSList *threads, pth_event_t tid_events)
979 guint n, i;
981 for (n = g_slist_length(threads), i = 0; i < n; i++) {
982 struct client_thread_s *cn = g_slist_nth_data(threads, i);
984 if (pth_event_occurred(cn->ev)) {
985 threads = remove_connection(threads, cn);
986 return cleanup_dead_queue(threads, tid_events);
989 pth_event_concat(tid_events, cn->ev, NULL);
992 return threads;
995 static void *socket_thread(void *arg)
997 pth_event_t ev;
998 gint sockfd = (gint)arg;
999 pth_attr_t attr;
1002 * Thread priority is inherited from the calling thread. This thread is
1003 * PTH_PRIO_MAX. Keep the "child" threads at standard priority.
1005 attr = pth_attr_new();
1006 pth_attr_init(attr);
1007 pth_attr_set(attr, PTH_ATTR_PRIO, PTH_PRIO_STD);
1009 ev = pth_event(PTH_EVENT_FD|PTH_UNTIL_FD_READABLE|PTH_UNTIL_FD_EXCEPTION, sockfd);
1011 for (;;) {
1012 socklen_t slen = sizeof(struct sockaddr_un);
1013 struct sockaddr_un raddr;
1014 gint fd = -1;
1015 gpointer value;
1017 if (pth_wait(ev) && pth_event_occurred(ev)) {
1018 if ((fd = pth_accept(sockfd, (struct sockaddr *)&raddr, &slen)) == -1) {
1019 if (errno != EAGAIN) {
1020 if (!quit) // probably EBADF
1021 log_write("accept(): %s", strerror(errno));
1023 break;
1028 if (fd >= 0) {
1029 pth_t tid;
1030 struct client_thread_s *new;
1032 log_write(N_("new connection: fd=%i"), fd);
1033 tid = pth_spawn(attr, doit, (void *)fd);
1035 if (!tid) {
1036 log_write(N_("pth_spawn() failed"));
1037 continue;
1040 new = g_malloc0(sizeof(struct client_thread_s));
1042 if (!new) {
1043 log_write("%s", strerror(ENOMEM));
1044 pth_cancel(tid);
1045 pth_join(tid, &value);
1046 continue;
1049 new->tid = tid;
1050 new->fd = fd;
1051 new->ev = pth_event(PTH_EVENT_TID|PTH_UNTIL_TID_DEAD, tid);
1052 pth_mutex_acquire(&cn_mutex, FALSE, NULL);
1053 pth_event_concat(cn_events, new->ev, NULL);
1054 cn_thread_list = g_slist_append(cn_thread_list, new);
1055 pth_mutex_release(&cn_mutex);
1059 pth_event_free(ev, PTH_FREE_THIS);
1060 pth_attr_destroy(attr);
1061 pth_exit(PTH_CANCELED);
1062 return NULL;
1065 static void *cleanup_thread(void *arg)
1067 cn_events = pth_event(PTH_EVENT_TIME, pth_timeout(0, 500000));
1069 for (;;) {
1070 if (pth_wait(cn_events) && pth_event_occurred(cn_events)) {
1071 pth_mutex_acquire(&cn_mutex, FALSE, NULL);
1072 pth_event_isolate(cn_events);
1073 pth_event_free(cn_events, PTH_FREE_THIS);
1074 cn_events = pth_event(PTH_EVENT_TIME, pth_timeout(0, 500000));
1075 cn_thread_list = cleanup_dead_queue(cn_thread_list, cn_events);
1076 pth_mutex_release(&cn_mutex);
1080 pth_event_isolate(cn_events);
1081 pth_event_free(cn_events, PTH_FREE_THIS);
1082 return NULL;
1085 static void *adjust_cache_time_thread(void *arg)
1087 MUTEX_LOCK(NULL, PTH_RWLOCK_RW, NULL);
1088 cache_adjust_timer();
1089 MUTEX_UNLOCK;
1090 return NULL;
1093 static void server_loop(int sockfd)
1095 pth_t socket_tid, cleanup_tid;
1096 guint n, i;
1097 sigset_t set;
1098 gint n_clients = 0;
1099 pth_attr_t attr;
1100 pth_event_t timeout_event;
1101 gpointer value;
1103 pth_mutex_init(&cn_mutex);
1104 log_write(N_("%s started"), PACKAGE_STRING);
1106 sigemptyset(&set);
1107 sigaddset(&set, SIGPIPE);
1108 sigaddset(&set, SIGTERM);
1109 sigaddset(&set, SIGINT);
1110 sigaddset(&set, SIGUSR1);
1111 sigaddset(&set, SIGUSR2);
1112 sigaddset(&set, SIGHUP);
1115 * pth_accept() doesn't timeout so we set the socket file descriptor to
1116 * non-blocking.
1118 pth_fdmode(sockfd, PTH_FDMODE_NONBLOCK);
1120 attr = pth_attr_new();
1121 pth_attr_init(attr);
1122 pth_attr_set(attr, PTH_ATTR_PRIO, PTH_PRIO_MAX);
1123 socket_tid = pth_spawn(attr, socket_thread, (void *)sockfd);
1124 pth_attr_destroy(attr);
1125 attr = pth_attr_new();
1126 pth_attr_init(attr);
1127 pth_attr_set(attr, PTH_ATTR_PRIO, PTH_PRIO_MAX);
1128 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
1129 pth_attr_set(attr, PTH_ATTR_CANCEL_STATE, PTH_CANCEL_ASYNCHRONOUS);
1130 cleanup_tid = pth_spawn(attr, cleanup_thread, NULL);
1131 pth_attr_init(attr);
1134 * For the cache_timeout configuration parameter. This replaces the old
1135 * SIGALRM stuff and is safer.
1137 timeout_event = pth_event(PTH_EVENT_TIME, pth_timeout(1, 0));
1138 attr = pth_attr_new();
1139 pth_attr_init(attr);
1140 pth_attr_set(attr, PTH_ATTR_JOINABLE, FALSE);
1141 pth_attr_set(attr, PTH_ATTR_PRIO, PTH_PRIO_MIN);
1143 do {
1144 gint sig = 0;
1146 pth_sigwait_ev(&set, &sig, timeout_event);
1148 if (pth_event_occurred(timeout_event)) {
1150 * The timer event has expired. Update the file cache. When the
1151 * cache mutex is locked and the timer expires again, the threads
1152 * will stack.
1154 pth_spawn(attr, adjust_cache_time_thread, NULL);
1155 pth_event_free(timeout_event, PTH_FREE_THIS);
1156 timeout_event = pth_event(PTH_EVENT_TIME, pth_timeout(1, 0));
1159 if (sig > 0) {
1160 /* Caught a signal. */
1161 switch (sig) {
1162 case SIGUSR1:
1163 reload_rcfile();
1164 break;
1165 case SIGABRT:
1166 MUTEX_LOCK(NULL, PTH_RWLOCK_RW, NULL);
1167 cache_clear(NULL, 2);
1168 MUTEX_UNLOCK;
1169 #ifndef MEM_DEBUG
1170 xpanic();
1171 #endif
1172 exit(EXIT_FAILURE);
1173 case SIGHUP:
1174 MUTEX_LOCK(NULL, PTH_RWLOCK_RW, NULL);
1175 log_write(N_("clearing file cache"));
1176 cache_clear(NULL, 2);
1177 MUTEX_UNLOCK;
1178 break;
1179 case SIGPIPE:
1180 case SIGUSR2:
1181 break;
1182 default:
1183 quit = 1;
1184 shutdown(sockfd, SHUT_RDWR);
1185 close(sockfd);
1186 pth_join(socket_tid, &value);
1187 break;
1190 } while (!quit);
1193 * We're out of the main server loop. This happens when a signal was sent
1194 * to terminate the daemon. We'll wait for all clients to disconnect
1195 * before exiting and ignore any following signals.
1197 pth_attr_destroy(attr);
1198 pth_event_free(timeout_event, PTH_FREE_THIS);
1200 n = pth_ctrl(PTH_CTRL_GETTHREADS);
1202 /* 2 because the cleanup thread still exists, plus self. */
1203 if (n > 2)
1204 log_write(N_("waiting for all threads to terminate"));
1206 while (n > 2) {
1207 gint t;
1208 pth_event_t events;
1210 if (n != n_clients) {
1211 log_write(N_("%i clients remain"), n-2);
1212 n_clients = n;
1215 events = pth_event(PTH_EVENT_FUNC, event_ring_hack, NULL, pth_timeout(1, 0));
1216 pth_mutex_acquire(&cn_mutex, FALSE, NULL);
1218 for (t = g_slist_length(cn_thread_list), i = 0; i < t; i++) {
1219 struct client_thread_s *cn = g_slist_nth_data(cn_thread_list, i);
1221 pth_event_concat(events, cn->ev, NULL);
1224 pth_mutex_release(&cn_mutex);
1225 pth_wait(events);
1226 pth_yield(cleanup_tid);
1227 pth_event_isolate(events);
1228 pth_event_free(events, PTH_FREE_THIS);
1229 n = pth_ctrl(PTH_CTRL_GETTHREADS);
1232 pth_cancel(cleanup_tid);
1235 int main(int argc, char *argv[])
1237 gint opt;
1238 struct sockaddr_un addr;
1239 struct passwd *pw = getpwuid(getuid());
1240 gchar buf[PATH_MAX];
1241 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
1242 gchar *socketarg = NULL;
1243 gchar *datadir = NULL;
1244 gboolean n;
1245 gchar *p;
1246 gchar **cache_push = NULL;
1247 gint iter = 0;
1248 gchar *import = NULL;
1249 gint default_timeout;
1250 gint rcfile_spec = 0;
1251 gint estatus = EXIT_FAILURE;
1252 gint sockfd;
1253 #ifndef MEM_DEBUG
1254 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
1255 #endif
1256 gint do_unlink = 1;
1257 gboolean secure = FALSE;
1258 guint ptotal = 0;
1259 gint background = 0;
1260 sigset_t set;
1261 #if 0
1262 #ifndef DEBUG
1263 #ifdef HAVE_SETRLIMIT
1264 struct rlimit rl;
1266 rl.rlim_cur = rl.rlim_max = 0;
1268 if (setrlimit(RLIMIT_CORE, &rl) != 0)
1269 err(EXIT_FAILURE, "setrlimit()");
1270 #endif
1271 #endif
1272 #endif
1274 #ifdef ENABLE_NLS
1275 setlocale(LC_ALL, "");
1276 bindtextdomain("pwmd", LOCALEDIR);
1277 textdomain("pwmd");
1278 #endif
1280 gpg_err_init();
1281 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
1282 #ifndef MEM_DEBUG
1283 g_mem_set_vtable(&mtable);
1284 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
1285 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
1286 xmlInitMemory();
1287 #endif
1288 pth_init();
1289 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
1291 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1292 err(EXIT_FAILURE, "%s", buf);
1294 snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
1296 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1297 err(EXIT_FAILURE, "%s", buf);
1299 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
1301 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
1302 err(EXIT_FAILURE, "sysconf()");
1304 cache_size = page_size;
1306 while ((opt = getopt(argc, argv, "bI:hvf:D")) != EOF) {
1307 switch (opt) {
1308 case 'b':
1309 background = 1;
1310 break;
1311 case 'D':
1312 secure = TRUE;
1313 break;
1314 case 'I':
1315 import = optarg;
1316 break;
1317 case 'f':
1318 g_free(rcfile);
1319 rcfile = g_strdup(optarg);
1320 rcfile_spec = 1;
1321 break;
1322 case 'v':
1323 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
1324 exit(EXIT_SUCCESS);
1325 case 'h':
1326 default:
1327 usage(argv[0]);
1331 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
1332 exit(EXIT_FAILURE);
1334 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1335 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1337 setup_gcrypt();
1339 if (import) {
1340 opt = xml_import(import, iter);
1341 g_key_file_free(keyfileh);
1342 g_free(rcfile);
1343 exit(opt == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1346 g_key_file_set_list_separator(keyfileh, ',');
1348 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
1349 errx(EXIT_FAILURE, N_("%s: socket_path not defined"), rcfile);
1351 if (*p == '~') {
1352 p++;
1353 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1354 g_free(p);
1355 socketarg = g_strdup(buf);
1357 else
1358 socketarg = p;
1360 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1361 errx(EXIT_FAILURE, N_("%s: data_directory not defined"), rcfile);
1363 datadir = expand_homedir(p);
1364 g_free(p);
1366 if (secure == FALSE && g_key_file_has_key(keyfileh, "default", "disable_list_and_dump", NULL) == TRUE) {
1367 n = g_key_file_get_boolean(keyfileh, "default", "disable_list_and_dump", NULL);
1368 disable_list_and_dump = n;
1370 else
1371 disable_list_and_dump = secure;
1373 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1374 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1375 else
1376 default_timeout = -1;
1378 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1379 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1381 if (cache_size < page_size || cache_size % page_size)
1382 errx(EXIT_FAILURE, N_("cache size must be in multiples of %li"), page_size);
1385 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1386 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1387 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1389 if (n == TRUE) {
1390 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1392 if (*p == '~') {
1393 p++;
1394 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1395 g_free(p);
1396 logfile = g_strdup(buf);
1398 else
1399 logfile = p;
1404 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1405 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1407 if (argc != optind) {
1408 if (cache_push)
1409 ptotal = g_strv_length(cache_push);
1411 for (; optind < argc; optind++) {
1412 if (strv_printf(&cache_push, "%s", argv[optind]) == FALSE)
1413 errx(EXIT_FAILURE, "%s", strerror(ENOMEM));
1417 if (strchr(socketarg, '/') == NULL) {
1418 socketdir = g_get_current_dir();
1419 socketname = g_strdup(socketarg);
1420 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1422 else {
1423 socketname = g_strdup(strrchr(socketarg, '/'));
1424 socketname++;
1425 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1426 socketdir = g_strdup(socketarg);
1427 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1430 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1431 #ifdef MMAP_ANONYMOUS
1432 MAP_PRIVATE|MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {
1433 #else
1434 MAP_PRIVATE|MAP_ANON, -1, 0)) == MAP_FAILED) {
1435 #endif
1436 err(EXIT_FAILURE, "mmap()");
1439 if (mlock(shm_data, cache_size) == -1)
1440 warn("mlock()");
1442 memset(shm_data, 0, cache_size);
1444 if (chdir(datadir)) {
1445 warn("%s", datadir);
1446 unlink(socketpath);
1447 goto do_exit;
1450 if (parse_keyfile_key() == FALSE)
1451 goto do_exit;
1453 clear_rcfile_key();
1456 * Set the cache entry for a file. Prompts for the password.
1458 if (cache_push) {
1459 for (opt = 0; cache_push[opt]; opt++)
1460 do_cache_push(cache_push[opt], NULL);
1462 g_strfreev(cache_push);
1463 warnx(background ? N_("Done. Daemonizing...") : N_("Done. Waiting for connections..."));
1467 * bind() doesn't like the full pathname of the socket or any non alphanum
1468 * characters so change to the directory where the socket is wanted then
1469 * create it then change to datadir.
1471 if (chdir(socketdir)) {
1472 warn("%s", socketdir);
1473 goto do_exit;
1476 g_free(socketdir);
1478 if ((sockfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1479 warn("socket()");
1480 goto do_exit;
1483 addr.sun_family = AF_UNIX;
1484 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1486 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
1487 warn("bind()");
1489 if (errno == EADDRINUSE)
1490 warnx(N_("Either there is another pwmd running or '%s' is a \n"
1491 "stale socket. Please remove it manually."), socketpath);
1493 do_unlink = 0;
1494 goto do_exit;
1497 if (g_key_file_has_key(keyfileh, "default", "socket_perms", NULL) == TRUE) {
1498 gchar *t = g_key_file_get_string(keyfileh, "default", "socket_perms", NULL);
1499 mode_t mode = strtol(t, NULL, 8);
1500 mode_t mask = umask(0);
1502 g_free(t);
1504 if (chmod(socketname, mode) == -1) {
1505 warn("%s", socketname);
1506 close(sockfd);
1507 unlink(socketpath);
1508 umask(mask);
1509 goto do_exit;
1512 umask(mask);
1515 g_free(--socketname);
1517 if (chdir(datadir)) {
1518 warn("%s", datadir);
1519 close(sockfd);
1520 unlink(socketpath);
1521 goto do_exit;
1524 g_free(datadir);
1525 pth_rwlock_init(&cache_rwlock);
1527 if (listen(sockfd, 0) == -1) {
1528 warn("listen()");
1529 goto do_exit;
1532 if (background) {
1533 switch (fork()) {
1534 case -1:
1535 warn("fork()");
1536 goto do_exit;
1537 case 0:
1538 close(0);
1539 close(1);
1540 close(2);
1541 setsid();
1542 break;
1543 default:
1544 exit(EXIT_SUCCESS);
1548 sigemptyset(&set);
1549 sigaddset(&set, SIGTERM);
1550 sigaddset(&set, SIGINT);
1551 sigaddset(&set, SIGUSR1);
1552 sigaddset(&set, SIGUSR2);
1553 sigaddset(&set, SIGHUP);
1554 sigaddset(&set, SIGPIPE);
1555 pth_sigmask(SIG_BLOCK, &set, NULL);
1556 server_loop(sockfd);
1557 estatus = EXIT_SUCCESS;
1559 do_exit:
1560 if (socketpath && do_unlink) {
1561 unlink(socketpath);
1562 g_free(socketpath);
1565 g_key_file_free(keyfileh);
1566 g_free(rcfile);
1567 memset(shm_data, 0, cache_size);
1569 if (munmap(shm_data, cache_size) == -1)
1570 log_write("munmap(): %s", strerror(errno));
1572 if (estatus == EXIT_SUCCESS)
1573 log_write(N_("pwmd exiting normally"));
1575 #if defined(DEBUG) && !defined(MEM_DEBUG)
1576 xdump();
1577 #endif
1578 pth_kill();
1579 exit(estatus);