Updated NEWS.
[pwmd.git] / src / pwmd.c
blob1cd2733212a67bf5273ee6b8a5e3932af18c37f1
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 reload_rcfile()
67 log_write(N_("reloading configuration file '%s'"), rcfile);
68 g_key_file_free(keyfileh);
69 keyfileh = parse_rcfile(0);
72 gpg_error_t send_syserror(assuan_context_t ctx, int e)
74 gpg_error_t n = gpg_error_from_errno(e);
76 return assuan_set_error(ctx, n, gpg_strerror(n));
79 gpg_error_t send_error(assuan_context_t ctx, gpg_error_t e)
81 gpg_err_code_t n = gpg_err_code(e);
82 gpg_error_t code = gpg_err_make(GPG_ERR_SOURCE_USER_1, n);
84 if (!e)
85 return 0;
87 if (!ctx) {
88 log_write("%s\n", pwmd_strerror(e));
89 return e;
92 if (n == EPWMD_LIBXML_ERROR) {
93 xmlErrorPtr xml_error = xmlGetLastError();
94 return assuan_set_error(ctx, code, xml_error->message);
97 return assuan_set_error(ctx, code, pwmd_strerror(e));
100 void log_write(const gchar *fmt, ...)
102 gchar *args, *line;
103 va_list ap;
104 struct tm *tm;
105 time_t now;
106 gchar tbuf[21];
107 gint fd = -1;
109 if ((!logfile && !isatty(STDERR_FILENO)) || !fmt)
110 return;
112 if (logfile) {
113 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
114 warn(N_("logfile"));
115 return;
119 va_start(ap, fmt);
121 if (g_vasprintf(&args, fmt, ap) == -1) {
122 va_end(ap);
123 return;
126 va_end(ap);
127 time(&now);
128 tm = localtime(&now);
129 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
130 tbuf[sizeof(tbuf) - 1] = 0;
131 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
133 if (!line) {
134 if (logfile)
135 close(fd);
137 g_free(args);
138 return;
141 if (logfile) {
142 write(fd, line, strlen(line));
143 fsync(fd);
144 close(fd);
147 if (isatty(STDERR_FILENO)) {
148 fprintf(stderr, "%s", line);
149 fflush(stderr);
152 g_free(line);
153 g_free(args);
156 static void catchsig(gint sig)
158 gint status;
160 if (sig != SIGALRM && sig != SIGCHLD)
161 log_write(N_("caught signal %i (%s)"), sig, strsignal(sig));
163 switch (sig) {
164 case SIGUSR1:
165 reload_rcfile();
166 break;
167 case SIGABRT:
168 cache_clear(NULL, 2);
169 #ifndef MEM_DEBUG
170 xpanic();
171 #endif
172 exit(EXIT_FAILURE);
173 case SIGALRM:
174 cache_adjust_timer();
175 alarm(1);
176 break;
177 case SIGCHLD:
178 waitpid(-1, &status, 0);
180 if (WIFEXITED(status) || WIFSIGNALED(status))
181 clients--;
183 break;
184 case SIGHUP:
185 log_write(N_("clearing file cache"));
186 cache_clear(NULL, 2);
187 break;
188 default:
189 signal(SIGALRM, SIG_IGN);
190 quit = 1;
191 shutdown(sfd, SHUT_RDWR);
192 close(sfd);
193 break;
197 static void usage(gchar *pn)
199 g_printf(N_(
200 "Usage: %s [-hvDb] [-f <rcfile>] [-I <filename>] [file1] [...]\n"
201 " -b run as a background process\n"
202 " -f load the specified rcfile (~/.pwmd/config)\n"
203 " -I import an XML file and write the encrypted data to stdout\n"
204 " -D disable use of the LIST and DUMP commands\n"
205 " -v version\n"
206 " -h this help text\n"
207 ), pn);
208 exit(EXIT_SUCCESS);
211 #ifndef MEM_DEBUG
212 static int gcry_SecureCheck(const void *ptr)
214 return 1;
216 #endif
218 static void setup_gcrypt()
220 gcry_check_version(NULL);
222 #ifndef MEM_DEBUG
223 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
224 xfree);
225 #endif
227 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
228 NULL) != 0)
229 errx(EXIT_FAILURE, N_("Required AES cipher not supported by libgcrypt."));
231 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
232 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
235 static void child_catchsig(int sig)
237 assuan_context_t ctx = global_client->ctx;
239 switch (sig) {
240 case SIGTERM:
241 cleanup_assuan(ctx);
242 g_free(global_client);
243 assuan_deinit_server(ctx);
244 log_write(N_("exiting"));
245 _exit(EXIT_SUCCESS);
246 break;
247 case SIGABRT:
248 #ifndef MEM_DEBUG
249 xpanic();
250 #endif
251 exit(EXIT_FAILURE);
252 default:
253 break;
258 * Called every time a connection is made.
260 static void doit(int fd)
262 assuan_context_t ctx;
263 int rc;
264 struct client_s *cl = g_malloc0(sizeof(struct client_s));
266 signal(SIGCHLD, SIG_DFL);
267 signal(SIGHUP, SIG_IGN);
268 signal(SIGINT, SIG_IGN);
269 signal(SIGALRM, SIG_IGN);
270 signal(SIGTERM, child_catchsig);
271 signal(SIGABRT, child_catchsig);
272 gpg_err_init();
273 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
274 #ifndef MEM_DEBUG
275 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
276 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
277 xmlInitMemory();
278 #endif
280 #ifdef HAVE_MLOCKALL
281 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
282 log_write("mlockall(): %s", strerror(errno));
283 exit(EXIT_FAILURE);
285 #endif
287 rc = assuan_init_socket_server_ext(&ctx, fd, 2);
289 if (rc) {
290 log_write("%s", gpg_strerror(rc));
291 exit(EXIT_FAILURE);
294 assuan_set_pointer(ctx, cl);
295 rc = register_commands(ctx);
297 if (rc) {
298 log_write("%s", gpg_strerror(rc));
299 exit(EXIT_FAILURE);
302 cl->ctx = ctx;
303 global_client = cl;
306 * It would be nice if there were an assuan_register_pre_cmd_notify().
307 * That way we can see if the file has been modified before calling the
308 * command.
310 rc = assuan_accept(ctx);
312 if (rc) {
313 log_write("%s", gpg_strerror(rc));
314 goto done;
317 rc = assuan_process(ctx);
319 if (rc)
320 log_write("%s", gpg_strerror(rc));
322 done:
323 if (cl->freed == FALSE)
324 cleanup_assuan(ctx);
326 g_free(cl);
327 assuan_deinit_server(ctx);
328 log_write(N_("exiting"));
329 _exit(EXIT_SUCCESS);
333 * Make sure all settings are set to either the specified setting or a
334 * default.
336 static void set_rcfile_defaults(GKeyFile *kf)
338 gchar buf[PATH_MAX];
340 if (g_key_file_has_key(kf, "default", "socket_path", NULL) == FALSE) {
341 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
342 g_key_file_set_string(kf, "default", "socket_path", buf);
345 if (g_key_file_has_key(kf, "default", "data_directory", NULL) == FALSE) {
346 snprintf(buf, sizeof(buf), "~/.pwmd/data");
347 g_key_file_set_string(kf, "default", "data_directory", buf);
350 if (g_key_file_has_key(kf, "default", "log_path", NULL) == FALSE) {
351 snprintf(buf, sizeof(buf), "~/.pwmd/log");
352 g_key_file_set_string(kf, "default", "log_path", buf);
355 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == FALSE)
356 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
358 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == FALSE)
359 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
361 #ifdef HAVE_MLOCKALL
362 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == FALSE)
363 g_key_file_set_boolean(kf, "default", "disable_mlockall", TRUE);
364 #endif
366 if (g_key_file_has_key(kf, "default", "cache_timeout", NULL) == FALSE)
367 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
369 if (g_key_file_has_key(kf, "default", "iterations", NULL) == FALSE)
370 g_key_file_set_integer(kf, "default", "iterations", 0);
372 if (g_key_file_has_key(kf, "default", "disable_list_and_dump", NULL) == FALSE)
373 g_key_file_set_boolean(kf, "default", "disable_list_and_dump", FALSE);
375 if (g_key_file_has_key(kf, "default", "iteration_progress", NULL) == FALSE)
376 g_key_file_set_integer(kf, "default", "iteration_progress", 0);
378 if (g_key_file_has_key(kf, "default", "compression_level", NULL) == FALSE)
379 g_key_file_set_integer(kf, "default", "compression_level", 6);
381 if (g_key_file_has_key(kf, "default", "recursion_depth", NULL) == FALSE)
382 g_key_file_set_integer(kf, "default", "recursion_depth", DEFAULT_RECURSION_DEPTH);
384 #ifdef HAVE_ZLIB_H
385 if (g_key_file_has_key(kf, "default", "zlib_bufsize", NULL) == FALSE)
386 g_key_file_set_integer(kf, "default", "zlib_bufsize", DEFAULT_ZLIB_BUFSIZE);
388 zlib_bufsize = g_key_file_get_integer(kf, "default", "zlib_bufsize", NULL);
389 #endif
391 max_recursion_depth = g_key_file_get_integer(kf, "default", "recursion_depth", NULL);
392 disable_list_and_dump = g_key_file_get_boolean(kf, "default", "disable_list_and_dump", NULL);
394 #ifdef HAVE_MLOCKALL
395 disable_mlock = g_key_file_get_boolean(kf, "default", "disable_mlockall", NULL);
396 #endif
399 static GKeyFile *parse_rcfile(int cmdline)
401 GKeyFile *kf = g_key_file_new();
402 GError *error = NULL;
404 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &error) == FALSE) {
405 log_write("%s: %s", rcfile, error->message);
407 if (cmdline)
408 exit(EXIT_FAILURE);
410 if (error->code == G_FILE_ERROR_NOENT) {
411 g_clear_error(&error);
412 set_rcfile_defaults(kf);
413 return kf;
416 g_clear_error(&error);
417 return NULL;
419 else
420 set_rcfile_defaults(kf);
422 return kf;
425 static gchar *get_password(const gchar *prompt)
427 gchar buf[LINE_MAX], *p;
428 struct termios told, tnew;
429 gchar *key;
431 if (tcgetattr(STDIN_FILENO, &told) == -1)
432 err(EXIT_FAILURE, "tcgetattr()");
434 memcpy(&tnew, &told, sizeof(struct termios));
435 tnew.c_lflag &= ~(ECHO);
436 tnew.c_lflag |= ICANON|ECHONL;
438 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
439 tcsetattr(STDIN_FILENO, TCSANOW, &told);
440 err(EXIT_FAILURE, "tcsetattr()");
443 fprintf(stderr, "%s", prompt);
445 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
446 tcsetattr(STDIN_FILENO, TCSANOW, &told);
447 return NULL;
450 tcsetattr(STDIN_FILENO, TCSANOW, &told);
451 p[strlen(p) - 1] = 0;
453 if (!p || !*p)
454 return NULL;
456 key = gcry_malloc(strlen(p) + 1);
457 sprintf(key, "%s", p);
458 memset(&buf, 0, sizeof(buf));
459 return key;
462 static gboolean do_try_xml_decrypt(const gchar *filename, guchar *key)
464 int fd;
465 struct stat st;
466 gpg_error_t error;
468 if ((fd = open_file(filename, &st)) == -1) {
469 warn("%s", filename);
470 return FALSE;
473 if (st.st_size == 0) {
474 warnx(N_("%s: skipping empty file"), filename);
475 close(fd);
476 return FALSE;
479 error = try_xml_decrypt(NULL, fd, st, key);
480 close(fd);
481 return error ? FALSE : TRUE;
484 static gboolean get_input(const gchar *filename, guchar *key)
486 gint try = 0;
487 gchar *password;
488 gchar *prompt;
490 prompt = g_strdup_printf(N_("Password for '%s': "), filename);
492 again:
493 if ((password = get_password(prompt)) == NULL) {
494 warnx(N_("%s: skipping file"), filename);
495 g_free(prompt);
496 return FALSE;
499 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
500 gcry_free(password);
502 if (do_try_xml_decrypt(filename, key) == FALSE) {
503 if (try++ == 2) {
504 warnx(N_("%s: invalid password, skipping"), filename);
505 g_free(prompt);
506 return FALSE;
508 else {
509 warnx(N_("%s: invalid password"), filename);
510 goto again;
514 g_free(prompt);
515 return TRUE;
518 static gboolean xml_import(const gchar *filename, gint iter)
520 xmlDocPtr doc;
521 gint fd;
522 struct stat st;
523 gint len;
524 xmlChar *xmlbuf;
525 xmlChar *xml;
526 gchar *key = NULL;
527 gchar *key2 = NULL;
528 guchar shakey[gcrykeysize];
529 gcry_cipher_hd_t gh;
530 gpg_error_t error;
531 #ifdef HAVE_ZLIB_H
532 gint level;
533 glong outsize;
534 gpointer outbuf;
535 #endif
537 #ifdef HAVE_MLOCKALL
538 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1)
539 err(EXIT_FAILURE, "mlockall()");
540 #endif
542 if (stat(filename, &st) == -1) {
543 warn("%s", filename);
544 return FALSE;
547 #ifndef MEM_DEBUG
548 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
549 xmlInitMemory();
550 #endif
552 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
553 send_error(NULL, gcryerrno);
554 gcry_cipher_close(gh);
555 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
556 return FALSE;
559 if ((key = get_password(N_("New password: "))) == NULL) {
560 fprintf(stderr, "%s\n", N_("Invalid password."));
561 gcry_cipher_close(gh);
562 return FALSE;
565 if ((key2 = get_password(N_("Verify password: "))) == NULL) {
566 fprintf(stderr, "%s\n", N_("Passwords do not match."));
567 gcry_free(key);
568 gcry_cipher_close(gh);
569 return FALSE;
572 if (g_utf8_collate(key, key2) != 0) {
573 fprintf(stderr, "%s\n", N_("Passwords do not match."));
574 gcry_free(key);
575 gcry_free(key2);
576 gcry_cipher_close(gh);
577 return FALSE;
580 gcry_free(key2);
582 if ((fd = open(filename, O_RDONLY)) == -1) {
583 gcry_free(key);
584 warn("%s", filename);
585 gcry_cipher_close(gh);
586 return FALSE;
589 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
590 gcry_free(key);
591 close(fd);
592 log_write("%s", strerror(ENOMEM));
593 gcry_cipher_close(gh);
594 return FALSE;
597 read(fd, xmlbuf, st.st_size);
598 close(fd);
599 xmlbuf[st.st_size] = 0;
602 * Make sure the document validates.
604 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
605 log_write("xmlReadDoc()");
606 close(fd);
607 gcry_free(key);
608 gcry_free(xmlbuf);
609 gcry_cipher_close(gh);
610 return FALSE;
613 gcry_free(xmlbuf);
614 xmlDocDumpMemory(doc, &xml, &len);
615 xmlFreeDoc(doc);
617 #ifdef HAVE_ZLIB_H
618 level = get_key_file_integer(filename, "compression_level");
620 if (level > 0) {
621 gint zerror;
623 if (do_compress(NULL, level, xml, len, &outbuf, &outsize, &zerror) == FALSE) {
624 memset(shakey, 0, sizeof(shakey));
625 gcry_free(xml);
627 if (zerror == Z_MEM_ERROR)
628 warnx("%s", strerror(ENOMEM));
629 else
630 warnx("do_compress() failed");
632 gcry_cipher_close(gh);
633 return FALSE;
635 else {
636 gcry_free(xml);
637 xml = outbuf;
638 len = outsize;
641 #endif
643 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
644 gcry_free(key);
645 error = do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter);
646 gcry_cipher_close(gh);
648 if (error) {
649 memset(shakey, 0, sizeof(shakey));
650 warnx("%s", gpg_strerror(error));
651 return FALSE;
654 memset(shakey, 0, sizeof(shakey));
655 return TRUE;
658 gchar *get_key_file_string(const gchar *section, const gchar *what)
660 gchar *val = NULL;
661 GError *gerror = NULL;
663 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
664 val = g_key_file_get_string(keyfileh, section, what, &gerror);
666 if (gerror) {
667 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
668 g_clear_error(&gerror);
671 else {
672 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
673 val = g_key_file_get_string(keyfileh, "default", what, &gerror);
675 if (gerror) {
676 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
677 g_clear_error(&gerror);
682 return val;
685 gint get_key_file_integer(const gchar *section, const gchar *what)
687 gint val = -1;
688 GError *gerror = NULL;
690 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
691 val = g_key_file_get_integer(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_integer(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 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
714 gboolean val = FALSE;
715 GError *gerror = NULL;
717 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
718 val = g_key_file_get_boolean(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_boolean(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 gchar *expand_homedir(gchar *str)
741 gchar *p = str;
743 if (*p++ == '~')
744 return g_strdup_printf("%s%s", g_get_home_dir(), p);
746 return g_strdup(str);
749 static gchar *_getline(const gchar *file)
751 FILE *fp;
752 gchar buf[LINE_MAX], *p;
753 gchar *str = NULL;
755 if ((fp = fopen(file, "r")) == NULL) {
756 warn("%s", file);
757 return NULL;
760 if ((p = fgets(buf, sizeof(buf), fp)) == NULL) {
761 warnx(N_("%s: empty file?"), file);
762 return NULL;
765 fclose(fp);
767 if (buf[strlen(buf) - 1] == '\n')
768 buf[strlen(buf) - 1] = 0;
770 str = gcry_malloc(strlen(p) + 1);
771 memcpy(str, p, strlen(p));
772 str[strlen(p)] = 0;
773 memset(&buf, 0, sizeof(buf));
774 return str;
777 static gboolean parse_keyfile_key()
779 gsize n;
780 gchar **groups;
781 gchar **p;
782 gchar *str;
784 groups = g_key_file_get_groups(keyfileh, &n);
786 for (p = groups; *p; p++) {
787 GError *error = NULL;
789 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE) {
790 str = g_key_file_get_string(keyfileh, *p, "key", &error);
792 if (!str) {
793 if (error) {
794 warnx("%s", error->message);
795 g_clear_error(&error);
798 continue;
801 do_cache_push(*p, str);
802 g_free(str);
803 continue;
806 if (error) {
807 warnx("%s", error->message);
808 g_clear_error(&error);
809 continue;
812 if (g_key_file_has_key(keyfileh, *p, "key_file", &error) == TRUE) {
813 gchar *t;
814 gchar *file = g_key_file_get_string(keyfileh, *p, "key_file", &error);
816 if (!file) {
817 if (error) {
818 warnx("%s", error->message);
819 g_clear_error(&error);
822 continue;
825 t = expand_homedir(file);
826 g_free(file);
827 file = t;
829 if ((str = _getline(file)) == NULL) {
830 g_free(file);
831 continue;
834 g_free(file);
835 do_cache_push(*p, str);
836 gcry_free(str);
837 continue;
840 if (error) {
841 warnx("%s", error->message);
842 g_clear_error(&error);
846 g_strfreev(groups);
847 return TRUE;
850 static gboolean do_cache_push(const gchar *filename, const gchar *password)
852 guchar *md5file;
853 guchar *key;
854 gint timeout;
855 const gchar *p = filename;
857 while (isspace(*p))
858 p++;
860 if (!*p)
861 return FALSE;
863 if (valid_filename(p) == FALSE) {
864 warnx(N_("%s: invalid characters in filename"), p);
865 return FALSE;
868 md5file = gcry_malloc(16);
869 key = gcry_malloc(gcrykeysize);
870 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
872 if (cache_iscached(md5file) == TRUE) {
873 warnx(N_("%s: file already cached, skipping"), p);
874 gcry_free(md5file);
875 gcry_free(key);
876 return FALSE;
879 if (access(p, R_OK|W_OK) != 0) {
880 gcry_free(md5file);
881 gcry_free(key);
883 if (errno != ENOENT) {
884 warn("%s", p);
885 return FALSE;
888 warn("%s", p);
889 return TRUE;
892 if (!password) {
893 if (get_input(p, key) == FALSE) {
894 gcry_free(key);
895 gcry_free(md5file);
896 return FALSE;
899 else {
900 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
902 if (do_try_xml_decrypt(filename, key) == FALSE) {
903 warnx(N_("%s: invalid password, skipping"), filename);
904 gcry_free(key);
905 gcry_free(md5file);
906 return FALSE;
910 if (cache_add_file(md5file, key) == FALSE) {
911 warnx("%s: %s", p, pwmd_strerror(EPWMD_MAX_SLOTS));
912 gcry_free(key);
913 gcry_free(md5file);
914 return FALSE;
917 timeout = get_key_file_integer(p, "cache_timeout");
918 cache_set_timeout(md5file, timeout);
919 warnx(N_("%s: file added to the cache"), filename);
920 gcry_free(key);
921 gcry_free(md5file);
922 return TRUE;
925 int main(int argc, char *argv[])
927 gint opt;
928 struct sockaddr_un addr;
929 struct passwd *pw = getpwuid(getuid());
930 gchar buf[PATH_MAX];
931 #ifndef MMAP_ANONYMOUS_SHARED
932 gchar shm_path[PATH_MAX];
933 #endif
934 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
935 gchar *socketarg = NULL;
936 gchar *datadir = NULL;
937 gint fd;
938 gboolean n;
939 gchar *p;
940 gchar **cache_push = NULL;
941 gint iter = 0;
942 gchar *import = NULL;
943 gint default_timeout;
944 gint rcfile_spec = 0;
945 gint estatus = EXIT_FAILURE;
946 #ifndef MEM_DEBUG
947 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
948 #endif
949 gint do_unlink = 1;
950 gboolean secure = FALSE;
951 guint ptotal = 0;
952 gint background = 0;
953 gint n_clients = 0;
954 #ifndef DEBUG
955 #ifdef HAVE_SETRLIMIT
956 struct rlimit rl;
958 rl.rlim_cur = rl.rlim_max = 0;
960 if (setrlimit(RLIMIT_CORE, &rl) != 0)
961 err(EXIT_FAILURE, "setrlimit()");
962 #endif
963 #endif
965 #ifdef ENABLE_NLS
966 setlocale(LC_ALL, "");
967 bindtextdomain("pwmd", LOCALEDIR);
968 textdomain("pwmd");
969 #endif
971 #ifndef MEM_DEBUG
972 g_mem_set_vtable(&mtable);
973 #endif
974 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
976 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
977 err(EXIT_FAILURE, "%s", buf);
979 snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
981 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
982 err(EXIT_FAILURE, "%s", buf);
984 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
986 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
987 err(EXIT_FAILURE, "sysconf()");
989 cache_size = page_size;
991 while ((opt = getopt(argc, argv, "bI:hvf:D")) != EOF) {
992 switch (opt) {
993 case 'b':
994 background = 1;
995 break;
996 case 'D':
997 secure = TRUE;
998 break;
999 case 'I':
1000 import = optarg;
1001 break;
1002 case 'f':
1003 g_free(rcfile);
1004 rcfile = g_strdup(optarg);
1005 rcfile_spec = 1;
1006 break;
1007 case 'v':
1008 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
1009 exit(EXIT_SUCCESS);
1010 case 'h':
1011 default:
1012 usage(argv[0]);
1016 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
1017 exit(EXIT_FAILURE);
1019 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1020 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1022 setup_gcrypt();
1024 if (import) {
1025 opt = xml_import(import, iter);
1026 g_key_file_free(keyfileh);
1027 g_free(rcfile);
1028 exit(opt == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1031 g_key_file_set_list_separator(keyfileh, ',');
1033 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
1034 errx(EXIT_FAILURE, N_("%s: socket_path not defined"), rcfile);
1036 if (*p == '~') {
1037 p++;
1038 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1039 g_free(p);
1040 socketarg = g_strdup(buf);
1042 else
1043 socketarg = p;
1045 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1046 errx(EXIT_FAILURE, N_("%s: data_directory not defined"), rcfile);
1048 datadir = expand_homedir(p);
1049 g_free(p);
1051 if (secure == FALSE && g_key_file_has_key(keyfileh, "default", "disable_list_and_dump", NULL) == TRUE) {
1052 n = g_key_file_get_boolean(keyfileh, "default", "disable_list_and_dump", NULL);
1053 disable_list_and_dump = n;
1055 else
1056 disable_list_and_dump = secure;
1058 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1059 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1060 else
1061 default_timeout = -1;
1063 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1064 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1066 if (cache_size < page_size || cache_size % page_size)
1067 errx(EXIT_FAILURE, N_("cache size must be in multiples of %li"), page_size);
1070 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1071 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1072 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1074 if (n == TRUE) {
1075 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1077 if (*p == '~') {
1078 p++;
1079 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1080 g_free(p);
1081 logfile = g_strdup(buf);
1083 else
1084 logfile = p;
1089 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1090 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1092 if (argc != optind) {
1093 if (cache_push)
1094 ptotal = g_strv_length(cache_push);
1096 for (; optind < argc; optind++) {
1097 if (strv_printf(&cache_push, "%s", argv[optind]) == FALSE)
1098 errx(EXIT_FAILURE, "%s", strerror(ENOMEM));
1102 if (strchr(socketarg, '/') == NULL) {
1103 socketdir = g_get_current_dir();
1104 socketname = g_strdup(socketarg);
1105 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1107 else {
1108 socketname = g_strdup(strrchr(socketarg, '/'));
1109 socketname++;
1110 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1111 socketdir = g_strdup(socketarg);
1112 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1115 #ifdef MMAP_ANONYMOUS_SHARED
1116 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1117 #ifdef MMAP_ANONYMOUS
1118 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == NULL) {
1119 #else
1120 MAP_SHARED|MAP_ANON, -1, 0)) == NULL) {
1121 #endif
1122 err(EXIT_FAILURE, "mmap()");
1124 #else
1125 snprintf(shm_path, sizeof(shm_path), "/pwmd.%i", pw->pw_uid);
1127 if ((fd = shm_open(shm_path, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
1128 err(EXIT_FAILURE, "shm_open(): %s", shm_path);
1131 * Should be enough for the file cache.
1133 if (ftruncate(fd, cache_size) == -1) {
1134 warn("ftruncate()");
1135 shm_unlink(shm_path);
1136 exit(EXIT_FAILURE);
1139 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
1140 fd, 0)) == NULL) {
1141 warn("mmap()");
1142 shm_unlink(shm_path);
1143 exit(EXIT_FAILURE);
1146 close(fd);
1147 #endif
1149 if (mlock(shm_data, cache_size) == -1)
1150 warn("mlock()");
1152 memset(shm_data, 0, cache_size);
1154 if (chdir(datadir)) {
1155 warn("%s", datadir);
1156 close(sfd);
1157 unlink(socketpath);
1158 goto do_exit;
1161 if (parse_keyfile_key() == FALSE)
1162 goto do_exit;
1165 * Set the cache entry for a file. Prompts for the password.
1167 if (cache_push) {
1168 for (opt = 0; cache_push[opt]; opt++)
1169 do_cache_push(cache_push[opt], NULL);
1171 g_strfreev(cache_push);
1172 warnx(background ? N_("Done. Daemonizing...") : N_("Done. Waiting for connections..."));
1176 * bind() doesn't like the full pathname of the socket or any non alphanum
1177 * characters so change to the directory where the socket is wanted then
1178 * create it then change to datadir.
1180 if (chdir(socketdir)) {
1181 warn("%s", socketdir);
1182 goto do_exit;
1185 g_free(socketdir);
1187 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1188 warn("socket()");
1189 goto do_exit;
1192 addr.sun_family = AF_UNIX;
1193 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1194 g_free(--socketname);
1196 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
1197 warn("bind()");
1199 if (errno == EADDRINUSE)
1200 warnx(N_("Either there is another pwmd running or '%s' is a \n"
1201 "stale socket. Please remove it manually."), socketpath);
1203 do_unlink = 0;
1204 goto do_exit;
1207 if (chdir(datadir)) {
1208 warn("%s", datadir);
1209 close(sfd);
1210 unlink(socketpath);
1211 goto do_exit;
1214 g_free(datadir);
1216 if (listen(sfd, 0) == -1) {
1217 warn("listen()");
1218 goto do_exit;
1221 signal(SIGCHLD, catchsig);
1222 signal(SIGTERM, catchsig);
1223 signal(SIGINT, catchsig);
1224 signal(SIGHUP, catchsig);
1225 signal(SIGALRM, catchsig);
1226 signal(SIGABRT, catchsig);
1227 signal(SIGUSR1, catchsig);
1228 alarm(1);
1230 if (background) {
1231 switch (fork()) {
1232 case -1:
1233 warn("fork()");
1234 goto do_exit;
1235 case 0:
1236 close(0);
1237 close(1);
1238 close(2);
1239 break;
1240 default:
1241 exit(EXIT_SUCCESS);
1245 log_write("%s %s", PACKAGE_STRING, N_("started"));
1246 send_cache_status(NULL);
1248 while (!quit) {
1249 socklen_t slen = sizeof(struct sockaddr_un);
1250 struct sockaddr_un raddr;
1251 pid_t pid;
1253 if ((fd = accept(sfd, (struct sockaddr *)&raddr, &slen)) == -1) {
1254 if (quit)
1255 break;
1257 if (errno == EAGAIN)
1258 continue;
1260 log_write("accept(): %s", strerror(errno));
1261 continue;
1264 switch ((pid = fork())) {
1265 case -1:
1266 log_write("fork(): %s", strerror(errno));
1267 break;
1268 case 0:
1269 doit(fd);
1270 break;
1271 default:
1272 break;
1275 clients++;
1276 log_write(N_("new connection: pid=%i"), pid);
1277 close(fd);
1280 estatus = EXIT_SUCCESS;
1282 do_exit:
1283 if (socketpath && do_unlink) {
1284 unlink(socketpath);
1285 g_free(socketpath);
1288 signal(SIGUSR1, SIG_IGN);
1289 g_key_file_free(keyfileh);
1290 g_free(rcfile);
1292 if (clients) {
1293 log_write(N_("waiting for all clients to disconnect"));
1295 do {
1296 if (clients != n_clients) {
1297 log_write(N_("%i clients remain"), clients);
1298 n_clients = clients;
1301 select(0, NULL, NULL, NULL, NULL);
1302 } while (clients);
1305 memset(shm_data, 0, cache_size);
1307 if (munmap(shm_data, cache_size) == -1)
1308 log_write("munmap(): %s", strerror(errno));
1310 #ifndef MMAP_ANONYMOUS_SHARED
1311 if (shm_unlink(shm_path) == -1)
1312 log_write("shm_unlink(): %s: %s", shm_path, strerror(errno));
1313 #endif
1315 if (estatus == EXIT_SUCCESS)
1316 log_write(N_("pwmd exiting normally"));
1318 exit(estatus);