Added the COMPRESS and DECOMPRESS status messages. Both output the
[pwmd.git] / src / pwmd.c
blobfb10fa6351c1a92d7c91cdfc5e92a8955fda0b7c
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);
120 g_vasprintf(&args, fmt, ap);
121 va_end(ap);
122 time(&now);
123 tm = localtime(&now);
124 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
125 tbuf[sizeof(tbuf) - 1] = 0;
126 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
128 if (logfile) {
129 write(fd, line, strlen(line));
130 fsync(fd);
131 close(fd);
134 if (isatty(STDERR_FILENO)) {
135 fprintf(stderr, "%s", line);
136 fflush(stderr);
139 g_free(line);
140 g_free(args);
143 static void catchsig(gint sig)
145 gint status;
147 if (sig != SIGALRM && sig != SIGCHLD)
148 log_write(N_("caught signal %i (%s)"), sig, strsignal(sig));
150 switch (sig) {
151 case SIGUSR1:
152 reload_rcfile();
153 break;
154 case SIGABRT:
155 cache_clear(NULL, 2);
156 #ifndef MEM_DEBUG
157 xpanic();
158 #endif
159 exit(EXIT_FAILURE);
160 case SIGALRM:
161 cache_adjust_timer();
162 alarm(1);
163 break;
164 case SIGCHLD:
165 waitpid(-1, &status, 0);
167 if (WIFEXITED(status) || WIFSIGNALED(status))
168 clients--;
170 break;
171 case SIGHUP:
172 log_write(N_("clearing file cache"));
173 cache_clear(NULL, 2);
174 break;
175 default:
176 signal(SIGALRM, SIG_IGN);
177 quit = 1;
178 shutdown(sfd, SHUT_RDWR);
179 close(sfd);
180 break;
184 static void usage(gchar *pn)
186 g_printf(N_(
187 "Usage: %s [-hvDb] [-f <rcfile>] [-I <filename>] [file1] [...]\n"
188 " -b run as a background process\n"
189 " -f load the specified rcfile (~/.pwmd/config)\n"
190 " -I import an XML file and write the encrypted data to stdout\n"
191 " -D disable use of the LIST and DUMP commands\n"
192 " -v version\n"
193 " -h this help text\n"
194 ), pn);
195 exit(EXIT_SUCCESS);
198 #ifndef MEM_DEBUG
199 static int gcry_SecureCheck(const void *ptr)
201 return 1;
203 #endif
205 static void setup_gcrypt()
207 gcry_check_version(NULL);
209 #ifndef MEM_DEBUG
210 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
211 xfree);
212 #endif
214 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
215 NULL) != 0)
216 errx(EXIT_FAILURE, N_("Required AES cipher not supported by libgcrypt."));
218 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
219 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
222 static void child_catchsig(int sig)
224 assuan_context_t ctx = global_client->ctx;
226 switch (sig) {
227 case SIGTERM:
228 cleanup_assuan(ctx);
229 g_free(global_client);
230 assuan_deinit_server(ctx);
231 log_write(N_("exiting"));
232 _exit(EXIT_SUCCESS);
233 break;
234 case SIGABRT:
235 #ifndef MEM_DEBUG
236 xpanic();
237 #endif
238 exit(EXIT_FAILURE);
239 default:
240 break;
245 * Called every time a connection is made.
247 static void doit(int fd)
249 assuan_context_t ctx;
250 int rc;
251 struct client_s *cl = g_malloc0(sizeof(struct client_s));
253 signal(SIGCHLD, SIG_DFL);
254 signal(SIGHUP, SIG_IGN);
255 signal(SIGINT, SIG_IGN);
256 signal(SIGALRM, SIG_IGN);
257 signal(SIGTERM, child_catchsig);
258 signal(SIGABRT, child_catchsig);
259 gpg_err_init();
260 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
261 #ifndef MEM_DEBUG
262 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
263 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
264 xmlInitMemory();
265 #endif
267 #ifdef HAVE_MLOCKALL
268 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1) {
269 log_write("mlockall(): %s", strerror(errno));
270 exit(EXIT_FAILURE);
272 #endif
274 rc = assuan_init_socket_server_ext(&ctx, fd, 2);
276 if (rc) {
277 log_write("%s", gpg_strerror(rc));
278 exit(EXIT_FAILURE);
281 assuan_set_pointer(ctx, cl);
282 rc = register_commands(ctx);
284 if (rc) {
285 log_write("%s", gpg_strerror(rc));
286 exit(EXIT_FAILURE);
289 cl->ctx = ctx;
290 global_client = cl;
293 * It would be nice if there were an assuan_register_pre_cmd_notify().
294 * That way we can see if the file has been modified before calling the
295 * command.
297 rc = assuan_accept(ctx);
299 if (rc) {
300 log_write("%s", gpg_strerror(rc));
301 goto done;
304 rc = assuan_process(ctx);
306 if (rc)
307 log_write("%s", gpg_strerror(rc));
309 done:
310 if (cl->freed == FALSE)
311 cleanup_assuan(ctx);
313 g_free(cl);
314 assuan_deinit_server(ctx);
315 log_write(N_("exiting"));
316 _exit(EXIT_SUCCESS);
320 * Make sure all settings are set to either the specified setting or a
321 * default.
323 static void set_rcfile_defaults(GKeyFile *kf)
325 gchar buf[PATH_MAX];
327 if (g_key_file_has_key(kf, "default", "socket_path", NULL) == FALSE) {
328 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
329 g_key_file_set_string(kf, "default", "socket_path", buf);
332 if (g_key_file_has_key(kf, "default", "data_directory", NULL) == FALSE) {
333 snprintf(buf, sizeof(buf), "~/.pwmd/data");
334 g_key_file_set_string(kf, "default", "data_directory", buf);
337 if (g_key_file_has_key(kf, "default", "log_path", NULL) == FALSE) {
338 snprintf(buf, sizeof(buf), "~/.pwmd/log");
339 g_key_file_set_string(kf, "default", "log_path", buf);
342 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == FALSE)
343 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
345 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == FALSE)
346 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
348 #ifdef HAVE_MLOCKALL
349 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == FALSE)
350 g_key_file_set_boolean(kf, "default", "disable_mlockall", FALSE);
351 #endif
353 if (g_key_file_has_key(kf, "default", "cache_timeout", NULL) == FALSE)
354 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
356 if (g_key_file_has_key(kf, "default", "iterations", NULL) == FALSE)
357 g_key_file_set_integer(kf, "default", "iterations", 0);
359 if (g_key_file_has_key(kf, "default", "disable_list_and_dump", NULL) == FALSE)
360 g_key_file_set_boolean(kf, "default", "disable_list_and_dump", FALSE);
362 if (g_key_file_has_key(kf, "default", "iteration_progress", NULL) == FALSE)
363 g_key_file_set_integer(kf, "default", "iteration_progress", 0);
365 if (g_key_file_has_key(kf, "default", "compression_level", NULL) == FALSE)
366 g_key_file_set_integer(kf, "default", "compression_level", 6);
368 if (g_key_file_has_key(kf, "default", "recursion_depth", NULL) == FALSE)
369 g_key_file_set_integer(kf, "default", "recursion_depth", DEFAULT_RECURSION_DEPTH);
371 #ifdef HAVE_ZLIB_H
372 if (g_key_file_has_key(kf, "default", "zlib_bufsize", NULL) == FALSE)
373 g_key_file_set_integer(kf, "default", "zlib_bufsize", DEFAULT_ZLIB_BUFSIZE);
375 zlib_bufsize = g_key_file_get_integer(kf, "default", "zlib_bufsize", NULL);
376 #endif
378 max_recursion_depth = g_key_file_get_integer(kf, "default", "recursion_depth", NULL);
379 disable_list_and_dump = g_key_file_get_boolean(kf, "default", "disable_list_and_dump", NULL);
381 #ifdef HAVE_MLOCKALL
382 disable_mlock = g_key_file_get_boolean(kf, "default", "disable_mlockall", NULL);
383 #endif
386 static GKeyFile *parse_rcfile(int cmdline)
388 GKeyFile *kf = g_key_file_new();
389 GError *error = NULL;
391 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &error) == FALSE) {
392 log_write("%s: %s", rcfile, error->message);
394 if (cmdline)
395 exit(EXIT_FAILURE);
397 if (error->code == G_FILE_ERROR_NOENT) {
398 g_clear_error(&error);
399 set_rcfile_defaults(kf);
400 return kf;
403 g_clear_error(&error);
404 return NULL;
406 else
407 set_rcfile_defaults(kf);
409 return kf;
412 static gchar *get_password(const gchar *prompt)
414 gchar buf[LINE_MAX], *p;
415 struct termios told, tnew;
416 gchar *key;
418 if (tcgetattr(STDIN_FILENO, &told) == -1)
419 err(EXIT_FAILURE, "tcgetattr()");
421 memcpy(&tnew, &told, sizeof(struct termios));
422 tnew.c_lflag &= ~(ECHO);
423 tnew.c_lflag |= ICANON|ECHONL;
425 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
426 tcsetattr(STDIN_FILENO, TCSANOW, &told);
427 err(EXIT_FAILURE, "tcsetattr()");
430 fprintf(stderr, "%s", prompt);
432 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
433 tcsetattr(STDIN_FILENO, TCSANOW, &told);
434 return NULL;
437 tcsetattr(STDIN_FILENO, TCSANOW, &told);
438 p[strlen(p) - 1] = 0;
440 if (!p || !*p)
441 return NULL;
443 key = gcry_malloc(strlen(p) + 1);
444 sprintf(key, "%s", p);
445 memset(&buf, 0, sizeof(buf));
446 return key;
449 static gboolean do_try_xml_decrypt(const gchar *filename, guchar *key)
451 int fd;
452 struct stat st;
453 gpg_error_t error;
455 if ((fd = open_file(filename, &st)) == -1) {
456 warn("%s", filename);
457 return FALSE;
460 if (st.st_size == 0) {
461 warnx(N_("%s: skipping empty file"), filename);
462 close(fd);
463 return FALSE;
466 error = try_xml_decrypt(NULL, fd, st, key);
467 close(fd);
468 return error ? FALSE : TRUE;
471 static gboolean get_input(const gchar *filename, guchar *key)
473 gint try = 0;
474 gchar *password;
475 gchar *prompt;
477 prompt = g_strdup_printf(N_("Password for '%s': "), filename);
479 again:
480 if ((password = get_password(prompt)) == NULL) {
481 warnx(N_("%s: skipping file"), filename);
482 g_free(prompt);
483 return FALSE;
486 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
487 gcry_free(password);
489 if (do_try_xml_decrypt(filename, key) == FALSE) {
490 if (try++ == 2) {
491 warnx(N_("%s: invalid password, skipping"), filename);
492 g_free(prompt);
493 return FALSE;
495 else {
496 warnx(N_("%s: invalid password"), filename);
497 goto again;
501 g_free(prompt);
502 return TRUE;
505 static gboolean xml_import(const gchar *filename, gint iter)
507 xmlDocPtr doc;
508 gint fd;
509 struct stat st;
510 gint len;
511 xmlChar *xmlbuf;
512 xmlChar *xml;
513 gchar *key = NULL;
514 gchar *key2 = NULL;
515 guchar shakey[gcrykeysize];
516 gcry_cipher_hd_t gh;
517 gpg_error_t error;
518 #ifdef HAVE_ZLIB_H
519 gint level;
520 glong outsize;
521 gpointer outbuf;
522 #endif
524 #ifdef HAVE_MLOCKALL
525 if (disable_mlock == FALSE && mlockall(MCL_FUTURE) == -1)
526 err(EXIT_FAILURE, "mlockall()");
527 #endif
529 if (stat(filename, &st) == -1) {
530 warn("%s", filename);
531 return FALSE;
534 #ifndef MEM_DEBUG
535 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
536 xmlInitMemory();
537 #endif
539 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
540 send_error(NULL, gcryerrno);
541 gcry_cipher_close(gh);
542 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
543 return FALSE;
546 if ((key = get_password(N_("New password: "))) == NULL) {
547 fprintf(stderr, "%s\n", N_("Invalid password."));
548 gcry_cipher_close(gh);
549 return FALSE;
552 if ((key2 = get_password(N_("Verify password: "))) == NULL) {
553 fprintf(stderr, "%s\n", N_("Passwords do not match."));
554 gcry_free(key);
555 gcry_cipher_close(gh);
556 return FALSE;
559 if (g_utf8_collate(key, key2) != 0) {
560 fprintf(stderr, "%s\n", N_("Passwords do not match."));
561 gcry_free(key);
562 gcry_free(key2);
563 gcry_cipher_close(gh);
564 return FALSE;
567 gcry_free(key2);
569 if ((fd = open(filename, O_RDONLY)) == -1) {
570 gcry_free(key);
571 warn("%s", filename);
572 gcry_cipher_close(gh);
573 return FALSE;
576 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
577 gcry_free(key);
578 close(fd);
579 log_write("%s", strerror(ENOMEM));
580 gcry_cipher_close(gh);
581 return FALSE;
584 read(fd, xmlbuf, st.st_size);
585 close(fd);
586 xmlbuf[st.st_size] = 0;
589 * Make sure the document validates.
591 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
592 log_write("xmlReadDoc()");
593 close(fd);
594 gcry_free(key);
595 gcry_free(xmlbuf);
596 gcry_cipher_close(gh);
597 return FALSE;
600 gcry_free(xmlbuf);
601 xmlDocDumpMemory(doc, &xml, &len);
602 xmlFreeDoc(doc);
604 #ifdef HAVE_ZLIB_H
605 level = get_key_file_integer(filename, "compression_level");
607 if (level > 0) {
608 gint zerror;
610 if (do_compress(NULL, level, xml, len, &outbuf, &outsize, &zerror) == FALSE) {
611 memset(shakey, 0, sizeof(shakey));
612 gcry_free(xml);
614 if (zerror == Z_MEM_ERROR)
615 warnx("%s", strerror(ENOMEM));
616 else
617 warnx("do_compress() failed");
619 gcry_cipher_close(gh);
620 return FALSE;
622 else {
623 gcry_free(xml);
624 xml = outbuf;
625 len = outsize;
628 #endif
630 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
631 gcry_free(key);
632 error = do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter);
633 gcry_cipher_close(gh);
635 if (error) {
636 memset(shakey, 0, sizeof(shakey));
637 warnx("%s", gpg_strerror(error));
638 return FALSE;
641 memset(shakey, 0, sizeof(shakey));
642 return TRUE;
645 gchar *get_key_file_string(const gchar *section, const gchar *what)
647 gchar *val = NULL;
648 GError *gerror = NULL;
650 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
651 val = g_key_file_get_string(keyfileh, section, what, &gerror);
653 if (gerror) {
654 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
655 g_clear_error(&gerror);
658 else {
659 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
660 val = g_key_file_get_string(keyfileh, "default", what, &gerror);
662 if (gerror) {
663 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
664 g_clear_error(&gerror);
669 return val;
672 gint get_key_file_integer(const gchar *section, const gchar *what)
674 gint val = -1;
675 GError *gerror = NULL;
677 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
678 val = g_key_file_get_integer(keyfileh, section, what, &gerror);
680 if (gerror) {
681 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
682 g_clear_error(&gerror);
685 else {
686 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
687 val = g_key_file_get_integer(keyfileh, "default", what, &gerror);
689 if (gerror) {
690 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
691 g_clear_error(&gerror);
696 return val;
699 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
701 gboolean val = FALSE;
702 GError *gerror = NULL;
704 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
705 val = g_key_file_get_boolean(keyfileh, section, what, &gerror);
707 if (gerror) {
708 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
709 g_clear_error(&gerror);
712 else {
713 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
714 val = g_key_file_get_boolean(keyfileh, "default", what, &gerror);
716 if (gerror) {
717 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
718 g_clear_error(&gerror);
723 return val;
726 gchar *expand_homedir(gchar *str)
728 gchar *p = str;
730 if (*p++ == '~')
731 return g_strdup_printf("%s%s", g_get_home_dir(), p);
733 return g_strdup(str);
736 static gchar *_getline(const gchar *file)
738 FILE *fp;
739 gchar buf[LINE_MAX], *p;
740 gchar *str = NULL;
742 if ((fp = fopen(file, "r")) == NULL) {
743 warn("%s", file);
744 return NULL;
747 if ((p = fgets(buf, sizeof(buf), fp)) == NULL) {
748 warnx(N_("%s: empty file?"), file);
749 return NULL;
752 fclose(fp);
754 if (buf[strlen(buf) - 1] == '\n')
755 buf[strlen(buf) - 1] = 0;
757 str = gcry_malloc(strlen(p) + 1);
758 memcpy(str, p, strlen(p));
759 str[strlen(p)] = 0;
760 memset(&buf, 0, sizeof(buf));
761 return str;
764 static gboolean parse_keyfile_key()
766 gsize n;
767 gchar **groups;
768 gchar **p;
769 gchar *str;
771 groups = g_key_file_get_groups(keyfileh, &n);
773 for (p = groups; *p; p++) {
774 GError *error = NULL;
776 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE) {
777 str = g_key_file_get_string(keyfileh, *p, "key", &error);
779 if (!str) {
780 if (error) {
781 warnx("%s", error->message);
782 g_clear_error(&error);
785 continue;
788 do_cache_push(*p, str);
789 g_free(str);
790 continue;
793 if (error) {
794 warnx("%s", error->message);
795 g_clear_error(&error);
796 continue;
799 if (g_key_file_has_key(keyfileh, *p, "key_file", &error) == TRUE) {
800 gchar *t;
801 gchar *file = g_key_file_get_string(keyfileh, *p, "key_file", &error);
803 if (!file) {
804 if (error) {
805 warnx("%s", error->message);
806 g_clear_error(&error);
809 continue;
812 t = expand_homedir(file);
813 g_free(file);
814 file = t;
816 if ((str = _getline(file)) == NULL) {
817 g_free(file);
818 continue;
821 g_free(file);
822 do_cache_push(*p, str);
823 gcry_free(str);
824 continue;
827 if (error) {
828 warnx("%s", error->message);
829 g_clear_error(&error);
833 g_strfreev(groups);
834 return TRUE;
837 static gboolean do_cache_push(const gchar *filename, const gchar *password)
839 guchar *md5file;
840 guchar *key;
841 gint timeout;
842 const gchar *p = filename;
844 while (isspace(*p))
845 p++;
847 if (!*p)
848 return FALSE;
850 if (valid_filename(p) == FALSE) {
851 warnx(N_("%s: invalid characters in filename"), p);
852 return FALSE;
855 md5file = gcry_malloc(16);
856 key = gcry_malloc(gcrykeysize);
857 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
859 if (cache_iscached(md5file) == TRUE) {
860 warnx(N_("%s: file already cached, skipping"), p);
861 gcry_free(md5file);
862 gcry_free(key);
863 return FALSE;
866 if (access(p, R_OK|W_OK) != 0) {
867 gcry_free(md5file);
868 gcry_free(key);
870 if (errno != ENOENT) {
871 warn("%s", p);
872 return FALSE;
875 warn("%s", p);
876 return TRUE;
879 if (!password) {
880 if (get_input(p, key) == FALSE) {
881 gcry_free(key);
882 gcry_free(md5file);
883 return FALSE;
886 else {
887 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
889 if (do_try_xml_decrypt(filename, key) == FALSE) {
890 warnx(N_("%s: invalid password, skipping"), filename);
891 gcry_free(key);
892 gcry_free(md5file);
893 return FALSE;
897 if (cache_add_file(md5file, key) == FALSE) {
898 warnx("%s: %s", p, pwmd_strerror(EPWMD_MAX_SLOTS));
899 gcry_free(key);
900 gcry_free(md5file);
901 return FALSE;
904 timeout = get_key_file_integer(p, "cache_timeout");
905 cache_set_timeout(md5file, timeout);
906 warnx(N_("%s: file added to the cache"), filename);
907 gcry_free(key);
908 gcry_free(md5file);
909 return TRUE;
912 int main(int argc, char *argv[])
914 gint opt;
915 struct sockaddr_un addr;
916 struct passwd *pw = getpwuid(getuid());
917 gchar buf[PATH_MAX];
918 #ifndef MMAP_ANONYMOUS_SHARED
919 gchar shm_path[PATH_MAX];
920 #endif
921 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
922 gchar *socketarg = NULL;
923 gchar *datadir = NULL;
924 gint fd;
925 gboolean n;
926 gchar *p;
927 gchar **cache_push = NULL;
928 gint iter = 0;
929 gchar *import = NULL;
930 gint default_timeout;
931 gint rcfile_spec = 0;
932 gint estatus = EXIT_FAILURE;
933 #ifndef MEM_DEBUG
934 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
935 #endif
936 gint do_unlink = 1;
937 gboolean secure = FALSE;
938 guint ptotal = 0;
939 gint background = 0;
940 gint n_clients = 0;
941 #ifndef DEBUG
942 #ifdef HAVE_SETRLIMIT
943 struct rlimit rl;
945 rl.rlim_cur = rl.rlim_max = 0;
947 if (setrlimit(RLIMIT_CORE, &rl) != 0)
948 err(EXIT_FAILURE, "setrlimit()");
949 #endif
950 #endif
952 #ifdef ENABLE_NLS
953 setlocale(LC_ALL, "");
954 bindtextdomain("pwmd", LOCALEDIR);
955 textdomain("pwmd");
956 #endif
958 #ifndef MEM_DEBUG
959 g_mem_set_vtable(&mtable);
960 #endif
961 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
963 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
964 err(EXIT_FAILURE, "%s", buf);
966 snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
968 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
969 err(EXIT_FAILURE, "%s", buf);
971 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
973 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
974 err(EXIT_FAILURE, "sysconf()");
976 cache_size = page_size;
978 while ((opt = getopt(argc, argv, "bI:hvf:D")) != EOF) {
979 switch (opt) {
980 case 'b':
981 background = 1;
982 break;
983 case 'D':
984 secure = TRUE;
985 break;
986 case 'I':
987 import = optarg;
988 break;
989 case 'f':
990 g_free(rcfile);
991 rcfile = g_strdup(optarg);
992 rcfile_spec = 1;
993 break;
994 case 'v':
995 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
996 exit(EXIT_SUCCESS);
997 case 'h':
998 default:
999 usage(argv[0]);
1003 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
1004 exit(EXIT_FAILURE);
1006 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1007 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1009 setup_gcrypt();
1011 if (import) {
1012 opt = xml_import(import, iter);
1013 g_key_file_free(keyfileh);
1014 g_free(rcfile);
1015 exit(opt == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1018 g_key_file_set_list_separator(keyfileh, ',');
1020 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
1021 errx(EXIT_FAILURE, N_("%s: socket_path not defined"), rcfile);
1023 if (*p == '~') {
1024 p++;
1025 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1026 g_free(p);
1027 socketarg = g_strdup(buf);
1029 else
1030 socketarg = p;
1032 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1033 errx(EXIT_FAILURE, N_("%s: data_directory not defined"), rcfile);
1035 datadir = expand_homedir(p);
1036 g_free(p);
1038 if (secure == FALSE && g_key_file_has_key(keyfileh, "default", "disable_list_and_dump", NULL) == TRUE) {
1039 n = g_key_file_get_boolean(keyfileh, "default", "disable_list_and_dump", NULL);
1040 disable_list_and_dump = n;
1042 else
1043 disable_list_and_dump = secure;
1045 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1046 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1047 else
1048 default_timeout = -1;
1050 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1051 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1053 if (cache_size < page_size || cache_size % page_size)
1054 errx(EXIT_FAILURE, N_("cache size must be in multiples of %li"), page_size);
1057 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1058 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1059 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1061 if (n == TRUE) {
1062 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1064 if (*p == '~') {
1065 p++;
1066 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1067 g_free(p);
1068 logfile = g_strdup(buf);
1070 else
1071 logfile = p;
1076 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1077 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1079 if (argc != optind) {
1080 if (cache_push)
1081 ptotal = g_strv_length(cache_push);
1083 for (; optind < argc; optind++)
1084 strv_printf(&cache_push, "%s", argv[optind]);
1087 if (strchr(socketarg, '/') == NULL) {
1088 socketdir = g_get_current_dir();
1089 socketname = g_strdup(socketarg);
1090 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1092 else {
1093 socketname = g_strdup(strrchr(socketarg, '/'));
1094 socketname++;
1095 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1096 socketdir = g_strdup(socketarg);
1097 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1100 #ifdef MMAP_ANONYMOUS_SHARED
1101 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1102 #ifdef MMAP_ANONYMOUS
1103 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == NULL) {
1104 #else
1105 MAP_SHARED|MAP_ANON, -1, 0)) == NULL) {
1106 #endif
1107 err(EXIT_FAILURE, "mmap()");
1109 #else
1110 snprintf(shm_path, sizeof(shm_path), "/pwmd.%i", pw->pw_uid);
1112 if ((fd = shm_open(shm_path, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
1113 err(EXIT_FAILURE, "shm_open(): %s", shm_path);
1116 * Should be enough for the file cache.
1118 if (ftruncate(fd, cache_size) == -1) {
1119 warn("ftruncate()");
1120 shm_unlink(shm_path);
1121 exit(EXIT_FAILURE);
1124 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
1125 fd, 0)) == NULL) {
1126 warn("mmap()");
1127 shm_unlink(shm_path);
1128 exit(EXIT_FAILURE);
1131 close(fd);
1132 #endif
1134 if (mlock(shm_data, cache_size) == -1)
1135 warn("mlock()");
1137 memset(shm_data, 0, cache_size);
1139 if (chdir(datadir)) {
1140 warn("%s", datadir);
1141 close(sfd);
1142 unlink(socketpath);
1143 goto do_exit;
1146 if (parse_keyfile_key() == FALSE)
1147 goto do_exit;
1150 * Set the cache entry for a file. Prompts for the password.
1152 if (cache_push) {
1153 for (opt = 0; cache_push[opt]; opt++)
1154 do_cache_push(cache_push[opt], NULL);
1156 g_strfreev(cache_push);
1157 warnx(background ? N_("Done. Daemonizing...") : N_("Done. Waiting for connections..."));
1161 * bind() doesn't like the full pathname of the socket or any non alphanum
1162 * characters so change to the directory where the socket is wanted then
1163 * create it then change to datadir.
1165 if (chdir(socketdir)) {
1166 warn("%s", socketdir);
1167 goto do_exit;
1170 g_free(socketdir);
1172 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1173 warn("socket()");
1174 goto do_exit;
1177 addr.sun_family = AF_UNIX;
1178 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1179 g_free(--socketname);
1181 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
1182 warn("bind()");
1184 if (errno == EADDRINUSE)
1185 warnx(N_("Either there is another pwmd running or '%s' is a \n"
1186 "stale socket. Please remove it manually."), socketpath);
1188 do_unlink = 0;
1189 goto do_exit;
1192 if (chdir(datadir)) {
1193 warn("%s", datadir);
1194 close(sfd);
1195 unlink(socketpath);
1196 goto do_exit;
1199 g_free(datadir);
1201 if (listen(sfd, 0) == -1) {
1202 warn("listen()");
1203 goto do_exit;
1206 signal(SIGCHLD, catchsig);
1207 signal(SIGTERM, catchsig);
1208 signal(SIGINT, catchsig);
1209 signal(SIGHUP, catchsig);
1210 signal(SIGALRM, catchsig);
1211 signal(SIGABRT, catchsig);
1212 signal(SIGUSR1, catchsig);
1213 alarm(1);
1215 if (background) {
1216 switch (fork()) {
1217 case -1:
1218 warn("fork()");
1219 goto do_exit;
1220 case 0:
1221 close(0);
1222 close(1);
1223 close(2);
1224 break;
1225 default:
1226 exit(EXIT_SUCCESS);
1230 log_write("%s %s", PACKAGE_STRING, N_("started"));
1231 send_cache_status(NULL);
1233 while (!quit) {
1234 socklen_t slen = sizeof(struct sockaddr_un);
1235 struct sockaddr_un raddr;
1236 pid_t pid;
1238 if ((fd = accept(sfd, (struct sockaddr *)&raddr, &slen)) == -1) {
1239 if (quit)
1240 break;
1242 if (errno == EAGAIN)
1243 continue;
1245 log_write("accept(): %s", strerror(errno));
1246 continue;
1249 switch ((pid = fork())) {
1250 case -1:
1251 log_write("fork(): %s", strerror(errno));
1252 break;
1253 case 0:
1254 doit(fd);
1255 break;
1256 default:
1257 break;
1260 clients++;
1261 log_write(N_("new connection: pid=%i"), pid);
1262 close(fd);
1265 estatus = EXIT_SUCCESS;
1267 do_exit:
1268 if (socketpath && do_unlink) {
1269 unlink(socketpath);
1270 g_free(socketpath);
1273 signal(SIGUSR1, SIG_IGN);
1274 g_key_file_free(keyfileh);
1275 g_free(rcfile);
1277 if (clients) {
1278 log_write(N_("waiting for all clients to disconnect"));
1280 do {
1281 if (clients != n_clients) {
1282 log_write(N_("%i clients remain"), clients);
1283 n_clients = clients;
1286 select(0, NULL, NULL, NULL, NULL);
1287 } while (clients);
1290 memset(shm_data, 0, cache_size);
1292 if (munmap(shm_data, cache_size) == -1)
1293 log_write("munmap(): %s", strerror(errno));
1295 #ifndef MMAP_ANONYMOUS_SHARED
1296 if (shm_unlink(shm_path) == -1)
1297 log_write("shm_unlink(): %s: %s", shm_path, strerror(errno));
1298 #endif
1300 if (estatus == EXIT_SUCCESS)
1301 log_write(N_("pwmd exiting normally"));
1303 exit(estatus);