Fix for commit e2067d9 and sending SIGUSR1.
[pwmd.git] / src / pwmd.c
blob6b2b3b33bbc690dea3d92f126c9b8cd07d6dcca1
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", "");
81 static void reload_rcfile()
83 log_write(N_("reloading configuration file '%s'"), rcfile);
84 g_key_file_free(keyfileh);
85 keyfileh = parse_rcfile(0);
86 clear_rcfile_key();
89 gpg_error_t send_syserror(assuan_context_t ctx, int e)
91 gpg_error_t n = gpg_error_from_errno(e);
93 return assuan_set_error(ctx, n, gpg_strerror(n));
96 gpg_error_t send_error(assuan_context_t ctx, gpg_error_t e)
98 gpg_err_code_t n = gpg_err_code(e);
99 gpg_error_t code = gpg_err_make(GPG_ERR_SOURCE_USER_1, n);
101 if (!e)
102 return 0;
104 if (!ctx) {
105 log_write("%s\n", pwmd_strerror(e));
106 return e;
109 if (n == EPWMD_LIBXML_ERROR) {
110 xmlErrorPtr xml_error = xmlGetLastError();
111 return assuan_set_error(ctx, code, xml_error->message);
114 return assuan_set_error(ctx, code, pwmd_strerror(e));
117 void log_write(const gchar *fmt, ...)
119 gchar *args, *line;
120 va_list ap;
121 struct tm *tm;
122 time_t now;
123 gchar tbuf[21];
124 gint fd = -1;
126 if ((!logfile && !isatty(STDERR_FILENO)) || !fmt)
127 return;
129 if (logfile) {
130 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
131 warn(N_("logfile"));
132 return;
136 va_start(ap, fmt);
138 if (g_vasprintf(&args, fmt, ap) == -1) {
139 va_end(ap);
140 return;
143 va_end(ap);
144 time(&now);
145 tm = localtime(&now);
146 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
147 tbuf[sizeof(tbuf) - 1] = 0;
148 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
150 if (!line) {
151 if (logfile)
152 close(fd);
154 g_free(args);
155 return;
158 if (logfile) {
159 write(fd, line, strlen(line));
160 fsync(fd);
161 close(fd);
164 if (isatty(STDERR_FILENO)) {
165 fprintf(stderr, "%s", line);
166 fflush(stderr);
169 g_free(line);
170 g_free(args);
173 static void catchsig(gint sig)
175 gint status;
177 if (sig != SIGALRM && sig != SIGCHLD)
178 log_write(N_("caught signal %i (%s)"), sig, strsignal(sig));
180 switch (sig) {
181 case SIGUSR1:
182 reload_rcfile();
183 break;
184 case SIGABRT:
185 cache_clear(NULL, 2);
186 #ifndef MEM_DEBUG
187 xpanic();
188 #endif
189 exit(EXIT_FAILURE);
190 case SIGALRM:
191 cache_adjust_timer();
192 alarm(1);
193 break;
194 case SIGCHLD:
195 waitpid(-1, &status, 0);
197 if (WIFEXITED(status) || WIFSIGNALED(status))
198 clients--;
200 break;
201 case SIGHUP:
202 log_write(N_("clearing file cache"));
203 cache_clear(NULL, 2);
204 break;
205 default:
206 signal(SIGALRM, SIG_IGN);
207 quit = 1;
208 shutdown(sfd, SHUT_RDWR);
209 close(sfd);
210 break;
214 static void usage(gchar *pn)
216 g_printf(N_(
217 "Usage: %s [-hvDb] [-f <rcfile>] [-I <filename>] [file1] [...]\n"
218 " -b run as a background process\n"
219 " -f load the specified rcfile (~/.pwmd/config)\n"
220 " -I import an XML file and write the encrypted data to stdout\n"
221 " -D disable use of the LIST and DUMP commands\n"
222 " -v version\n"
223 " -h this help text\n"
224 ), pn);
225 exit(EXIT_SUCCESS);
228 #ifndef MEM_DEBUG
229 static int gcry_SecureCheck(const void *ptr)
231 return 1;
233 #endif
235 static void setup_gcrypt()
237 gcry_check_version(NULL);
239 #ifndef MEM_DEBUG
240 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
241 xfree);
242 #endif
244 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
245 NULL) != 0)
246 errx(EXIT_FAILURE, N_("Required AES cipher not supported by libgcrypt."));
248 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
249 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
252 static void child_catchsig(int sig)
254 assuan_context_t ctx = global_client->ctx;
256 switch (sig) {
257 case SIGTERM:
258 cleanup_assuan(ctx);
259 g_free(global_client);
260 assuan_deinit_server(ctx);
261 log_write(N_("exiting"));
262 _exit(EXIT_SUCCESS);
263 break;
264 case SIGABRT:
265 #ifndef MEM_DEBUG
266 xpanic();
267 #endif
268 exit(EXIT_FAILURE);
269 default:
270 break;
275 * Called every time a connection is made.
277 static void doit(int fd)
279 assuan_context_t ctx;
280 int rc;
281 struct client_s *cl = g_malloc0(sizeof(struct client_s));
283 signal(SIGCHLD, SIG_DFL);
284 signal(SIGHUP, SIG_IGN);
285 signal(SIGINT, SIG_IGN);
286 signal(SIGALRM, SIG_IGN);
287 signal(SIGTERM, child_catchsig);
288 signal(SIGABRT, child_catchsig);
289 gpg_err_init();
290 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
291 #ifndef MEM_DEBUG
292 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
293 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
294 xmlInitMemory();
295 #endif
297 #ifdef HAVE_MLOCKALL
298 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
299 log_write("mlockall(): %s", strerror(errno));
300 exit(EXIT_FAILURE);
302 #endif
304 rc = assuan_init_socket_server_ext(&ctx, fd, 2);
306 if (rc) {
307 log_write("%s", gpg_strerror(rc));
308 exit(EXIT_FAILURE);
311 assuan_set_pointer(ctx, cl);
312 rc = register_commands(ctx);
314 if (rc) {
315 log_write("%s", gpg_strerror(rc));
316 exit(EXIT_FAILURE);
319 cl->ctx = ctx;
320 global_client = cl;
323 * It would be nice if there were an assuan_register_pre_cmd_notify().
324 * That way we can see if the file has been modified before calling the
325 * command.
327 rc = assuan_accept(ctx);
329 if (rc) {
330 log_write("%s", gpg_strerror(rc));
331 goto done;
334 rc = assuan_process(ctx);
336 if (rc)
337 log_write("%s", gpg_strerror(rc));
339 done:
340 if (cl->freed == FALSE)
341 cleanup_assuan(ctx);
343 g_free(cl);
344 assuan_deinit_server(ctx);
345 log_write(N_("exiting"));
346 _exit(EXIT_SUCCESS);
350 * Make sure all settings are set to either the specified setting or a
351 * default.
353 static void set_rcfile_defaults(GKeyFile *kf)
355 gchar buf[PATH_MAX];
357 if (g_key_file_has_key(kf, "default", "socket_path", NULL) == FALSE) {
358 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
359 g_key_file_set_string(kf, "default", "socket_path", buf);
362 if (g_key_file_has_key(kf, "default", "data_directory", NULL) == FALSE) {
363 snprintf(buf, sizeof(buf), "~/.pwmd/data");
364 g_key_file_set_string(kf, "default", "data_directory", buf);
367 if (g_key_file_has_key(kf, "default", "log_path", NULL) == FALSE) {
368 snprintf(buf, sizeof(buf), "~/.pwmd/log");
369 g_key_file_set_string(kf, "default", "log_path", buf);
372 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == FALSE)
373 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
375 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == FALSE)
376 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
378 #ifdef HAVE_MLOCKALL
379 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == FALSE)
380 g_key_file_set_boolean(kf, "default", "disable_mlockall", TRUE);
381 #endif
383 if (g_key_file_has_key(kf, "default", "cache_timeout", NULL) == FALSE)
384 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
386 if (g_key_file_has_key(kf, "default", "iterations", NULL) == FALSE)
387 g_key_file_set_integer(kf, "default", "iterations", 0);
389 if (g_key_file_has_key(kf, "default", "disable_list_and_dump", NULL) == FALSE)
390 g_key_file_set_boolean(kf, "default", "disable_list_and_dump", FALSE);
392 if (g_key_file_has_key(kf, "default", "iteration_progress", NULL) == FALSE)
393 g_key_file_set_integer(kf, "default", "iteration_progress", 0);
395 if (g_key_file_has_key(kf, "default", "compression_level", NULL) == FALSE)
396 g_key_file_set_integer(kf, "default", "compression_level", 6);
398 if (g_key_file_has_key(kf, "default", "recursion_depth", NULL) == FALSE)
399 g_key_file_set_integer(kf, "default", "recursion_depth", DEFAULT_RECURSION_DEPTH);
401 #ifdef HAVE_ZLIB_H
402 if (g_key_file_has_key(kf, "default", "zlib_bufsize", NULL) == FALSE)
403 g_key_file_set_integer(kf, "default", "zlib_bufsize", DEFAULT_ZLIB_BUFSIZE);
405 zlib_bufsize = g_key_file_get_integer(kf, "default", "zlib_bufsize", NULL);
406 #endif
408 max_recursion_depth = g_key_file_get_integer(kf, "default", "recursion_depth", NULL);
409 disable_list_and_dump = g_key_file_get_boolean(kf, "default", "disable_list_and_dump", NULL);
411 #ifdef HAVE_MLOCKALL
412 disable_mlock = g_key_file_get_boolean(kf, "default", "disable_mlockall", NULL);
413 #endif
416 static GKeyFile *parse_rcfile(int cmdline)
418 GKeyFile *kf = g_key_file_new();
419 GError *error = NULL;
421 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &error) == FALSE) {
422 log_write("%s: %s", rcfile, error->message);
424 if (cmdline)
425 exit(EXIT_FAILURE);
427 if (error->code == G_FILE_ERROR_NOENT) {
428 g_clear_error(&error);
429 set_rcfile_defaults(kf);
430 return kf;
433 g_clear_error(&error);
434 return NULL;
436 else
437 set_rcfile_defaults(kf);
439 return kf;
442 static gchar *get_password(const gchar *prompt)
444 gchar buf[LINE_MAX], *p;
445 struct termios told, tnew;
446 gchar *key;
448 if (tcgetattr(STDIN_FILENO, &told) == -1)
449 err(EXIT_FAILURE, "tcgetattr()");
451 memcpy(&tnew, &told, sizeof(struct termios));
452 tnew.c_lflag &= ~(ECHO);
453 tnew.c_lflag |= ICANON|ECHONL;
455 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
456 tcsetattr(STDIN_FILENO, TCSANOW, &told);
457 err(EXIT_FAILURE, "tcsetattr()");
460 fprintf(stderr, "%s", prompt);
462 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
463 tcsetattr(STDIN_FILENO, TCSANOW, &told);
464 return NULL;
467 tcsetattr(STDIN_FILENO, TCSANOW, &told);
468 p[strlen(p) - 1] = 0;
470 if (!p || !*p)
471 return NULL;
473 key = gcry_malloc(strlen(p) + 1);
474 sprintf(key, "%s", p);
475 memset(&buf, 0, sizeof(buf));
476 return key;
479 static gboolean do_try_xml_decrypt(const gchar *filename, guchar *key)
481 int fd;
482 struct stat st;
483 gpg_error_t error;
485 if ((fd = open_file(filename, &st)) == -1) {
486 warn("%s", filename);
487 return FALSE;
490 if (st.st_size == 0) {
491 warnx(N_("%s: skipping empty file"), filename);
492 close(fd);
493 return FALSE;
496 error = try_xml_decrypt(NULL, fd, st, key);
497 close(fd);
498 return error ? FALSE : TRUE;
501 static gboolean get_input(const gchar *filename, guchar *key)
503 gint try = 0;
504 gchar *password;
505 gchar *prompt;
507 prompt = g_strdup_printf(N_("Password for '%s': "), filename);
509 again:
510 if ((password = get_password(prompt)) == NULL) {
511 warnx(N_("%s: skipping file"), filename);
512 g_free(prompt);
513 return FALSE;
516 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
517 gcry_free(password);
519 if (do_try_xml_decrypt(filename, key) == FALSE) {
520 if (try++ == 2) {
521 warnx(N_("%s: invalid password, skipping"), filename);
522 g_free(prompt);
523 return FALSE;
525 else {
526 warnx(N_("%s: invalid password"), filename);
527 goto again;
531 g_free(prompt);
532 return TRUE;
535 static gboolean xml_import(const gchar *filename, gint iter)
537 xmlDocPtr doc;
538 gint fd;
539 struct stat st;
540 gint len;
541 xmlChar *xmlbuf;
542 xmlChar *xml;
543 gchar *key = NULL;
544 gchar *key2 = NULL;
545 guchar shakey[gcrykeysize];
546 gcry_cipher_hd_t gh;
547 gpg_error_t error;
548 #ifdef HAVE_ZLIB_H
549 gint level;
550 glong outsize;
551 gpointer outbuf;
552 #endif
554 #ifdef HAVE_MLOCKALL
555 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1)
556 err(EXIT_FAILURE, "mlockall()");
557 #endif
559 if (stat(filename, &st) == -1) {
560 warn("%s", filename);
561 return FALSE;
564 #ifndef MEM_DEBUG
565 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
566 xmlInitMemory();
567 #endif
569 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
570 send_error(NULL, gcryerrno);
571 gcry_cipher_close(gh);
572 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
573 return FALSE;
576 if ((key = get_password(N_("New password: "))) == NULL) {
577 fprintf(stderr, "%s\n", N_("Invalid password."));
578 gcry_cipher_close(gh);
579 return FALSE;
582 if ((key2 = get_password(N_("Verify password: "))) == NULL) {
583 fprintf(stderr, "%s\n", N_("Passwords do not match."));
584 gcry_free(key);
585 gcry_cipher_close(gh);
586 return FALSE;
589 if (g_utf8_collate(key, key2) != 0) {
590 fprintf(stderr, "%s\n", N_("Passwords do not match."));
591 gcry_free(key);
592 gcry_free(key2);
593 gcry_cipher_close(gh);
594 return FALSE;
597 gcry_free(key2);
599 if ((fd = open(filename, O_RDONLY)) == -1) {
600 gcry_free(key);
601 warn("%s", filename);
602 gcry_cipher_close(gh);
603 return FALSE;
606 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
607 gcry_free(key);
608 close(fd);
609 log_write("%s", strerror(ENOMEM));
610 gcry_cipher_close(gh);
611 return FALSE;
614 read(fd, xmlbuf, st.st_size);
615 close(fd);
616 xmlbuf[st.st_size] = 0;
619 * Make sure the document validates.
621 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
622 log_write("xmlReadDoc()");
623 close(fd);
624 gcry_free(key);
625 gcry_free(xmlbuf);
626 gcry_cipher_close(gh);
627 return FALSE;
630 gcry_free(xmlbuf);
631 xmlDocDumpMemory(doc, &xml, &len);
632 xmlFreeDoc(doc);
634 #ifdef HAVE_ZLIB_H
635 level = get_key_file_integer(filename, "compression_level");
637 if (level > 0) {
638 gint zerror;
640 if (do_compress(NULL, level, xml, len, &outbuf, &outsize, &zerror) == FALSE) {
641 memset(shakey, 0, sizeof(shakey));
642 gcry_free(xml);
644 if (zerror == Z_MEM_ERROR)
645 warnx("%s", strerror(ENOMEM));
646 else
647 warnx("do_compress() failed");
649 gcry_cipher_close(gh);
650 return FALSE;
652 else {
653 gcry_free(xml);
654 xml = outbuf;
655 len = outsize;
658 #endif
660 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
661 gcry_free(key);
662 error = do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter);
663 gcry_cipher_close(gh);
665 if (error) {
666 memset(shakey, 0, sizeof(shakey));
667 warnx("%s", gpg_strerror(error));
668 return FALSE;
671 memset(shakey, 0, sizeof(shakey));
672 return TRUE;
675 gchar *get_key_file_string(const gchar *section, const gchar *what)
677 gchar *val = NULL;
678 GError *gerror = NULL;
680 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
681 val = g_key_file_get_string(keyfileh, section, what, &gerror);
683 if (gerror) {
684 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
685 g_clear_error(&gerror);
688 else {
689 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
690 val = g_key_file_get_string(keyfileh, "default", what, &gerror);
692 if (gerror) {
693 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
694 g_clear_error(&gerror);
699 return val;
702 gint get_key_file_integer(const gchar *section, const gchar *what)
704 gint val = -1;
705 GError *gerror = NULL;
707 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
708 val = g_key_file_get_integer(keyfileh, section, what, &gerror);
710 if (gerror) {
711 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
712 g_clear_error(&gerror);
715 else {
716 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
717 val = g_key_file_get_integer(keyfileh, "default", what, &gerror);
719 if (gerror) {
720 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
721 g_clear_error(&gerror);
726 return val;
729 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
731 gboolean val = FALSE;
732 GError *gerror = NULL;
734 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
735 val = g_key_file_get_boolean(keyfileh, section, what, &gerror);
737 if (gerror) {
738 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
739 g_clear_error(&gerror);
742 else {
743 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
744 val = g_key_file_get_boolean(keyfileh, "default", what, &gerror);
746 if (gerror) {
747 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
748 g_clear_error(&gerror);
753 return val;
756 gchar *expand_homedir(gchar *str)
758 gchar *p = str;
760 if (*p++ == '~')
761 return g_strdup_printf("%s%s", g_get_home_dir(), p);
763 return g_strdup(str);
766 static gchar *_getline(const gchar *file)
768 FILE *fp;
769 gchar buf[LINE_MAX], *p;
770 gchar *str = NULL;
772 if ((fp = fopen(file, "r")) == NULL) {
773 warn("%s", file);
774 return NULL;
777 if ((p = fgets(buf, sizeof(buf), fp)) == NULL) {
778 warnx(N_("%s: empty file?"), file);
779 return NULL;
782 fclose(fp);
784 if (buf[strlen(buf) - 1] == '\n')
785 buf[strlen(buf) - 1] = 0;
787 str = gcry_malloc(strlen(p) + 1);
788 memcpy(str, p, strlen(p));
789 str[strlen(p)] = 0;
790 memset(&buf, 0, sizeof(buf));
791 return str;
794 static gboolean parse_keyfile_key()
796 gsize n;
797 gchar **groups;
798 gchar **p;
799 gchar *str;
801 groups = g_key_file_get_groups(keyfileh, &n);
803 for (p = groups; *p; p++) {
804 GError *error = NULL;
806 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE) {
807 str = g_key_file_get_string(keyfileh, *p, "key", &error);
809 if (!str) {
810 if (error) {
811 warnx("%s", error->message);
812 g_clear_error(&error);
815 continue;
818 do_cache_push(*p, str);
819 g_free(str);
820 continue;
823 if (error) {
824 warnx("%s", error->message);
825 g_clear_error(&error);
826 continue;
829 if (g_key_file_has_key(keyfileh, *p, "key_file", &error) == TRUE) {
830 gchar *t;
831 gchar *file = g_key_file_get_string(keyfileh, *p, "key_file", &error);
833 if (!file) {
834 if (error) {
835 warnx("%s", error->message);
836 g_clear_error(&error);
839 continue;
842 t = expand_homedir(file);
843 g_free(file);
844 file = t;
846 if ((str = _getline(file)) == NULL) {
847 g_free(file);
848 continue;
851 g_free(file);
852 do_cache_push(*p, str);
853 gcry_free(str);
854 continue;
857 if (error) {
858 warnx("%s", error->message);
859 g_clear_error(&error);
863 g_strfreev(groups);
864 return TRUE;
867 static gboolean do_cache_push(const gchar *filename, const gchar *password)
869 guchar *md5file;
870 guchar *key;
871 gint timeout;
872 const gchar *p = filename;
874 while (isspace(*p))
875 p++;
877 if (!*p)
878 return FALSE;
880 if (valid_filename(p) == FALSE) {
881 warnx(N_("%s: invalid characters in filename"), p);
882 return FALSE;
885 md5file = gcry_malloc(16);
886 key = gcry_malloc(gcrykeysize);
887 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
889 if (cache_iscached(md5file) == TRUE) {
890 warnx(N_("%s: file already cached, skipping"), p);
891 gcry_free(md5file);
892 gcry_free(key);
893 return FALSE;
896 if (access(p, R_OK|W_OK) != 0) {
897 gcry_free(md5file);
898 gcry_free(key);
900 if (errno != ENOENT) {
901 warn("%s", p);
902 return FALSE;
905 warn("%s", p);
906 return TRUE;
909 if (!password) {
910 if (get_input(p, key) == FALSE) {
911 gcry_free(key);
912 gcry_free(md5file);
913 return FALSE;
916 else {
917 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
919 if (do_try_xml_decrypt(filename, key) == FALSE) {
920 warnx(N_("%s: invalid password, skipping"), filename);
921 gcry_free(key);
922 gcry_free(md5file);
923 return FALSE;
927 if (cache_add_file(md5file, key) == FALSE) {
928 warnx("%s: %s", p, pwmd_strerror(EPWMD_MAX_SLOTS));
929 gcry_free(key);
930 gcry_free(md5file);
931 return FALSE;
934 timeout = get_key_file_integer(p, "cache_timeout");
935 cache_set_timeout(md5file, timeout);
936 warnx(N_("%s: file added to the cache"), filename);
937 gcry_free(key);
938 gcry_free(md5file);
939 return TRUE;
942 int main(int argc, char *argv[])
944 gint opt;
945 struct sockaddr_un addr;
946 struct passwd *pw = getpwuid(getuid());
947 gchar buf[PATH_MAX];
948 #ifndef MMAP_ANONYMOUS_SHARED
949 gchar shm_path[PATH_MAX];
950 #endif
951 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
952 gchar *socketarg = NULL;
953 gchar *datadir = NULL;
954 gint fd;
955 gboolean n;
956 gchar *p;
957 gchar **cache_push = NULL;
958 gint iter = 0;
959 gchar *import = NULL;
960 gint default_timeout;
961 gint rcfile_spec = 0;
962 gint estatus = EXIT_FAILURE;
963 #ifndef MEM_DEBUG
964 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
965 #endif
966 gint do_unlink = 1;
967 gboolean secure = FALSE;
968 guint ptotal = 0;
969 gint background = 0;
970 gint n_clients = 0;
971 #ifndef DEBUG
972 #ifdef HAVE_SETRLIMIT
973 struct rlimit rl;
975 rl.rlim_cur = rl.rlim_max = 0;
977 if (setrlimit(RLIMIT_CORE, &rl) != 0)
978 err(EXIT_FAILURE, "setrlimit()");
979 #endif
980 #endif
982 #ifdef ENABLE_NLS
983 setlocale(LC_ALL, "");
984 bindtextdomain("pwmd", LOCALEDIR);
985 textdomain("pwmd");
986 #endif
988 #ifndef MEM_DEBUG
989 g_mem_set_vtable(&mtable);
990 #endif
991 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
993 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
994 err(EXIT_FAILURE, "%s", buf);
996 snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
998 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
999 err(EXIT_FAILURE, "%s", buf);
1001 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
1003 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
1004 err(EXIT_FAILURE, "sysconf()");
1006 cache_size = page_size;
1008 while ((opt = getopt(argc, argv, "bI:hvf:D")) != EOF) {
1009 switch (opt) {
1010 case 'b':
1011 background = 1;
1012 break;
1013 case 'D':
1014 secure = TRUE;
1015 break;
1016 case 'I':
1017 import = optarg;
1018 break;
1019 case 'f':
1020 g_free(rcfile);
1021 rcfile = g_strdup(optarg);
1022 rcfile_spec = 1;
1023 break;
1024 case 'v':
1025 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
1026 exit(EXIT_SUCCESS);
1027 case 'h':
1028 default:
1029 usage(argv[0]);
1033 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
1034 exit(EXIT_FAILURE);
1036 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1037 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1039 setup_gcrypt();
1041 if (import) {
1042 opt = xml_import(import, iter);
1043 g_key_file_free(keyfileh);
1044 g_free(rcfile);
1045 exit(opt == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1048 g_key_file_set_list_separator(keyfileh, ',');
1050 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
1051 errx(EXIT_FAILURE, N_("%s: socket_path not defined"), rcfile);
1053 if (*p == '~') {
1054 p++;
1055 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1056 g_free(p);
1057 socketarg = g_strdup(buf);
1059 else
1060 socketarg = p;
1062 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1063 errx(EXIT_FAILURE, N_("%s: data_directory not defined"), rcfile);
1065 datadir = expand_homedir(p);
1066 g_free(p);
1068 if (secure == FALSE && g_key_file_has_key(keyfileh, "default", "disable_list_and_dump", NULL) == TRUE) {
1069 n = g_key_file_get_boolean(keyfileh, "default", "disable_list_and_dump", NULL);
1070 disable_list_and_dump = n;
1072 else
1073 disable_list_and_dump = secure;
1075 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1076 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1077 else
1078 default_timeout = -1;
1080 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1081 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1083 if (cache_size < page_size || cache_size % page_size)
1084 errx(EXIT_FAILURE, N_("cache size must be in multiples of %li"), page_size);
1087 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1088 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1089 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1091 if (n == TRUE) {
1092 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1094 if (*p == '~') {
1095 p++;
1096 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1097 g_free(p);
1098 logfile = g_strdup(buf);
1100 else
1101 logfile = p;
1106 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1107 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1109 if (argc != optind) {
1110 if (cache_push)
1111 ptotal = g_strv_length(cache_push);
1113 for (; optind < argc; optind++) {
1114 if (strv_printf(&cache_push, "%s", argv[optind]) == FALSE)
1115 errx(EXIT_FAILURE, "%s", strerror(ENOMEM));
1119 if (strchr(socketarg, '/') == NULL) {
1120 socketdir = g_get_current_dir();
1121 socketname = g_strdup(socketarg);
1122 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1124 else {
1125 socketname = g_strdup(strrchr(socketarg, '/'));
1126 socketname++;
1127 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1128 socketdir = g_strdup(socketarg);
1129 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1132 #ifdef MMAP_ANONYMOUS_SHARED
1133 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1134 #ifdef MMAP_ANONYMOUS
1135 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == MAP_FAILED) {
1136 #else
1137 MAP_SHARED|MAP_ANON, -1, 0)) == MAP_FAILED) {
1138 #endif
1139 err(EXIT_FAILURE, "mmap()");
1141 #else
1142 snprintf(shm_path, sizeof(shm_path), "/pwmd.%i", pw->pw_uid);
1144 if ((fd = shm_open(shm_path, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
1145 err(EXIT_FAILURE, "shm_open(): %s", shm_path);
1148 * Should be enough for the file cache.
1150 if (ftruncate(fd, cache_size) == -1) {
1151 warn("ftruncate()");
1152 shm_unlink(shm_path);
1153 exit(EXIT_FAILURE);
1156 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
1157 fd, 0)) == MAP_FAILED) {
1158 warn("mmap()");
1159 shm_unlink(shm_path);
1160 exit(EXIT_FAILURE);
1163 close(fd);
1164 #endif
1166 if (mlock(shm_data, cache_size) == -1)
1167 warn("mlock()");
1169 memset(shm_data, 0, cache_size);
1171 if (chdir(datadir)) {
1172 warn("%s", datadir);
1173 close(sfd);
1174 unlink(socketpath);
1175 goto do_exit;
1178 if (parse_keyfile_key() == FALSE)
1179 goto do_exit;
1181 clear_rcfile_key();
1184 * Set the cache entry for a file. Prompts for the password.
1186 if (cache_push) {
1187 for (opt = 0; cache_push[opt]; opt++)
1188 do_cache_push(cache_push[opt], NULL);
1190 g_strfreev(cache_push);
1191 warnx(background ? N_("Done. Daemonizing...") : N_("Done. Waiting for connections..."));
1195 * bind() doesn't like the full pathname of the socket or any non alphanum
1196 * characters so change to the directory where the socket is wanted then
1197 * create it then change to datadir.
1199 if (chdir(socketdir)) {
1200 warn("%s", socketdir);
1201 goto do_exit;
1204 g_free(socketdir);
1206 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1207 warn("socket()");
1208 goto do_exit;
1211 addr.sun_family = AF_UNIX;
1212 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1213 g_free(--socketname);
1215 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
1216 warn("bind()");
1218 if (errno == EADDRINUSE)
1219 warnx(N_("Either there is another pwmd running or '%s' is a \n"
1220 "stale socket. Please remove it manually."), socketpath);
1222 do_unlink = 0;
1223 goto do_exit;
1226 if (chdir(datadir)) {
1227 warn("%s", datadir);
1228 close(sfd);
1229 unlink(socketpath);
1230 goto do_exit;
1233 g_free(datadir);
1235 if (listen(sfd, 0) == -1) {
1236 warn("listen()");
1237 goto do_exit;
1240 signal(SIGCHLD, catchsig);
1241 signal(SIGTERM, catchsig);
1242 signal(SIGINT, catchsig);
1243 signal(SIGHUP, catchsig);
1244 signal(SIGALRM, catchsig);
1245 signal(SIGABRT, catchsig);
1246 signal(SIGUSR1, catchsig);
1247 alarm(1);
1249 if (background) {
1250 switch (fork()) {
1251 case -1:
1252 warn("fork()");
1253 goto do_exit;
1254 case 0:
1255 close(0);
1256 close(1);
1257 close(2);
1258 break;
1259 default:
1260 exit(EXIT_SUCCESS);
1264 log_write("%s %s", PACKAGE_STRING, N_("started"));
1265 send_cache_status(NULL);
1267 while (!quit) {
1268 socklen_t slen = sizeof(struct sockaddr_un);
1269 struct sockaddr_un raddr;
1270 pid_t pid;
1272 if ((fd = accept(sfd, (struct sockaddr *)&raddr, &slen)) == -1) {
1273 if (quit)
1274 break;
1276 if (errno == EAGAIN)
1277 continue;
1279 log_write("accept(): %s", strerror(errno));
1280 continue;
1283 switch ((pid = fork())) {
1284 case -1:
1285 log_write("fork(): %s", strerror(errno));
1286 break;
1287 case 0:
1288 doit(fd);
1289 break;
1290 default:
1291 break;
1294 clients++;
1295 log_write(N_("new connection: pid=%i"), pid);
1296 close(fd);
1299 estatus = EXIT_SUCCESS;
1301 do_exit:
1302 if (socketpath && do_unlink) {
1303 unlink(socketpath);
1304 g_free(socketpath);
1307 signal(SIGUSR1, SIG_IGN);
1308 g_key_file_free(keyfileh);
1309 g_free(rcfile);
1311 if (clients) {
1312 log_write(N_("waiting for all clients to disconnect"));
1314 do {
1315 if (clients != n_clients) {
1316 log_write(N_("%i clients remain"), clients);
1317 n_clients = clients;
1320 select(0, NULL, NULL, NULL, NULL);
1321 } while (clients);
1324 memset(shm_data, 0, cache_size);
1326 if (munmap(shm_data, cache_size) == -1)
1327 log_write("munmap(): %s", strerror(errno));
1329 #ifndef MMAP_ANONYMOUS_SHARED
1330 if (shm_unlink(shm_path) == -1)
1331 log_write("shm_unlink(): %s: %s", shm_path, strerror(errno));
1332 #endif
1334 if (estatus == EXIT_SUCCESS)
1335 log_write(N_("pwmd exiting normally"));
1337 exit(estatus);