The file cache is now locked with a mutex to prevent other processes
[pwmd.git] / src / pwmd.c
blob2730ae539d986f48347a0ce75ee436aa0b221051
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, int e)
93 gpg_error_t n = gpg_error_from_errno(e);
95 return 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 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_set_error(ctx, code, xml_error->message);
116 return 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 catchsig(gint sig)
177 gint status;
179 if (sig != SIGALRM && sig != SIGCHLD)
180 log_write(N_("caught signal %i (%s)"), sig, strsignal(sig));
182 switch (sig) {
183 case SIGUSR1:
184 reload_rcfile();
185 break;
186 case SIGABRT:
187 cache_clear(NULL, 2);
188 #ifndef MEM_DEBUG
189 xpanic();
190 #endif
191 exit(EXIT_FAILURE);
192 case SIGALRM:
193 cache_adjust_timer();
194 alarm(1);
195 break;
196 case SIGCHLD:
197 waitpid(-1, &status, 0);
199 if (WIFEXITED(status) || WIFSIGNALED(status))
200 clients--;
202 break;
203 case SIGHUP:
204 log_write(N_("clearing file cache"));
205 cache_clear(NULL, 2);
206 break;
207 default:
208 signal(SIGALRM, SIG_IGN);
209 quit = 1;
210 shutdown(sfd, SHUT_RDWR);
211 close(sfd);
212 break;
216 static void usage(gchar *pn)
218 g_printf(N_(
219 "Usage: %s [-hvDb] [-f <rcfile>] [-I <filename>] [file1] [...]\n"
220 " -b run as a background process\n"
221 " -f load the specified rcfile (~/.pwmd/config)\n"
222 " -I import an XML file and write the encrypted data to stdout\n"
223 " -D disable use of the LIST and DUMP commands\n"
224 " -v version\n"
225 " -h this help text\n"
226 ), pn);
227 exit(EXIT_SUCCESS);
230 #ifndef MEM_DEBUG
231 static int gcry_SecureCheck(const void *ptr)
233 return 1;
235 #endif
237 static void setup_gcrypt()
239 gcry_check_version(NULL);
241 #ifndef MEM_DEBUG
242 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
243 xfree);
244 #endif
246 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
247 NULL) != 0)
248 errx(EXIT_FAILURE, N_("Required AES cipher not supported by libgcrypt."));
250 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
251 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
254 static void child_catchsig(int sig)
256 assuan_context_t ctx = global_client->ctx;
258 switch (sig) {
259 case SIGTERM:
260 cleanup_assuan(ctx);
261 g_free(global_client);
262 assuan_deinit_server(ctx);
263 log_write(N_("exiting"));
264 _exit(EXIT_SUCCESS);
265 break;
266 case SIGABRT:
267 #ifndef MEM_DEBUG
268 xpanic();
269 #endif
270 exit(EXIT_FAILURE);
271 default:
272 break;
277 * Called every time a connection is made.
279 static void doit(int fd)
281 assuan_context_t ctx;
282 gint rc;
283 struct client_s *cl = g_malloc0(sizeof(struct client_s));
284 gchar ver[ASSUAN_LINELENGTH];
286 signal(SIGCHLD, SIG_DFL);
287 signal(SIGHUP, SIG_IGN);
288 signal(SIGINT, SIG_IGN);
289 signal(SIGALRM, SIG_IGN);
290 signal(SIGTERM, child_catchsig);
291 signal(SIGABRT, child_catchsig);
292 gpg_err_init();
293 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
294 #ifndef MEM_DEBUG
295 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
296 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
297 xmlInitMemory();
298 #endif
300 #ifdef HAVE_MLOCKALL
301 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
302 log_write("mlockall(): %s", strerror(errno));
303 exit(EXIT_FAILURE);
305 #endif
307 rc = assuan_init_socket_server_ext(&ctx, fd, 2);
309 if (rc) {
310 log_write("%s", gpg_strerror(rc));
311 exit(EXIT_FAILURE);
314 g_snprintf(ver, sizeof(ver), "%s", PACKAGE_STRING);
315 assuan_set_hello_line(ctx, ver);
316 assuan_set_pointer(ctx, cl);
317 rc = register_commands(ctx);
319 if (rc) {
320 log_write("%s", gpg_strerror(rc));
321 exit(EXIT_FAILURE);
324 cl->ctx = ctx;
325 global_client = cl;
328 * It would be nice if there were an assuan_register_pre_cmd_notify().
329 * That way we can see if the file has been modified before calling the
330 * command.
332 rc = assuan_accept(ctx);
334 if (rc) {
335 log_write("%s", gpg_strerror(rc));
336 goto done;
339 rc = assuan_process(ctx);
341 if (rc)
342 log_write("%s", gpg_strerror(rc));
344 done:
345 if (cl->freed == FALSE)
346 cleanup_assuan(ctx);
348 g_free(cl);
349 assuan_deinit_server(ctx);
350 log_write(N_("exiting"));
351 _exit(EXIT_SUCCESS);
355 * Make sure all settings are set to either the specified setting or a
356 * default.
358 static void set_rcfile_defaults(GKeyFile *kf)
360 gchar buf[PATH_MAX];
362 if (g_key_file_has_key(kf, "default", "socket_path", NULL) == FALSE) {
363 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
364 g_key_file_set_string(kf, "default", "socket_path", buf);
367 if (g_key_file_has_key(kf, "default", "data_directory", NULL) == FALSE) {
368 snprintf(buf, sizeof(buf), "~/.pwmd/data");
369 g_key_file_set_string(kf, "default", "data_directory", buf);
372 if (g_key_file_has_key(kf, "default", "log_path", NULL) == FALSE) {
373 snprintf(buf, sizeof(buf), "~/.pwmd/log");
374 g_key_file_set_string(kf, "default", "log_path", buf);
377 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == FALSE)
378 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
380 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == FALSE)
381 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
383 #ifdef HAVE_MLOCKALL
384 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == FALSE)
385 g_key_file_set_boolean(kf, "default", "disable_mlockall", TRUE);
386 #endif
388 if (g_key_file_has_key(kf, "default", "cache_timeout", NULL) == FALSE)
389 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
391 if (g_key_file_has_key(kf, "default", "iterations", NULL) == FALSE)
392 g_key_file_set_integer(kf, "default", "iterations", 0);
394 if (g_key_file_has_key(kf, "default", "disable_list_and_dump", NULL) == FALSE)
395 g_key_file_set_boolean(kf, "default", "disable_list_and_dump", FALSE);
397 if (g_key_file_has_key(kf, "default", "iteration_progress", NULL) == FALSE)
398 g_key_file_set_integer(kf, "default", "iteration_progress", 0);
400 if (g_key_file_has_key(kf, "default", "compression_level", NULL) == FALSE)
401 g_key_file_set_integer(kf, "default", "compression_level", 6);
403 if (g_key_file_has_key(kf, "default", "recursion_depth", NULL) == FALSE)
404 g_key_file_set_integer(kf, "default", "recursion_depth", DEFAULT_RECURSION_DEPTH);
406 #ifdef HAVE_ZLIB_H
407 if (g_key_file_has_key(kf, "default", "zlib_bufsize", NULL) == FALSE)
408 g_key_file_set_integer(kf, "default", "zlib_bufsize", DEFAULT_ZLIB_BUFSIZE);
410 zlib_bufsize = g_key_file_get_integer(kf, "default", "zlib_bufsize", NULL);
411 #endif
413 max_recursion_depth = g_key_file_get_integer(kf, "default", "recursion_depth", NULL);
414 disable_list_and_dump = g_key_file_get_boolean(kf, "default", "disable_list_and_dump", NULL);
416 #ifdef HAVE_MLOCKALL
417 disable_mlock = g_key_file_get_boolean(kf, "default", "disable_mlockall", NULL);
418 #endif
421 static GKeyFile *parse_rcfile(int cmdline)
423 GKeyFile *kf = g_key_file_new();
424 GError *error = NULL;
426 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &error) == FALSE) {
427 log_write("%s: %s", rcfile, error->message);
429 if (cmdline)
430 exit(EXIT_FAILURE);
432 if (error->code == G_FILE_ERROR_NOENT) {
433 g_clear_error(&error);
434 set_rcfile_defaults(kf);
435 return kf;
438 g_clear_error(&error);
439 return NULL;
441 else
442 set_rcfile_defaults(kf);
444 return kf;
447 static gchar *get_password(const gchar *prompt)
449 gchar buf[LINE_MAX], *p;
450 struct termios told, tnew;
451 gchar *key;
453 if (tcgetattr(STDIN_FILENO, &told) == -1)
454 err(EXIT_FAILURE, "tcgetattr()");
456 memcpy(&tnew, &told, sizeof(struct termios));
457 tnew.c_lflag &= ~(ECHO);
458 tnew.c_lflag |= ICANON|ECHONL;
460 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
461 tcsetattr(STDIN_FILENO, TCSANOW, &told);
462 err(EXIT_FAILURE, "tcsetattr()");
465 fprintf(stderr, "%s", prompt);
467 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
468 tcsetattr(STDIN_FILENO, TCSANOW, &told);
469 return NULL;
472 tcsetattr(STDIN_FILENO, TCSANOW, &told);
473 p[strlen(p) - 1] = 0;
475 if (!p || !*p)
476 return NULL;
478 key = gcry_malloc(strlen(p) + 1);
479 sprintf(key, "%s", p);
480 memset(&buf, 0, sizeof(buf));
481 return key;
484 static gboolean do_try_xml_decrypt(const gchar *filename, guchar *key)
486 int fd;
487 struct stat st;
488 gpg_error_t error;
490 if ((fd = open_file(filename, &st)) == -1) {
491 warn("%s", filename);
492 return FALSE;
495 if (st.st_size == 0) {
496 warnx(N_("%s: skipping empty file"), filename);
497 close(fd);
498 return FALSE;
501 error = try_xml_decrypt(NULL, fd, st, key);
502 close(fd);
503 return error ? FALSE : TRUE;
506 static gboolean get_input(const gchar *filename, guchar *key)
508 gint try = 0;
509 gchar *password;
510 gchar *prompt;
512 prompt = g_strdup_printf(N_("Password for '%s': "), filename);
514 again:
515 if ((password = get_password(prompt)) == NULL) {
516 warnx(N_("%s: skipping file"), filename);
517 g_free(prompt);
518 return FALSE;
521 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
522 gcry_free(password);
524 if (do_try_xml_decrypt(filename, key) == FALSE) {
525 if (try++ == 2) {
526 warnx(N_("%s: invalid password, skipping"), filename);
527 g_free(prompt);
528 return FALSE;
530 else {
531 warnx(N_("%s: invalid password"), filename);
532 goto again;
536 g_free(prompt);
537 return TRUE;
540 static gboolean xml_import(const gchar *filename, gint iter)
542 xmlDocPtr doc;
543 gint fd;
544 struct stat st;
545 gint len;
546 xmlChar *xmlbuf;
547 xmlChar *xml;
548 gchar *key = NULL;
549 gchar *key2 = NULL;
550 guchar shakey[gcrykeysize];
551 gcry_cipher_hd_t gh;
552 gpg_error_t error;
553 #ifdef HAVE_ZLIB_H
554 gint level;
555 glong outsize;
556 gpointer outbuf;
557 gint zerror;
558 #endif
560 #ifdef HAVE_MLOCKALL
561 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1)
562 err(EXIT_FAILURE, "mlockall()");
563 #endif
565 if (stat(filename, &st) == -1) {
566 warn("%s", filename);
567 return FALSE;
570 #ifndef MEM_DEBUG
571 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
572 xmlInitMemory();
573 #endif
575 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
576 send_error(NULL, gcryerrno);
577 gcry_cipher_close(gh);
578 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
579 return FALSE;
582 if ((key = get_password(N_("New password: "))) == NULL) {
583 fprintf(stderr, "%s\n", N_("Invalid password."));
584 gcry_cipher_close(gh);
585 return FALSE;
588 if ((key2 = get_password(N_("Verify password: "))) == NULL) {
589 fprintf(stderr, "%s\n", N_("Passwords do not match."));
590 gcry_free(key);
591 gcry_cipher_close(gh);
592 return FALSE;
595 if (g_utf8_collate(key, key2) != 0) {
596 fprintf(stderr, "%s\n", N_("Passwords do not match."));
597 gcry_free(key);
598 gcry_free(key2);
599 gcry_cipher_close(gh);
600 return FALSE;
603 gcry_free(key2);
605 if ((fd = open(filename, O_RDONLY)) == -1) {
606 gcry_free(key);
607 warn("%s", filename);
608 gcry_cipher_close(gh);
609 return FALSE;
612 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
613 gcry_free(key);
614 close(fd);
615 log_write("%s", strerror(ENOMEM));
616 gcry_cipher_close(gh);
617 return FALSE;
620 if (read(fd, xmlbuf, st.st_size) == -1) {
621 error = errno;
622 close(fd);
623 gcry_free(key);
624 gcry_cipher_close(gh);
625 errno = error;
626 err(EXIT_FAILURE, "read()");
629 close(fd);
630 xmlbuf[st.st_size] = 0;
633 * Make sure the document validates.
635 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
636 log_write("xmlReadDoc()");
637 close(fd);
638 gcry_free(key);
639 gcry_free(xmlbuf);
640 gcry_cipher_close(gh);
641 return FALSE;
644 gcry_free(xmlbuf);
645 xmlDocDumpMemory(doc, &xml, &len);
646 xmlFreeDoc(doc);
648 #ifdef HAVE_ZLIB_H
649 level = get_key_file_integer(filename, "compression_level");
651 if (level < 0)
652 level = 0;
654 if (do_compress(NULL, level, xml, len, &outbuf, &outsize, &zerror) == FALSE) {
655 memset(shakey, 0, sizeof(shakey));
656 gcry_free(xml);
658 if (zerror == Z_MEM_ERROR)
659 warnx("%s", strerror(ENOMEM));
660 else
661 warnx("do_compress() failed");
663 gcry_cipher_close(gh);
664 return FALSE;
666 else {
667 gcry_free(xml);
668 xml = outbuf;
669 len = outsize;
671 #endif
673 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
674 gcry_free(key);
675 error = do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter);
676 gcry_cipher_close(gh);
678 if (error) {
679 memset(shakey, 0, sizeof(shakey));
680 warnx("%s", gpg_strerror(error));
681 return FALSE;
684 memset(shakey, 0, sizeof(shakey));
685 return TRUE;
688 gchar *get_key_file_string(const gchar *section, const gchar *what)
690 gchar *val = NULL;
691 GError *gerror = NULL;
693 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
694 val = g_key_file_get_string(keyfileh, section, what, &gerror);
696 if (gerror) {
697 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
698 g_clear_error(&gerror);
701 else {
702 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
703 val = g_key_file_get_string(keyfileh, "default", what, &gerror);
705 if (gerror) {
706 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
707 g_clear_error(&gerror);
712 return val;
715 gint get_key_file_integer(const gchar *section, const gchar *what)
717 gint val = -1;
718 GError *gerror = NULL;
720 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
721 val = g_key_file_get_integer(keyfileh, section, what, &gerror);
723 if (gerror) {
724 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
725 g_clear_error(&gerror);
728 else {
729 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
730 val = g_key_file_get_integer(keyfileh, "default", what, &gerror);
732 if (gerror) {
733 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
734 g_clear_error(&gerror);
739 return val;
742 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
744 gboolean val = FALSE;
745 GError *gerror = NULL;
747 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
748 val = g_key_file_get_boolean(keyfileh, section, what, &gerror);
750 if (gerror) {
751 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
752 g_clear_error(&gerror);
755 else {
756 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
757 val = g_key_file_get_boolean(keyfileh, "default", what, &gerror);
759 if (gerror) {
760 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
761 g_clear_error(&gerror);
766 return val;
769 gchar *expand_homedir(gchar *str)
771 gchar *p = str;
773 if (*p++ == '~')
774 return g_strdup_printf("%s%s", g_get_home_dir(), p);
776 return g_strdup(str);
779 static gchar *_getline(const gchar *file)
781 FILE *fp;
782 gchar buf[LINE_MAX], *p;
783 gchar *str = NULL;
785 if ((fp = fopen(file, "r")) == NULL) {
786 warn("%s", file);
787 return NULL;
790 if ((p = fgets(buf, sizeof(buf), fp)) == NULL) {
791 warnx(N_("%s: empty file?"), file);
792 return NULL;
795 fclose(fp);
797 if (buf[strlen(buf) - 1] == '\n')
798 buf[strlen(buf) - 1] = 0;
800 str = gcry_malloc(strlen(p) + 1);
801 memcpy(str, p, strlen(p));
802 str[strlen(p)] = 0;
803 memset(&buf, 0, sizeof(buf));
804 return str;
807 static gboolean parse_keyfile_key()
809 gsize n;
810 gchar **groups;
811 gchar **p;
812 gchar *str;
814 groups = g_key_file_get_groups(keyfileh, &n);
816 for (p = groups; *p; p++) {
817 GError *error = NULL;
819 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE) {
820 str = g_key_file_get_string(keyfileh, *p, "key", &error);
822 if (!str) {
823 if (error) {
824 warnx("%s", error->message);
825 g_clear_error(&error);
828 continue;
831 do_cache_push(*p, str);
832 g_free(str);
833 continue;
836 if (error) {
837 warnx("%s", error->message);
838 g_clear_error(&error);
839 continue;
842 if (g_key_file_has_key(keyfileh, *p, "key_file", &error) == TRUE) {
843 gchar *t;
844 gchar *file = g_key_file_get_string(keyfileh, *p, "key_file", &error);
846 if (!file) {
847 if (error) {
848 warnx("%s", error->message);
849 g_clear_error(&error);
852 continue;
855 t = expand_homedir(file);
856 g_free(file);
857 file = t;
859 if ((str = _getline(file)) == NULL) {
860 g_free(file);
861 continue;
864 g_free(file);
865 do_cache_push(*p, str);
866 gcry_free(str);
867 continue;
870 if (error) {
871 warnx("%s", error->message);
872 g_clear_error(&error);
876 g_strfreev(groups);
877 return TRUE;
880 static gboolean do_cache_push(const gchar *filename, const gchar *password)
882 guchar *md5file;
883 guchar *key;
884 gint timeout;
885 const gchar *p = filename;
887 while (isspace(*p))
888 p++;
890 if (!*p)
891 return FALSE;
893 if (valid_filename(p) == FALSE) {
894 warnx(N_("%s: invalid characters in filename"), p);
895 return FALSE;
898 md5file = gcry_malloc(16);
899 key = gcry_malloc(gcrykeysize);
900 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
902 if (cache_iscached(md5file) == TRUE) {
903 warnx(N_("%s: file already cached, skipping"), p);
904 gcry_free(md5file);
905 gcry_free(key);
906 return FALSE;
909 if (access(p, R_OK|W_OK) != 0) {
910 gcry_free(md5file);
911 gcry_free(key);
913 if (errno != ENOENT) {
914 warn("%s", p);
915 return FALSE;
918 warn("%s", p);
919 return TRUE;
922 if (!password) {
923 if (get_input(p, key) == FALSE) {
924 gcry_free(key);
925 gcry_free(md5file);
926 return FALSE;
929 else {
930 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
932 if (do_try_xml_decrypt(filename, key) == FALSE) {
933 warnx(N_("%s: invalid password, skipping"), filename);
934 gcry_free(key);
935 gcry_free(md5file);
936 return FALSE;
940 if (cache_add_file(md5file, key) == FALSE) {
941 warnx("%s: %s", p, pwmd_strerror(EPWMD_MAX_SLOTS));
942 gcry_free(key);
943 gcry_free(md5file);
944 return FALSE;
947 timeout = get_key_file_integer(p, "cache_timeout");
948 cache_set_timeout(md5file, timeout);
949 warnx(N_("%s: file added to the cache"), filename);
950 gcry_free(key);
951 gcry_free(md5file);
952 return TRUE;
955 int main(int argc, char *argv[])
957 gint opt;
958 struct sockaddr_un addr;
959 struct passwd *pw = getpwuid(getuid());
960 gchar buf[PATH_MAX];
961 #ifndef MMAP_ANONYMOUS_SHARED
962 gchar shm_path[PATH_MAX];
963 #endif
964 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
965 gchar *socketarg = NULL;
966 gchar *datadir = NULL;
967 gint fd;
968 gboolean n;
969 gchar *p;
970 gchar **cache_push = NULL;
971 gint iter = 0;
972 gchar *import = NULL;
973 gint default_timeout;
974 gint rcfile_spec = 0;
975 gint estatus = EXIT_FAILURE;
976 #ifndef MEM_DEBUG
977 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
978 #endif
979 gint do_unlink = 1;
980 gboolean secure = FALSE;
981 guint ptotal = 0;
982 gint background = 0;
983 gint n_clients = 0;
984 pthread_mutexattr_t cache_mutex_attr;
985 pthread_mutex_t cache_mutex;
986 #ifndef DEBUG
987 #ifdef HAVE_SETRLIMIT
988 struct rlimit rl;
990 rl.rlim_cur = rl.rlim_max = 0;
992 if (setrlimit(RLIMIT_CORE, &rl) != 0)
993 err(EXIT_FAILURE, "setrlimit()");
994 #endif
995 #endif
997 #ifdef ENABLE_NLS
998 setlocale(LC_ALL, "");
999 bindtextdomain("pwmd", LOCALEDIR);
1000 textdomain("pwmd");
1001 #endif
1003 #ifndef MEM_DEBUG
1004 g_mem_set_vtable(&mtable);
1005 #endif
1006 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
1008 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1009 err(EXIT_FAILURE, "%s", buf);
1011 snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
1013 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1014 err(EXIT_FAILURE, "%s", buf);
1016 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
1018 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
1019 err(EXIT_FAILURE, "sysconf()");
1021 cache_size = page_size;
1023 while ((opt = getopt(argc, argv, "bI:hvf:D")) != EOF) {
1024 switch (opt) {
1025 case 'b':
1026 background = 1;
1027 break;
1028 case 'D':
1029 secure = TRUE;
1030 break;
1031 case 'I':
1032 import = optarg;
1033 break;
1034 case 'f':
1035 g_free(rcfile);
1036 rcfile = g_strdup(optarg);
1037 rcfile_spec = 1;
1038 break;
1039 case 'v':
1040 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
1041 exit(EXIT_SUCCESS);
1042 case 'h':
1043 default:
1044 usage(argv[0]);
1048 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
1049 exit(EXIT_FAILURE);
1051 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1052 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1054 setup_gcrypt();
1056 if (import) {
1057 opt = xml_import(import, iter);
1058 g_key_file_free(keyfileh);
1059 g_free(rcfile);
1060 exit(opt == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1063 g_key_file_set_list_separator(keyfileh, ',');
1065 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
1066 errx(EXIT_FAILURE, N_("%s: socket_path not defined"), rcfile);
1068 if (*p == '~') {
1069 p++;
1070 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1071 g_free(p);
1072 socketarg = g_strdup(buf);
1074 else
1075 socketarg = p;
1077 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1078 errx(EXIT_FAILURE, N_("%s: data_directory not defined"), rcfile);
1080 datadir = expand_homedir(p);
1081 g_free(p);
1083 if (secure == FALSE && g_key_file_has_key(keyfileh, "default", "disable_list_and_dump", NULL) == TRUE) {
1084 n = g_key_file_get_boolean(keyfileh, "default", "disable_list_and_dump", NULL);
1085 disable_list_and_dump = n;
1087 else
1088 disable_list_and_dump = secure;
1090 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1091 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1092 else
1093 default_timeout = -1;
1095 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1096 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1098 if (cache_size < page_size || cache_size % page_size)
1099 errx(EXIT_FAILURE, N_("cache size must be in multiples of %li"), page_size);
1102 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1103 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1104 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1106 if (n == TRUE) {
1107 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1109 if (*p == '~') {
1110 p++;
1111 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1112 g_free(p);
1113 logfile = g_strdup(buf);
1115 else
1116 logfile = p;
1121 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1122 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1124 if (argc != optind) {
1125 if (cache_push)
1126 ptotal = g_strv_length(cache_push);
1128 for (; optind < argc; optind++) {
1129 if (strv_printf(&cache_push, "%s", argv[optind]) == FALSE)
1130 errx(EXIT_FAILURE, "%s", strerror(ENOMEM));
1134 if (strchr(socketarg, '/') == NULL) {
1135 socketdir = g_get_current_dir();
1136 socketname = g_strdup(socketarg);
1137 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1139 else {
1140 socketname = g_strdup(strrchr(socketarg, '/'));
1141 socketname++;
1142 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1143 socketdir = g_strdup(socketarg);
1144 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1147 #ifdef MMAP_ANONYMOUS_SHARED
1148 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1149 #ifdef MMAP_ANONYMOUS
1150 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {
1151 #else
1152 MAP_SHARED|MAP_ANON, -1, 0)) == MAP_FAILED) {
1153 #endif
1154 err(EXIT_FAILURE, "mmap()");
1156 #else
1157 snprintf(shm_path, sizeof(shm_path), "/pwmd.%i", pw->pw_uid);
1159 if ((fd = shm_open(shm_path, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
1160 err(EXIT_FAILURE, "shm_open(): %s", shm_path);
1163 * Should be enough for the file cache.
1165 if (ftruncate(fd, cache_size) == -1) {
1166 warn("ftruncate()");
1167 shm_unlink(shm_path);
1168 exit(EXIT_FAILURE);
1171 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
1172 fd, 0)) == MAP_FAILED) {
1173 warn("mmap()");
1174 shm_unlink(shm_path);
1175 exit(EXIT_FAILURE);
1178 close(fd);
1179 #endif
1181 if (mlock(shm_data, cache_size) == -1)
1182 warn("mlock()");
1184 memset(shm_data, 0, cache_size);
1186 if (chdir(datadir)) {
1187 warn("%s", datadir);
1188 close(sfd);
1189 unlink(socketpath);
1190 goto do_exit;
1193 if (parse_keyfile_key() == FALSE)
1194 goto do_exit;
1196 clear_rcfile_key();
1199 * Set the cache entry for a file. Prompts for the password.
1201 if (cache_push) {
1202 for (opt = 0; cache_push[opt]; opt++)
1203 do_cache_push(cache_push[opt], NULL);
1205 g_strfreev(cache_push);
1206 warnx(background ? N_("Done. Daemonizing...") : N_("Done. Waiting for connections..."));
1210 * bind() doesn't like the full pathname of the socket or any non alphanum
1211 * characters so change to the directory where the socket is wanted then
1212 * create it then change to datadir.
1214 if (chdir(socketdir)) {
1215 warn("%s", socketdir);
1216 goto do_exit;
1219 g_free(socketdir);
1221 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1222 warn("socket()");
1223 goto do_exit;
1226 addr.sun_family = AF_UNIX;
1227 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1228 g_free(--socketname);
1230 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
1231 warn("bind()");
1233 if (errno == EADDRINUSE)
1234 warnx(N_("Either there is another pwmd running or '%s' is a \n"
1235 "stale socket. Please remove it manually."), socketpath);
1237 do_unlink = 0;
1238 goto do_exit;
1241 if (chdir(datadir)) {
1242 warn("%s", datadir);
1243 close(sfd);
1244 unlink(socketpath);
1245 goto do_exit;
1248 g_free(datadir);
1249 pthread_mutexattr_init(&cache_mutex_attr);
1250 pthread_mutexattr_settype(&cache_mutex_attr, PTHREAD_MUTEX_FAST_NP);
1251 pthread_mutex_init(&cache_mutex, &cache_mutex_attr);
1252 pthread_mutexattr_destroy(&cache_mutex_attr);
1253 cache_mutexp = shm_data + MUTEX_OFFSET;
1254 memcpy(cache_mutexp, &cache_mutex, sizeof(cache_mutex));
1256 if (listen(sfd, 0) == -1) {
1257 warn("listen()");
1258 goto do_exit;
1261 signal(SIGCHLD, catchsig);
1262 signal(SIGTERM, catchsig);
1263 signal(SIGINT, catchsig);
1264 signal(SIGHUP, catchsig);
1265 signal(SIGALRM, catchsig);
1266 signal(SIGABRT, catchsig);
1267 signal(SIGUSR1, catchsig);
1268 alarm(1);
1270 if (background) {
1271 switch (fork()) {
1272 case -1:
1273 warn("fork()");
1274 goto do_exit;
1275 case 0:
1276 close(0);
1277 close(1);
1278 close(2);
1279 break;
1280 default:
1281 exit(EXIT_SUCCESS);
1285 log_write("%s %s", PACKAGE_STRING, N_("started"));
1286 send_cache_status(NULL);
1288 while (!quit) {
1289 socklen_t slen = sizeof(struct sockaddr_un);
1290 struct sockaddr_un raddr;
1291 pid_t pid;
1293 if ((fd = accept(sfd, (struct sockaddr *)&raddr, &slen)) == -1) {
1294 if (quit)
1295 break;
1297 if (errno == EAGAIN)
1298 continue;
1300 log_write("accept(): %s", strerror(errno));
1301 continue;
1304 switch ((pid = fork())) {
1305 case -1:
1306 log_write("fork(): %s", strerror(errno));
1307 break;
1308 case 0:
1309 doit(fd);
1310 break;
1311 default:
1312 break;
1315 clients++;
1316 log_write(N_("new connection: pid=%i"), pid);
1317 close(fd);
1320 estatus = EXIT_SUCCESS;
1322 do_exit:
1323 if (socketpath && do_unlink) {
1324 unlink(socketpath);
1325 g_free(socketpath);
1328 signal(SIGUSR1, SIG_IGN);
1329 g_key_file_free(keyfileh);
1330 g_free(rcfile);
1332 if (clients) {
1333 log_write(N_("waiting for all clients to disconnect"));
1335 do {
1336 if (clients != n_clients) {
1337 log_write(N_("%i clients remain"), clients);
1338 n_clients = clients;
1341 select(0, NULL, NULL, NULL, NULL);
1342 } while (clients);
1345 memset(shm_data, 0, cache_size);
1347 if (munmap(shm_data, cache_size) == -1)
1348 log_write("munmap(): %s", strerror(errno));
1350 #ifndef MMAP_ANONYMOUS_SHARED
1351 if (shm_unlink(shm_path) == -1)
1352 log_write("shm_unlink(): %s: %s", shm_path, strerror(errno));
1353 #endif
1355 if (estatus == EXIT_SUCCESS)
1356 log_write(N_("pwmd exiting normally"));
1358 exit(estatus);