Some portability fixes:
[pwmd.git] / src / pwmd.c
blobdafb9bb95bda90f34f91e1efc741fbb09aee2ce2
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 #include "mem.h"
51 #include "xml.h"
52 #include "common.h"
53 #include "commands.h"
54 #include "pwmd_error.h"
55 #include "cache.h"
56 #include "pwmd.h"
58 static void reload_rcfile()
60 log_write("reloading configuration file '%s'", rcfile);
61 g_key_file_free(keyfileh);
62 keyfileh = parse_rcfile(0);
65 gpg_error_t send_syserror(assuan_context_t ctx, int e)
67 gpg_error_t n = gpg_error_from_errno(e);
69 return assuan_set_error(ctx, n, gpg_strerror(n));
72 gpg_error_t send_error(assuan_context_t ctx, gpg_error_t pwmd_errno)
74 gpg_err_code_t n = gpg_err_code(pwmd_errno);
75 gpg_error_t code = gpg_err_make(GPG_ERR_SOURCE_USER_1, n);
77 if (pwmd_errno == 0)
78 return 0;
80 if (!ctx) {
81 fprintf(stderr, "%s\n", pwmd_strerror(pwmd_errno));
82 return pwmd_errno;
85 return assuan_set_error(ctx, code, pwmd_strerror(pwmd_errno));
88 void log_write(const gchar *fmt, ...)
90 gchar *args, *line;
91 va_list ap;
92 struct tm *tm;
93 time_t now;
94 gchar tbuf[21];
95 gint fd = -1;
97 if ((!logfile && !isatty(STDERR_FILENO)) || !fmt)
98 return;
100 if (logfile) {
101 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
102 warn("logfile");
103 return;
107 va_start(ap, fmt);
108 g_vasprintf(&args, fmt, ap);
109 va_end(ap);
110 time(&now);
111 tm = localtime(&now);
112 strftime(tbuf, sizeof(tbuf), "%b %d %Y %H:%M:%S ", tm);
113 tbuf[sizeof(tbuf) - 1] = 0;
114 line = g_strdup_printf("%s %i %s\n", tbuf, getpid(), args);
116 if (logfile) {
117 write(fd, line, strlen(line));
118 fsync(fd);
119 close(fd);
122 if (isatty(STDERR_FILENO))
123 fprintf(stderr, "%s", line);
125 g_free(line);
126 g_free(args);
129 static void catchsig(gint sig)
131 gint status;
133 if (sig != SIGALRM)
134 log_write("caught signal %i (%s)", sig, strsignal(sig));
136 switch (sig) {
137 case SIGUSR1:
138 reload_rcfile();
139 break;
140 case SIGABRT:
141 cache_clear(NULL, 2);
142 xpanic();
143 exit(EXIT_FAILURE);
144 case SIGALRM:
145 cache_adjust_timer();
146 alarm(1);
147 break;
148 case SIGCHLD:
149 waitpid(-1, &status, 0);
150 break;
151 case SIGHUP:
152 log_write("clearing file cache");
153 cache_clear(NULL, 2);
154 break;
155 default:
156 quit = 1;
157 shutdown(sfd, SHUT_RDWR);
158 close(sfd);
159 break;
163 static void usage(gchar *pn)
165 g_printf(
166 "Usage: %s [-hvDb] [-f <rcfile>] [-I <filename>] [file1] [...]\n"
167 " -b run as a background process\n"
168 " -f load the specified rcfile (~/.pwmd/config)\n"
169 " -I import an XML file and write the encrypted data to stdout\n"
170 " -D disable use of the LIST and DUMP commands\n"
171 " -v version\n"
172 " -h this help text\n",
173 pn);
174 exit(EXIT_SUCCESS);
177 static int gcry_SecureCheck(const void *ptr)
179 return 1;
182 static void setup_gcrypt()
184 gcry_check_version(NULL);
186 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
187 xfree);
189 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
190 NULL) != 0)
191 errx(EXIT_FAILURE, "AES cipher not supported");
193 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
194 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
198 * Called every time a connection is made.
200 static void doit(int fd)
202 assuan_context_t ctx;
203 int rc;
204 struct client_s *cl = g_malloc0(sizeof(struct client_s));
206 signal(SIGCHLD, SIG_DFL);
207 signal(SIGHUP, SIG_DFL);
208 signal(SIGINT, SIG_DFL);
209 signal(SIGALRM, SIG_DFL);
210 gpg_err_init();
211 assuan_set_assuan_err_source(GPG_ERR_SOURCE_DEFAULT);
212 assuan_set_malloc_hooks(xmalloc, xrealloc, xfree);
213 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
214 xmlInitMemory();
216 #ifdef HAVE_MLOCKALL
217 if (use_mlock && mlockall(MCL_FUTURE) == -1) {
218 log_write("mlockall(): %s", strerror(errno));
219 exit(EXIT_FAILURE);
221 #endif
223 rc = assuan_init_socket_server_ext(&ctx, fd, 2);
225 if (rc) {
226 log_write("failed to initialize assuan server: %s", gpg_strerror(rc));
227 exit(EXIT_FAILURE);
230 assuan_set_pointer(ctx, cl);
231 rc = register_commands(ctx);
233 if (rc) {
234 log_write("failed to register assuan commands: %s", gpg_strerror(rc));
235 exit(EXIT_FAILURE);
238 cl->ctx = ctx;
241 * It would be nice if there were an assuan_register_pre_cmd_notify().
242 * That way we can see if the file has been modified before calling the
243 * command.
245 rc = assuan_accept(ctx);
247 if (rc) {
248 log_write("assuan_accept() failed: %s", gpg_strerror(rc));
249 goto done;
252 rc = assuan_process(ctx);
254 if (rc)
255 log_write("assuan_process() failed: %s", gpg_strerror(rc));
257 done:
258 assuan_deinit_server(ctx);
259 log_write("exiting");
260 _exit(EXIT_SUCCESS);
264 * Make sure all settings are set to either the specified setting or a
265 * default.
267 static void set_rcfile_defaults(GKeyFile *kf)
269 gchar buf[PATH_MAX];
271 if (g_key_file_has_key(kf, "default", "socket_path", NULL) == FALSE) {
272 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
273 g_key_file_set_string(kf, "default", "socket_path", buf);
276 if (g_key_file_has_key(kf, "default", "data_directory", NULL) == FALSE) {
277 snprintf(buf, sizeof(buf), "~/.pwmd/data");
278 g_key_file_set_string(kf, "default", "data_directory", buf);
281 if (g_key_file_has_key(kf, "default", "log_path", NULL) == FALSE) {
282 snprintf(buf, sizeof(buf), "~/.pwmd/log");
283 g_key_file_set_string(kf, "default", "log_path", buf);
286 if (g_key_file_has_key(kf, "default", "enable_logging", NULL) == FALSE)
287 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
289 if (g_key_file_has_key(kf, "default", "cache_size", NULL) == FALSE)
290 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
292 if (g_key_file_has_key(kf, "default", "disable_mlockall", NULL) == FALSE)
293 g_key_file_set_boolean(kf, "default", "disable_mlockall", FALSE);
295 if (g_key_file_has_key(kf, "default", "cache_timeout", NULL) == FALSE)
296 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
298 if (g_key_file_has_key(kf, "default", "iterations", NULL) == FALSE)
299 g_key_file_set_integer(kf, "default", "iterations", 0);
301 if (g_key_file_has_key(kf, "default", "disable_list_and_dump", NULL) == FALSE)
302 g_key_file_set_boolean(kf, "default", "disable_list_and_dump", FALSE);
304 if (g_key_file_has_key(kf, "default", "iteration_progress", NULL) == FALSE)
305 g_key_file_set_integer(kf, "default", "iteration_progress", 0);
308 static GKeyFile *parse_rcfile(int cmdline)
310 GKeyFile *kf = g_key_file_new();
311 GError *error = NULL;
313 if (g_key_file_load_from_file(kf, rcfile, G_KEY_FILE_NONE, &error) == FALSE) {
314 warnx("%s: %s", rcfile, error->message);
316 if (cmdline)
317 exit(EXIT_FAILURE);
319 if (error->code == G_FILE_ERROR_NOENT) {
320 g_clear_error(&error);
321 set_rcfile_defaults(kf);
322 return kf;
325 g_clear_error(&error);
326 return NULL;
328 else
329 set_rcfile_defaults(kf);
331 return kf;
334 static gboolean try_xml_decrypt(gint fd, struct stat st, guchar *key)
336 guchar *iv;
337 void *inbuf;
338 gcry_cipher_hd_t gh;
339 gsize insize;
340 guchar tkey[gcrykeysize];
341 gint iter;
342 struct file_header_s {
343 guint iter;
344 guchar iv[gcryblocksize];
345 } file_header;
347 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
348 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
349 return FALSE;
352 lseek(fd, 0, SEEK_SET);
353 insize = st.st_size - sizeof(struct file_header_s);
354 iv = gcry_malloc(gcryblocksize);
355 read(fd, &file_header, sizeof(struct file_header_s));
356 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
357 inbuf = gcry_malloc(insize);
358 read(fd, inbuf, insize);
359 memcpy(tkey, key, sizeof(tkey));
360 tkey[0] ^= 1;
362 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
363 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
364 gcry_cipher_close(gh);
365 gcry_free(inbuf);
366 gcry_free(iv);
367 return FALSE;
370 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
371 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
372 gcry_cipher_close(gh);
373 gcry_free(inbuf);
374 gcry_free(iv);
375 return FALSE;
378 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
379 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
380 gcry_cipher_close(gh);
381 gcry_free(inbuf);
382 gcry_free(iv);
383 return FALSE;
386 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
387 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
388 gcry_cipher_close(gh);
389 gcry_free(inbuf);
390 gcry_free(iv);
391 return FALSE;
394 iter = file_header.iter;
396 while (iter-- > 0) {
397 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
398 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
399 gcry_cipher_close(gh);
400 gcry_free(inbuf);
401 gcry_free(iv);
402 return FALSE;
405 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
406 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
407 gcry_cipher_close(gh);
408 gcry_free(inbuf);
409 gcry_free(iv);
410 return FALSE;
414 gcry_cipher_close(gh);
415 gcry_free(iv);
417 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
418 gcry_free(inbuf);
419 return FALSE;
422 gcry_free(inbuf);
423 return TRUE;
426 static gchar *get_password(const gchar *prompt)
428 gchar buf[LINE_MAX], *p;
429 struct termios told, tnew;
430 gchar *key;
432 if (tcgetattr(STDIN_FILENO, &told) == -1)
433 err(EXIT_FAILURE, "tcgetattr()");
435 memcpy(&tnew, &told, sizeof(struct termios));
436 tnew.c_lflag &= ~(ECHO);
437 tnew.c_lflag |= ICANON|ECHONL;
439 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
440 tcsetattr(STDIN_FILENO, TCSANOW, &told);
441 err(EXIT_FAILURE, "tcsetattr()");
444 fprintf(stderr, "%s", prompt);
446 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
447 tcsetattr(STDIN_FILENO, TCSANOW, &told);
448 return NULL;
451 tcsetattr(STDIN_FILENO, TCSANOW, &told);
452 p[strlen(p) - 1] = 0;
454 if (!p || !*p)
455 return NULL;
457 key = gcry_malloc(strlen(p) + 1);
458 sprintf(key, "%s", p);
459 memset(&buf, 0, sizeof(buf));
460 return key;
463 static gboolean do_try_xml_decrypt(const gchar *filename, guchar *key)
465 int fd;
466 struct stat st;
468 if ((fd = open_file(filename, &st)) == -1) {
469 warn("%s", filename);
470 return FALSE;
473 if (st.st_size == 0) {
474 warnx("%s: skipping empty file", filename);
475 close(fd);
476 return FALSE;
479 if (try_xml_decrypt(fd, st, key) == FALSE) {
480 close(fd);
481 return FALSE;
484 close(fd);
485 return TRUE;
488 static gboolean get_input(const gchar *filename, guchar *key)
490 gint try = 0;
491 gchar *password;
492 gchar *prompt;
494 if ((prompt = xmalloc(strlen(filename) + strlen("Password for '") + 4)) == NULL) {
495 warn("malloc()");
496 return FALSE;
499 sprintf(prompt, "Password for '%s': ", filename);
501 again:
502 if ((password = get_password(prompt)) == NULL) {
503 warnx("%s: skipping file", filename);
504 xfree(prompt);
505 return FALSE;
508 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
509 gcry_free(password);
511 if (do_try_xml_decrypt(filename, key) == FALSE) {
512 if (try++ == 2) {
513 warnx("%s: invalid password, skipping", filename);
514 xfree(prompt);
515 return FALSE;
517 else {
518 warnx("%s: invalid password", filename);
519 goto again;
523 xfree(prompt);
524 return TRUE;
527 static gboolean xml_import(const gchar *filename, gint iter)
529 xmlDocPtr doc;
530 gint fd;
531 struct stat st;
532 int len;
533 xmlChar *xmlbuf;
534 xmlChar *xml;
535 gchar *key = NULL;
536 gchar *key2 = NULL;
537 guchar shakey[gcrykeysize];
538 gcry_cipher_hd_t gh;
539 gpg_error_t error;
541 #ifdef HAVE_MLOCKALL
542 if (use_mlock && mlockall(MCL_FUTURE) == -1)
543 err(EXIT_FAILURE, "mlockall()");
544 #endif
546 if (stat(filename, &st) == -1) {
547 warn("%s", filename);
548 return FALSE;
551 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
552 xmlInitMemory();
554 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
555 send_error(NULL, gcryerrno);
556 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
557 return FALSE;
560 if ((key = get_password("New password: ")) == NULL) {
561 fprintf(stderr, "Invalid password.\n");
562 return FALSE;
565 if ((key2 = get_password("Verify password: ")) == NULL) {
566 fprintf(stderr, "Passwords do not match.\n");
567 gcry_free(key);
568 return FALSE;
571 if (strcmp(key, key2) != 0) {
572 fprintf(stderr, "Passwords do not match.\n");
573 gcry_free(key);
574 gcry_free(key2);
575 return FALSE;
578 gcry_free(key2);
580 if ((fd = open(filename, O_RDONLY)) == -1) {
581 gcry_free(key);
582 warn("%s", filename);
583 return FALSE;
586 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
587 gcry_free(key);
588 close(fd);
589 warnx("gcry_malloc() failed");
590 return FALSE;
593 read(fd, xmlbuf, st.st_size);
594 close(fd);
595 xmlbuf[st.st_size] = 0;
598 * Make sure the document validates.
600 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
601 warnx("xmlReadDoc() failed");
602 close(fd);
603 gcry_free(key);
604 gcry_free(xmlbuf);
605 return FALSE;
608 gcry_free(xmlbuf);
609 xmlDocDumpMemory(doc, &xml, &len);
610 xmlFreeDoc(doc);
611 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
612 gcry_free(key);
613 error = do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter);
615 if (error) {
616 memset(shakey, 0, sizeof(shakey));
617 xmlFree(xml);
618 warnx("%s", gpg_strerror(error));
619 return FALSE;
622 memset(shakey, 0, sizeof(shakey));
623 xmlFree(xml);
624 return TRUE;
627 gchar *get_key_file_string(const gchar *section, const gchar *what)
629 gchar *val = NULL;
630 GError *gerror = NULL;
632 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
633 val = g_key_file_get_string(keyfileh, section, what, &gerror);
635 if (gerror) {
636 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
637 g_clear_error(&gerror);
640 else {
641 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
642 val = g_key_file_get_string(keyfileh, "default", what, &gerror);
644 if (gerror) {
645 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
646 g_clear_error(&gerror);
651 return val;
654 gint get_key_file_integer(const gchar *section, const gchar *what)
656 gint val = -1;
657 GError *gerror = NULL;
659 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
660 val = g_key_file_get_integer(keyfileh, section, what, &gerror);
662 if (gerror) {
663 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
664 g_clear_error(&gerror);
667 else {
668 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
669 val = g_key_file_get_integer(keyfileh, "default", what, &gerror);
671 if (gerror) {
672 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
673 g_clear_error(&gerror);
678 return val;
681 gboolean get_key_file_boolean(const gchar *section, const gchar *what)
683 gboolean val = FALSE;
684 GError *gerror = NULL;
686 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
687 val = g_key_file_get_boolean(keyfileh, section, what, &gerror);
689 if (gerror) {
690 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
691 g_clear_error(&gerror);
694 else {
695 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
696 val = g_key_file_get_boolean(keyfileh, "default", what, &gerror);
698 if (gerror) {
699 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
700 g_clear_error(&gerror);
705 return val;
708 gchar *expand_homedir(gchar *str)
710 gchar *p = str;
711 gchar buf[PATH_MAX];
713 if (*p == '~') {
714 p++;
715 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
716 return g_strdup(buf);
719 return g_strdup(str);
722 static gchar *_getline(const gchar *file)
724 FILE *fp;
725 gchar buf[LINE_MAX], *p;
726 gchar *str = NULL;
728 if ((fp = fopen(file, "r")) == NULL) {
729 warn("%s", file);
730 return NULL;
733 if ((p = fgets(buf, sizeof(buf), fp)) == NULL) {
734 warnx("%s: empty file?", file);
735 return NULL;
738 fclose(fp);
739 buf[strlen(buf) - 1] = 0;
740 str = gcry_malloc(strlen(p) + 1);
741 memcpy(str, p, strlen(p));
742 str[strlen(p)] = 0;
743 memset(&buf, 0, sizeof(buf));
744 return str;
747 static gboolean parse_keyfile_key()
749 gsize n;
750 gchar **groups;
751 gchar **p;
752 gchar *str;
754 groups = g_key_file_get_groups(keyfileh, &n);
756 for (p = groups; *p; p++) {
757 GError *error = NULL;
759 if (g_key_file_has_key(keyfileh, *p, "key", &error) == TRUE) {
760 str = g_key_file_get_string(keyfileh, *p, "key", &error);
762 if (!str) {
763 if (error) {
764 warnx("%s", error->message);
765 g_clear_error(&error);
768 continue;
771 do_cache_push(*p, str);
772 g_free(str);
773 continue;
776 if (error) {
777 warnx("%s", error->message);
778 g_clear_error(&error);
779 continue;
782 if (g_key_file_has_key(keyfileh, *p, "key_file", &error) == TRUE) {
783 gchar *t;
784 gchar *file = g_key_file_get_string(keyfileh, *p, "key_file", &error);
786 if (!file) {
787 if (error) {
788 warnx("%s", error->message);
789 g_clear_error(&error);
792 continue;
795 t = expand_homedir(file);
796 g_free(file);
797 file = t;
799 if ((str = _getline(file)) == NULL) {
800 g_free(file);
801 continue;
804 g_free(file);
805 do_cache_push(*p, str);
806 gcry_free(str);
807 continue;
810 if (error) {
811 warnx("%s", error->message);
812 g_clear_error(&error);
816 g_strfreev(groups);
817 return TRUE;
820 static gboolean do_cache_push(const gchar *filename, const gchar *password)
822 guchar *md5file;
823 guchar *key;
824 gint timeout;
825 const gchar *p = filename;
827 while (isspace(*p))
828 p++;
830 if (!*p)
831 return FALSE;
833 if (valid_filename(p) == FALSE) {
834 warnx("%s: invalid characters in filename", p);
835 return FALSE;
838 md5file = gcry_malloc(16);
839 key = gcry_malloc(gcrykeysize);
840 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
842 if (cache_iscached(md5file) == TRUE) {
843 warnx("%s: file already cached, skipping", p);
844 gcry_free(md5file);
845 gcry_free(key);
846 return FALSE;
849 if (access(p, R_OK|W_OK) != 0) {
850 gcry_free(md5file);
851 gcry_free(key);
853 if (errno != ENOENT) {
854 warn("%s", p);
855 return FALSE;
858 warn("%s", p);
859 return TRUE;
862 if (!password) {
863 if (get_input(p, key) == FALSE) {
864 gcry_free(key);
865 gcry_free(md5file);
866 return FALSE;
869 else {
870 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
872 if (do_try_xml_decrypt(filename, key) == FALSE) {
873 warnx("%s: invalid password, skipping", filename);
874 gcry_free(key);
875 gcry_free(md5file);
876 return FALSE;
880 if (cache_add_file(md5file, key) == FALSE) {
881 warnx("%s: %s", p, pwmd_strerror(EPWMD_MAX_SLOTS));
882 gcry_free(key);
883 gcry_free(md5file);
884 return FALSE;
887 timeout = get_key_file_integer(p, "cache_timeout");
888 cache_set_timeout(md5file, timeout);
889 warnx("%s: file added to the cache", filename);
890 gcry_free(key);
891 gcry_free(md5file);
892 return TRUE;
895 int main(int argc, char *argv[])
897 gint opt;
898 struct sockaddr_un addr;
899 struct passwd *pw = getpwuid(getuid());
900 gchar buf[PATH_MAX];
901 #ifndef MMAP_ANONYMOUS_SHARED
902 gchar shm_path[PATH_MAX];
903 #endif
904 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
905 gchar *socketarg = NULL;
906 gchar *datadir = NULL;
907 gint fd;
908 gboolean n;
909 gchar *p;
910 gchar **cache_push = NULL;
911 gint iter = 0;
912 gchar *import = NULL;
913 gint default_timeout;
914 gint rcfile_spec = 0;
915 gint estatus = EXIT_FAILURE;
916 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
917 gint do_unlink = 1;
918 gboolean secure = FALSE;
919 guint ptotal = 0;
920 gint background = 0;
921 #ifndef DEBUG
922 #ifdef HAVE_SETRLIMIT
923 struct rlimit rl;
925 rl.rlim_cur = rl.rlim_max = 0;
927 if (setrlimit(RLIMIT_CORE, &rl) != 0)
928 err(EXIT_FAILURE, "setrlimit()");
929 #endif
930 #endif
932 g_mem_set_vtable(&mtable);
933 snprintf(buf, sizeof(buf), "%s/.pwmd", pw->pw_dir);
935 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
936 err(EXIT_FAILURE, "%s", buf);
938 snprintf(buf, sizeof(buf), "%s/.pwmd/data", pw->pw_dir);
940 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
941 err(EXIT_FAILURE, "%s", buf);
943 rcfile = g_strdup_printf("%s/.pwmd/config", pw->pw_dir);
945 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
946 err(EXIT_FAILURE, "sysconf()");
948 cache_size = page_size;
950 #ifdef HAVE_MLOCKALL
952 * Default to using mlockall().
954 use_mlock = 1;
955 #endif
957 while ((opt = getopt(argc, argv, "bI:hvf:D")) != EOF) {
958 switch (opt) {
959 case 'b':
960 background = 1;
961 break;
962 case 'D':
963 secure = TRUE;
964 break;
965 case 'I':
966 import = optarg;
967 break;
968 case 'f':
969 g_free(rcfile);
970 rcfile = g_strdup(optarg);
971 rcfile_spec = 1;
972 break;
973 case 'v':
974 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
975 exit(EXIT_SUCCESS);
976 case 'h':
977 default:
978 usage(argv[0]);
982 if ((keyfileh = parse_rcfile(rcfile_spec)) == NULL)
983 exit(EXIT_FAILURE);
985 g_key_file_set_list_separator(keyfileh, ',');
987 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
988 errx(EXIT_FAILURE, "%s: socket_path not defined", rcfile);
990 if (*p == '~') {
991 p++;
992 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
993 g_free(p);
994 socketarg = g_strdup(buf);
996 else
997 socketarg = p;
999 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1000 errx(EXIT_FAILURE, "%s: data_directory not defined", rcfile);
1002 datadir = expand_homedir(p);
1003 g_free(p);
1005 if (secure == FALSE && g_key_file_has_key(keyfileh, "default", "disable_list_and_dump", NULL) == TRUE) {
1006 n = g_key_file_get_boolean(keyfileh, "default", "disable_list_and_dump", NULL);
1007 disable_list_and_dump = n;
1009 else
1010 disable_list_and_dump = secure;
1012 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1013 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1014 else
1015 default_timeout = -1;
1017 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1018 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1020 if (cache_size < page_size || cache_size % page_size)
1021 errx(EXIT_FAILURE, "cache size must be in multiples of %li.", page_size);
1024 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1025 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1027 #ifdef HAVE_MLOCKALL
1028 if (g_key_file_has_key(keyfileh, "default", "disable_mlockall", NULL) == TRUE)
1029 use_mlock = g_key_file_get_integer(keyfileh, "default", "disable_mlockall", NULL);
1030 #endif
1032 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1033 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1034 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1036 if (n == TRUE) {
1037 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1039 if (*p == '~') {
1040 p++;
1041 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1042 g_free(p);
1043 logfile = g_strdup(buf);
1045 else
1046 logfile = p;
1051 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1052 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1054 if (argc != optind) {
1055 if (cache_push)
1056 ptotal = g_strv_length(cache_push);
1058 for (; optind < argc; optind++)
1059 append_to_array(&cache_push, &ptotal, argv[optind]);
1062 if (strchr(socketarg, '/') == NULL) {
1063 socketdir = g_get_current_dir();
1064 socketname = g_strdup(socketarg);
1065 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1067 else {
1068 socketname = g_strdup(strrchr(socketarg, '/'));
1069 socketname++;
1070 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1071 socketdir = g_strdup(socketarg);
1072 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1075 #ifdef MMAP_ANONYMOUS_SHARED
1076 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1077 #ifdef MMAP_ANONYMOUS
1078 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == NULL) {
1079 #else
1080 MAP_SHARED|MAP_ANON, -1, 0)) == NULL) {
1081 #endif
1082 err(EXIT_FAILURE, "mmap()");
1084 #else
1085 snprintf(shm_path, sizeof(shm_path), "/pwmd.%i", pw->pw_uid);
1087 if ((fd = shm_open(shm_path, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
1088 err(EXIT_FAILURE, "shm_open(): %s", shm_path);
1091 * Should be enough for the file cache.
1093 if (ftruncate(fd, cache_size) == -1) {
1094 warn("ftruncate()");
1095 shm_unlink(shm_path);
1096 exit(EXIT_FAILURE);
1099 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
1100 fd, 0)) == NULL) {
1101 warn("mmap()");
1102 shm_unlink(shm_path);
1103 exit(EXIT_FAILURE);
1106 close(fd);
1107 #endif
1109 if (mlock(shm_data, cache_size) == -1)
1110 warn("mlock()");
1112 memset(shm_data, 0, cache_size);
1113 setup_gcrypt();
1115 if (import) {
1116 opt = xml_import(import, iter);
1117 g_free(socketpath);
1119 if (munmap(shm_data, cache_size) == -1)
1120 log_write("munmap(): %s", strerror(errno));
1122 #ifndef MMAP_ANONYMOUS_SHARED
1123 if (shm_unlink(shm_path) == -1)
1124 log_write("shm_unlink(): %s: %s", shm_path, strerror(errno));
1125 #endif
1127 exit((opt) == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1130 getcwd(buf, sizeof(buf));
1133 * bind() doesn't like the full pathname of the socket or any non alphanum
1134 * characters so change to the directory where the socket is wanted then
1135 * create it then change to datadir.
1137 if (chdir(socketdir)) {
1138 warn("%s", socketdir);
1139 goto do_exit;
1142 g_free(socketdir);
1144 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) {
1145 warn("socket()");
1146 goto do_exit;
1149 addr.sun_family = AF_UNIX;
1150 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1151 g_free(--socketname);
1153 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1) {
1154 warn("bind()");
1156 if (errno == EADDRINUSE)
1157 warnx("Either there is another pwmd running or '%s' is a \n"
1158 "stale socket. Please remove it manually.", socketpath);
1160 do_unlink = 0;
1161 goto do_exit;
1164 if (chdir(datadir)) {
1165 warn("%s", datadir);
1166 close(sfd);
1167 unlink(socketpath);
1168 goto do_exit;
1171 g_free(datadir);
1173 if (parse_keyfile_key() == FALSE)
1174 goto do_exit;
1177 * Set the cache entry for a file. Prompts for the password.
1179 if (cache_push) {
1180 for (opt = 0; cache_push[opt]; opt++)
1181 do_cache_push(cache_push[opt], NULL);
1183 g_strfreev(cache_push);
1184 warnx("Done! %s", background ? "Daemonizing..." : "Waiting for connections...");
1187 chdir(buf);
1189 if (listen(sfd, 0) == -1) {
1190 warn("listen()");
1191 goto do_exit;
1194 signal(SIGCHLD, catchsig);
1195 signal(SIGTERM, catchsig);
1196 signal(SIGINT, catchsig);
1197 signal(SIGHUP, catchsig);
1198 signal(SIGALRM, catchsig);
1199 signal(SIGABRT, catchsig);
1200 signal(SIGUSR1, catchsig);
1201 alarm(1);
1203 if (background) {
1204 switch (fork()) {
1205 case -1:
1206 warn("fork()");
1207 goto do_exit;
1208 case 0:
1209 close(0);
1210 close(1);
1211 close(2);
1212 break;
1213 default:
1214 exit(EXIT_SUCCESS);
1218 log_write("%s started: %li slots available", PACKAGE_STRING, cache_size / sizeof(file_cache_t));
1220 while (!quit) {
1221 socklen_t slen = sizeof(struct sockaddr_un);
1222 struct sockaddr_un raddr;
1223 pid_t pid;
1225 if ((fd = accept(sfd, (struct sockaddr *)&raddr, &slen)) == -1) {
1226 if (quit)
1227 break;
1229 if (errno == EAGAIN)
1230 continue;
1232 log_write("accept(): %s", strerror(errno));
1233 continue;
1236 switch ((pid = fork())) {
1237 case -1:
1238 log_write("fork(): %s", strerror(errno));
1239 break;
1240 case 0:
1241 doit(fd);
1242 break;
1243 default:
1244 break;
1247 log_write("new connection: pid=%i", pid);
1248 close(fd);
1251 estatus = EXIT_SUCCESS;
1253 do_exit:
1254 chdir(buf);
1256 if (socketpath && do_unlink) {
1257 unlink(socketpath);
1258 g_free(socketpath);
1261 g_key_file_free(keyfileh);
1262 g_free(rcfile);
1263 memset(shm_data, 0, cache_size);
1265 if (munmap(shm_data, cache_size) == -1)
1266 log_write("munmap(): %s", strerror(errno));
1268 #ifndef MMAP_ANONYMOUS_SHARED
1269 if (shm_unlink(shm_path) == -1)
1270 log_write("shm_unlink(): %s: %s", shm_path, strerror(errno));
1271 #endif
1273 if (estatus == EXIT_SUCCESS)
1274 log_write("pwmd exiting normally");
1276 exit(estatus);