Check for alarm in configure.ac.
[pwmd.git] / src / pwmd.c
blob4a2da86bf7f1a7d31addbc6a0e89ceaee7cd8bcb
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 "xml.h"
51 #include "common.h"
52 #include "commands.h"
53 #include "pwmd_error.h"
54 #include "pwmd.h"
56 void send_to_client(struct client_s *client, const gchar *fmt, ...)
58 va_list ap;
59 gint n = 0;
60 gchar **p;
62 if (!client)
63 return;
65 va_start(ap, fmt);
67 if (client->outbuf)
68 for (p = client->outbuf, n = 0; *p; p++, n++);
70 if ((client->outbuf = g_realloc(client->outbuf, (n + 2) * sizeof(gchar *))) == NULL) {
71 warn("g_realloc()");
72 return;
75 client->outbuf[n++] = g_strdup_vprintf(fmt, ap);
76 client->outbuf[n] = NULL;
77 va_end(ap);
80 void send_error(struct client_s *client, int pwmd_errno)
82 send_to_client(client, "ERR %03i %s\n", pwmd_errno, pwmd_strerror(pwmd_errno));
85 void log_write(const gchar *fmt, ...)
87 gchar *args, *line;
88 va_list ap;
89 struct tm *tm;
90 time_t now;
91 gchar tbuf[21];
92 gint fd;
93 gint tofile = !isatty(STDOUT_FILENO);
95 if (!logfile || !fmt)
96 return;
98 if (tofile) {
99 if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) {
100 warn("logfile");
101 return;
105 va_start(ap, fmt);
106 g_vasprintf(&args, fmt, ap);
107 va_end(ap);
109 if (tofile) {
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);
115 write(fd, line, strlen(line));
116 fsync(fd);
117 close(fd);
118 g_free(line);
120 else
121 fprintf(stderr, "%s\n", args);
123 g_free(args);
126 static void cache_increment_elapsed()
128 file_cache_t f;
129 void *p;
130 glong len;
132 for (p = shm_data, len = 0; len <= cache_size;) {
133 memcpy(&f, p, sizeof(file_cache_t));
135 if (f.timeout >= 0) {
136 f.elapsed++;
138 if (f.elapsed >= f.when)
139 memset(&f.key, 0, sizeof(f.key));
141 memcpy(p, &f, sizeof(file_cache_t));
144 p += sizeof(file_cache_t);
145 len += sizeof(file_cache_t);
147 if (len + sizeof(file_cache_t) > cache_size)
148 break;
152 static void catchsig(gint sig)
154 gint status;
156 if (sig != SIGALRM)
157 log_write("caught signal %i (%s)", sig, strsignal(sig));
159 switch (sig) {
160 case SIGALRM:
161 cache_increment_elapsed();
162 alarm(1);
163 break;
164 case SIGCHLD:
165 waitpid(-1, &status, 0);
166 break;
167 case SIGHUP:
168 log_write("clearing file cache");
169 memset(shm_data, 0, cache_size);
170 break;
171 default:
172 quit = 1;
173 shutdown(sfd, SHUT_RDWR);
174 close(sfd);
175 break;
179 static void usage(gchar *pn)
181 g_printf(
182 "Usage: %s [-hv] [-f <rcfile>] [-I <filename>]\n"
183 " -f load the specified rcfile (~/.pwmdrc)\n"
184 " -I import an XML file and write the encrypted data to stdout\n"
185 " -v version\n"
186 " -h this help text\n",
187 pn);
188 exit(EXIT_SUCCESS);
191 gchar **split_input_line(gchar *str, gchar *delim, gint n)
193 if (!str || !*str)
194 return NULL;
196 return g_strsplit(str, delim, n);
199 static int gcry_SecureCheck(const void *ptr)
201 return 1;
204 static void setup_gcrypt()
206 gcry_check_version(NULL);
208 gcry_set_allocation_handler(xmalloc, xmalloc, gcry_SecureCheck, xrealloc,
209 xfree);
211 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, NULL,
212 NULL) != 0)
213 errx(EXIT_FAILURE, "AES cipher not supported");
215 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_KEYLEN, NULL, &gcrykeysize);
216 gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_GET_BLKLEN, NULL, &gcryblocksize);
219 static gint input_parser(gchar *str)
221 gchar *p, *t;
222 gchar **req = NULL;
224 if (str)
225 str = g_strchug(str);
227 if (!*str)
228 return P_OK;
230 while ((p = strsep(&str, "\n")) != NULL) {
231 if (g_ascii_strcasecmp(p, "QUIT") == 0)
232 return P_QUIT;
233 else if (g_ascii_strcasecmp(p, "HELP") == 0)
234 help_command(cl, NULL);
235 else if (g_ascii_strncasecmp(p, "HELP ", 5) == 0) {
236 t = p + 5;
237 t = g_strchug(t);
238 help_command(cl, t);
240 else if (g_ascii_strcasecmp(p, "LIST") == 0 ||
241 g_ascii_strncasecmp(p, "LIST ", 5) == 0) {
242 if (cl->state != STATE_OPEN)
243 send_error(cl, EPWMD_NO_FILE);
244 else
245 list_command(cl, p);
247 else if (g_ascii_strncasecmp(p, "STORE ", 6) == 0) {
248 t = p + 6;
249 t = g_strchug(t);
251 if (cl->state != STATE_OPEN)
252 send_error(cl, EPWMD_NO_FILE);
253 else {
254 if ((req = split_input_line(t, "\t", 0)) != NULL) {
255 if (store_command(cl, req) == TRUE)
256 send_to_client(cl, "OK \n");
258 else
259 send_error(cl, EPWMD_COMMAND_SYNTAX);
262 else if (g_ascii_strncasecmp(p, "DELETE ", 7) == 0) {
263 t = p + 7;
265 if (cl->state != STATE_OPEN)
266 send_error(cl, EPWMD_NO_FILE);
267 else {
268 if ((req = split_input_line(t, "\t", 0)) != NULL) {
269 if (delete_command(cl, req) == TRUE)
270 send_to_client(cl, "OK \n");
272 else
273 send_error(cl, EPWMD_COMMAND_SYNTAX);
276 else if (g_ascii_strncasecmp(p, "GET ", 4) == 0) {
277 t = p + 4;
278 t = g_strchug(t);
280 if (cl->state != STATE_OPEN)
281 send_error(cl, EPWMD_NO_FILE);
282 else {
283 if ((req = split_input_line(t, "\t", 0)) != NULL)
284 get_command(cl, &cl->reader, req, 0);
285 else
286 send_error(cl, EPWMD_COMMAND_SYNTAX);
289 else if (g_ascii_strncasecmp(p, "ATTR ", 5) == 0) {
290 t = p + 5;
291 t = g_strchug(t);
293 if (cl->state != STATE_OPEN)
294 send_error(cl, EPWMD_NO_FILE);
295 else {
296 if ((req = split_input_line(t, " ", 4)) != NULL) {
297 if (attr_command(cl, req) == TRUE)
298 send_to_client(cl, "OK \n");
300 else
301 send_error(cl, EPWMD_COMMAND_SYNTAX);
304 else if (g_ascii_strncasecmp(p, "OPEN ", 5) == 0) {
305 t = p + 5;
306 t = g_strchug(t);
308 if (cl->state == STATE_OPEN)
309 send_error(cl, EPWMD_FILE_OPENED);
310 else {
311 if ((req = split_input_line(t, " ", 2)) != NULL) {
312 if (open_command(cl, req) == TRUE) {
313 send_to_client(cl, "OK \n");
314 cl->state = STATE_OPEN;
318 * The document has been parsed and is stored in cl->doc.
320 if (cl->xml) {
321 gcry_free(cl->xml);
322 cl->xml = NULL;
325 else
326 send_error(cl, EPWMD_COMMAND_SYNTAX);
329 else if (g_ascii_strncasecmp(p, "SAVE", 4) == 0) {
330 t = p + 4;
332 if (*t && *t != ' ')
333 send_error(cl, EPWMD_COMMAND_SYNTAX);
334 else {
335 t = g_strchug(t);
337 if (cl->state != STATE_OPEN)
338 send_error(cl, EPWMD_NO_FILE);
339 else {
340 req = split_input_line(t, " ", 1);
342 if (save_command(cl, cl->filename, (req) ? req[0] : NULL) == TRUE)
343 send_to_client(cl, "OK \n");
347 else if (g_ascii_strncasecmp(p, "CACHE ", 6) == 0) {
348 t = p + 6;
349 t = g_strchug(t);
350 req = split_input_line(t, " ", 0);
352 if (cache_command(cl, req) == TRUE)
353 send_to_client(cl, "OK \n");
355 else if (g_ascii_strncasecmp(p, "DUMP", 4) == 0) {
356 if (cl->state != STATE_OPEN)
357 send_error(cl, EPWMD_NO_FILE);
358 else {
359 if (dump_command(cl) == TRUE)
360 send_to_client(cl, "OK \n");
363 else
364 send_error(cl, EPWMD_COMMAND_SYNTAX);
367 if (req) {
368 g_strfreev(req);
369 req = NULL;
372 return P_OK;
375 static gboolean source_prepare(GSource *src, gint *to)
377 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
378 return TRUE;
380 return FALSE;
383 static gboolean source_check(GSource *src)
385 if (cl->gfd.revents & (G_IO_IN|G_IO_PRI))
386 return TRUE;
388 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT)))
389 return TRUE;
391 return FALSE;
394 static gboolean source_dispatch(GSource *src, GSourceFunc cb, gpointer data)
396 return (*cb)(data);
399 static gboolean source_cb(gpointer data)
401 GIOStatus ret;
402 gsize len;
403 gchar *line = NULL;
404 GError *gerror = NULL;
405 gchar **p;
407 if (cl->gfd.revents & (G_IO_HUP|G_IO_NVAL|G_IO_ERR))
408 goto quit;
410 if (cl->outbuf && (cl->gfd.revents & (G_IO_OUT))) {
411 for (p = cl->outbuf; *p; p++) {
412 ret = g_io_channel_write_chars(cl->ioc, *p, -1, &len, &gerror);
414 if (ret == G_IO_STATUS_NORMAL)
415 g_io_channel_flush(cl->ioc, &gerror);
416 else
417 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
419 g_clear_error(&gerror);
421 if (quit)
422 goto quit;
425 g_strfreev(cl->outbuf);
426 cl->outbuf = NULL;
429 if (!cl->gfd.revents & (G_IO_IN))
430 return TRUE;
432 ret = g_io_channel_read_line(cl->ioc, &line, &len, NULL, &gerror);
434 if (ret != G_IO_STATUS_NORMAL) {
435 if (ret == G_IO_STATUS_EOF)
436 goto quit;
438 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gerror->message);
439 g_clear_error(&gerror);
440 return TRUE;
443 line[g_utf8_strlen(line, -1) - 1] = 0;
445 switch (input_parser(line)) {
446 case P_QUIT:
447 quit:
448 if (line) {
449 memset(line, 0, len);
450 g_free(line);
453 g_clear_error(&gerror);
455 if (cl->xml)
456 gcry_free(cl->xml);
458 if (cl->gh)
459 gcry_cipher_close(cl->gh);
461 if (cl->doc)
462 xmlFreeDoc(cl->doc);
464 if (cl->reader)
465 xmlFreeTextReader(cl->reader);
467 if (cl->outbuf)
468 g_free(cl->outbuf);
470 if (cl->filename)
471 g_free(cl->filename);
473 #if 0
474 if (memlist)
475 g_slist_free(memlist);
476 #endif
478 g_main_loop_unref(gloop);
479 g_main_loop_quit(gloop);
480 return FALSE;
481 default:
482 break;
485 memset(line, 0, len);
486 g_free(line);
487 return TRUE;
490 static struct memlist_s *memlist_remove(struct memlist_s *list, struct memlist_s *r)
492 struct memlist_s *m, *last = NULL, *p;
494 for (m = list; m; m = m->next) {
495 if (m->ptr == r->ptr) {
496 memset(m->ptr, 0, m->size);
497 free(m->ptr);
498 p = m->next;
499 free(m);
501 if (last) {
502 if (p)
503 last->next = p;
504 else
505 last->next = NULL;
507 else if (p)
508 list = p;
509 else
510 list = NULL;
512 break;
515 last = m;
518 return list;
521 static struct memlist_s *memlist_append(struct memlist_s *list, struct memlist_s *new)
523 struct memlist_s *m;
525 if (!list) {
526 list = new;
527 list->next = NULL;
528 return list;
531 for (m = list; m; m = m->next) {
532 if (!m->next) {
533 m->next = new;
534 m = m->next;
535 m->next = NULL;
536 break;
540 return list;
543 void xfree(void *ptr)
545 struct memlist_s *m;
547 if (!ptr)
548 return;
550 for (m = memlist; m; m = m->next) {
551 if (m->ptr == ptr) {
552 #ifdef DEBUG
553 fprintf(stderr, "xfree(): %p %i\n", ptr, m->size);
554 #endif
555 memlist = memlist_remove(memlist, m);
556 return;
560 warnx("xfree(): %p not found", ptr);
561 assert(0);
564 void *xmalloc(size_t size)
566 void *p;
567 struct memlist_s *new;
569 if (size <= 0)
570 return NULL;
572 if ((new = malloc(sizeof(struct memlist_s))) == NULL)
573 return NULL;
575 if ((p = malloc(size)) == NULL) {
576 free(new);
577 return NULL;
580 new->ptr = p;
581 new->size = size;
582 memlist = memlist_append(memlist, new);
583 #ifdef DEBUG
584 fprintf(stderr, "xmalloc(): %p %i\n", p, size);
585 #endif
586 return new->ptr;
589 void *xcalloc(size_t nmemb, size_t size)
591 void *p;
592 struct memlist_s *new;
594 if (size <= 0)
595 return NULL;
597 if ((new = malloc(sizeof(struct memlist_s))) == NULL)
598 return NULL;
600 if ((p = calloc(nmemb, size)) == NULL) {
601 free(new);
602 return NULL;
605 new->ptr = p;
606 new->size = size;
607 memlist = memlist_append(memlist, new);
608 #ifdef DEBUG
609 fprintf(stderr, "xcalloc(): %p %i\n", p, size);
610 #endif
611 return new->ptr;
614 void *xrealloc(void *ptr, size_t size)
616 void *new;
617 struct memlist_s *m;
619 if (!ptr)
620 return xmalloc(size);
622 if (size <= 0)
623 return ptr;
625 for (m = memlist; m; m = m->next) {
626 if (m->ptr == ptr) {
627 if ((new = malloc(size)) == NULL)
628 return NULL;
630 memcpy(new, m->ptr, (size < m->size) ? size : m->size);
631 memset(m->ptr, 0, m->size);
632 free(m->ptr);
633 m->ptr = new;
634 m->size = size;
635 #ifdef DEBUG
636 fprintf(stderr, "xrealloc(): %p %i\n", new, size);
637 #endif
638 return m->ptr;
642 warnx("xrealloc(): %p not found", ptr);
643 assert(0);
644 return NULL;
647 char *xstrdup(const char *str)
649 char *new;
650 size_t len;
651 const char *p;
652 char *np;
654 if (!str)
655 return NULL;
657 len = strlen(str) + 1;
659 if ((new = xmalloc(len * sizeof(char))) == NULL)
660 return NULL;
662 for (p = str, np = new; *p; p++)
663 *np++ = *p;
665 *np = 0;
666 #ifdef DEBUG
667 fprintf(stderr, "xstrdup(): %p\n", new);
668 #endif
669 return new;
673 * Called every time a connection is made.
675 static void doit(int fd)
677 static GSourceFuncs gsrcf = {
678 source_prepare, source_check, source_dispatch, NULL, 0, 0
680 GPollFD gfd = { fd, G_IO_IN|G_IO_OUT|G_IO_HUP|G_IO_ERR, 0 };
681 GMemVTable mtable = { xmalloc, xrealloc, xfree, xcalloc, NULL, NULL };
683 signal(SIGCHLD, SIG_DFL);
684 signal(SIGHUP, SIG_DFL);
685 signal(SIGINT, SIG_DFL);
686 signal(SIGALRM, SIG_DFL);
687 g_mem_set_vtable(&mtable);
689 #ifdef HAVE_MLOCKALL
690 if (use_mlock && mlockall(MCL_FUTURE) == -1) {
691 log_write("mlockall(): %s", strerror(errno));
692 exit(EXIT_FAILURE);
694 #endif
696 gloop = g_main_loop_new(NULL, TRUE);
697 cl = g_malloc0(sizeof(struct client_s));
698 cl->src = g_source_new(&gsrcf, sizeof(GSource));
699 cl->gfd = gfd;
700 cl->state = STATE_CONNECTED;
701 cl->ioc = g_io_channel_unix_new(fd);
702 g_source_add_poll(cl->src, &cl->gfd);
703 g_source_set_callback(cl->src, source_cb, NULL, NULL);
704 g_source_attach(cl->src, NULL);
706 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
707 xmlInitMemory();
709 if ((gcryerrno = gcry_cipher_open(&cl->gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
710 send_to_client(cl, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
711 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
712 quit = 1;
714 else
715 // FIXME 100% CPU if removed (poll()).
716 send_to_client(cl, "OK \n");
718 g_main_loop_run(gloop);
719 g_io_channel_unref(cl->ioc);
720 g_free(cl);
721 shutdown(fd, SHUT_RDWR);
722 log_write("exiting");
723 _exit(EXIT_SUCCESS);
726 static void set_rcfile_defaults(GKeyFile *kf)
728 gchar buf[PATH_MAX];
730 snprintf(buf, sizeof(buf), "~/.pwmd/socket");
731 g_key_file_set_string(kf, "default", "socket_path", buf);
732 snprintf(buf, sizeof(buf), "~/.pwmd");
733 g_key_file_set_string(kf, "default", "data_directory", buf);
734 snprintf(buf, sizeof(buf), "~/.pwmd/.log");
735 g_key_file_set_string(kf, "default", "log_path", buf);
736 g_key_file_set_boolean(kf, "default", "enable_logging", FALSE);
737 g_key_file_set_integer(kf, "default", "cache_size", cache_size);
738 g_key_file_set_boolean(kf, "default", "disable_mlockall", FALSE);
739 g_key_file_set_integer(kf, "default", "cache_timeout", -1);
740 g_key_file_set_integer(kf, "default", "iterations", 0);
743 static GKeyFile *parse_rcfile(const gchar *filename)
745 GKeyFile *kf = g_key_file_new();
746 GError *error = NULL;
748 if (g_key_file_load_from_file(kf, filename, G_KEY_FILE_NONE, &error) == FALSE) {
749 if (error->code == G_FILE_ERROR_NOENT) {
750 g_clear_error(&error);
751 set_rcfile_defaults(kf);
752 return kf;
754 else {
755 warnx("%s: %s", filename, error->message);
756 g_clear_error(&error);
757 return NULL;
761 return kf;
764 static gboolean try_xml_decrypt(gint fd, struct stat st, guchar *key)
766 guchar *iv;
767 void *inbuf;
768 gcry_cipher_hd_t gh;
769 gsize insize;
770 guchar tkey[gcrykeysize];
771 gint iter;
772 struct file_header_s {
773 guint iter;
774 guchar iv[gcryblocksize];
775 } file_header;
777 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
778 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
779 return FALSE;
782 lseek(fd, 0, SEEK_SET);
783 insize = st.st_size - sizeof(struct file_header_s);
784 iv = gcry_malloc(gcryblocksize);
785 read(fd, &file_header, sizeof(struct file_header_s));
786 memcpy(iv, &file_header.iv, sizeof(file_header.iv));
787 inbuf = gcry_malloc(insize);
788 read(fd, inbuf, insize);
789 memcpy(tkey, key, sizeof(tkey));
790 tkey[0] ^= 1;
792 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
793 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
794 gcry_cipher_close(gh);
795 gcry_free(inbuf);
796 gcry_free(iv);
797 return FALSE;
800 if ((gcryerrno = gcry_cipher_setkey(gh, key, gcrykeysize))) {
801 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
802 gcry_cipher_close(gh);
803 gcry_free(inbuf);
804 gcry_free(iv);
805 return FALSE;
808 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
809 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
810 gcry_cipher_close(gh);
811 gcry_free(inbuf);
812 gcry_free(iv);
813 return FALSE;
816 if ((gcryerrno = gcry_cipher_setkey(gh, tkey, gcrykeysize))) {
817 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
818 gcry_cipher_close(gh);
819 gcry_free(inbuf);
820 gcry_free(iv);
821 return FALSE;
824 iter = file_header.iter;
826 while (iter-- > 0) {
827 if ((gcryerrno = gcry_cipher_setiv(gh, iv, gcryblocksize))) {
828 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
829 gcry_cipher_close(gh);
830 gcry_free(inbuf);
831 gcry_free(iv);
832 return FALSE;
835 if (decrypt_xml(gh, inbuf, insize, NULL, 0) == FALSE) {
836 warnx("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
837 gcry_cipher_close(gh);
838 gcry_free(inbuf);
839 gcry_free(iv);
840 return FALSE;
844 gcry_cipher_close(gh);
845 gcry_free(iv);
847 if (g_strncasecmp(inbuf, "<?xml version=\"1.0\"?>", 21) != 0) {
848 gcry_free(inbuf);
849 return FALSE;
852 gcry_free(inbuf);
853 return TRUE;
856 static gchar *get_password(const gchar *prompt)
858 gchar buf[LINE_MAX], *p;
859 struct termios told, tnew;
860 gchar *key;
862 if (tcgetattr(STDIN_FILENO, &told) == -1)
863 err(EXIT_FAILURE, "tcgetattr()");
865 memcpy(&tnew, &told, sizeof(struct termios));
866 tnew.c_lflag &= ~(ECHO);
867 tnew.c_lflag |= ICANON|ECHONL;
869 if (tcsetattr(STDIN_FILENO, TCSANOW, &tnew) == -1) {
870 tcsetattr(STDIN_FILENO, TCSANOW, &told);
871 err(EXIT_FAILURE, "tcsetattr()");
874 fprintf(stderr, "%s", prompt);
876 if ((p = fgets(buf, sizeof(buf), stdin)) == NULL) {
877 tcsetattr(STDIN_FILENO, TCSANOW, &told);
878 return NULL;
881 tcsetattr(STDIN_FILENO, TCSANOW, &told);
882 p[strlen(p) - 1] = 0;
884 if (!p || !*p)
885 return NULL;
887 key = gcry_malloc(strlen(p) + 1);
888 sprintf(key, "%s", p);
889 return key;
892 static gboolean get_input(const gchar *filename, guchar *key)
894 gint fd;
895 struct stat st;
896 int try = 0;
897 gchar *password;
898 gchar *prompt;
900 if ((fd = open_file(filename, &st)) == -1) {
901 warn("%s", filename);
902 return FALSE;
905 if (st.st_size == 0) {
906 fprintf(stderr, "Skipping empty file '%s'.\n", filename);
907 close(fd);
908 return FALSE;
911 if ((prompt = xmalloc(strlen(filename) + strlen("Password for '") + 4)) == NULL) {
912 close(fd);
913 warn("malloc()");
914 return FALSE;
917 sprintf(prompt, "Password for '%s': ", filename);
919 again:
920 if ((password = get_password(prompt)) == NULL) {
921 fprintf(stderr, "Skipping.\n");
922 close(fd);
923 xfree(prompt);
924 return FALSE;
927 gcry_md_hash_buffer(GCRY_MD_SHA256, key, password, strlen(password));
928 xfree(password);
930 if (try_xml_decrypt(fd, st, key) == FALSE) {
931 if (try++ == 2) {
932 fprintf(stderr, "Invalid password. Skipping file.\n");
933 close(fd);
934 xfree(prompt);
935 return FALSE;
937 else {
938 fprintf(stderr, "Invalid password.\n");
939 goto again;
943 close(fd);
944 xfree(prompt);
945 return TRUE;
948 gint cache_file_count()
950 void *p;
951 gint n = 0;
952 glong len;
953 file_cache_t f;
955 for (p = shm_data, len = 0; len <= cache_size;) {
956 memcpy(&f, p, sizeof(file_cache_t));
958 if (f.used == TRUE)
959 n++;
961 p += sizeof(file_cache_t);
962 len += sizeof(file_cache_t);
964 if (len + sizeof(file_cache_t) > cache_size)
965 break;
968 return n;
971 gboolean cache_add_file(const guchar *md5file, const guchar *shakey)
973 void *p;
974 file_cache_t f;
975 gint nfiles = cache_file_count();
976 glong len;
979 * Make sure there is enough secure memory.
981 if (!md5file || (nfiles + 1) * sizeof(file_cache_t) > cache_size)
982 return FALSE;
985 * Find the first available "slot".
987 for (p = shm_data, len = 0; len <= cache_size;) {
988 memcpy(&f, p, sizeof(file_cache_t));
990 if (f.used == FALSE) {
991 memcpy(&f.filename, md5file, sizeof(f.filename));
993 if (shakey)
994 memcpy(&f.key, shakey, sizeof(f.key));
996 f.used = TRUE;
997 f.elapsed = f.when = 0;
998 f.timeout = -1;
999 memcpy(p, &f, sizeof(file_cache_t));
1000 return TRUE;
1003 p += sizeof(file_cache_t);
1004 len += sizeof(file_cache_t);
1006 if (len + sizeof(file_cache_t) > cache_size)
1007 break;
1010 return FALSE;
1013 static gboolean xml_import(const gchar *filename, gint iter)
1015 xmlDocPtr doc;
1016 gint fd;
1017 struct stat st;
1018 int len;
1019 xmlChar *xmlbuf;
1020 xmlChar *xml;
1021 gchar *key = NULL;
1022 gchar *key2 = NULL;
1023 guchar shakey[gcrykeysize];
1024 gcry_cipher_hd_t gh;
1026 #ifdef HAVE_MLOCKALL
1027 if (use_mlock && mlockall(MCL_FUTURE) == -1)
1028 err(EXIT_FAILURE, "mlockall()");
1029 #endif
1031 if (stat(filename, &st) == -1) {
1032 warn("%s", filename);
1033 return FALSE;
1036 xmlMemSetup(xfree, xmalloc, xrealloc, xstrdup);
1037 xmlInitMemory();
1039 if ((gcryerrno = gcry_cipher_open(&gh, GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 0))) {
1040 send_to_client(NULL, "ERR %03i gcrypt: %s\n", EPWMD_ERROR, gcry_strerror(gcryerrno));
1041 log_write("%s(%i): %s", __FUNCTION__, __LINE__, gcry_strerror(gcryerrno));
1042 return FALSE;
1045 if ((key = get_password("New password: ")) == NULL) {
1046 fprintf(stderr, "Invalid password.\n");
1047 return FALSE;
1050 if ((key2 = get_password("Verify password: ")) == NULL) {
1051 fprintf(stderr, "Passwords do not match.\n");
1052 gcry_free(key);
1053 return FALSE;
1056 if (strcmp(key, key2) != 0) {
1057 fprintf(stderr, "Passwords do not match.\n");
1058 gcry_free(key);
1059 gcry_free(key2);
1060 return FALSE;
1063 gcry_free(key2);
1065 if ((fd = open(filename, O_RDONLY)) == -1) {
1066 gcry_free(key);
1067 warn("%s", filename);
1068 return FALSE;
1071 if ((xmlbuf = gcry_malloc(st.st_size+1)) == NULL) {
1072 gcry_free(key);
1073 close(fd);
1074 warnx("gcry_malloc() failed");
1075 return FALSE;
1078 read(fd, xmlbuf, st.st_size);
1079 close(fd);
1080 xmlbuf[st.st_size] = 0;
1083 * Make sure the document validates.
1085 if ((doc = xmlReadDoc(xmlbuf, NULL, "UTF-8", XML_PARSE_NOBLANKS)) == NULL) {
1086 warnx("xmlReadDoc() failed");
1087 close(fd);
1088 gcry_free(key);
1089 gcry_free(xmlbuf);
1090 return FALSE;
1093 gcry_free(xmlbuf);
1094 xmlDocDumpMemory(doc, &xml, &len);
1095 xmlFreeDoc(doc);
1096 gcry_md_hash_buffer(GCRY_MD_SHA256, shakey, key, strlen(key));
1097 gcry_free(key);
1099 if (do_xml_encrypt(NULL, gh, NULL, xml, len, shakey, iter) == FALSE) {
1100 memset(shakey, 0, sizeof(shakey));
1101 xmlFree(xml);
1102 return FALSE;
1105 memset(shakey, 0, sizeof(shakey));
1106 xmlFree(xml);
1107 return TRUE;
1110 gint get_key_file_integer(const gchar *section, const gchar *what)
1112 gint val = -1;
1113 GError *gerror = NULL;
1115 if (g_key_file_has_key(keyfileh, section, what, NULL) == TRUE) {
1116 val = g_key_file_get_integer(keyfileh, section, what, &gerror);
1118 if (gerror) {
1119 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
1120 g_clear_error(&gerror);
1123 else {
1124 if (g_key_file_has_key(keyfileh, "default", what, NULL) == TRUE) {
1125 val = g_key_file_get_integer(keyfileh, "default", what, &gerror);
1127 if (gerror) {
1128 log_write("%s(%i): %s", __FILE__, __LINE__, gerror->message);
1129 g_clear_error(&gerror);
1134 return val;
1137 int main(int argc, char *argv[])
1139 gint opt;
1140 struct sockaddr_un addr;
1141 struct passwd *pw = getpwuid(getuid());
1142 gchar buf[PATH_MAX];
1143 gchar *socketpath = NULL, *socketdir, *socketname = NULL;
1144 gchar *socketarg = NULL;
1145 gchar *datadir = NULL;
1146 gint fd;
1147 gboolean n;
1148 gchar *p;
1149 gchar **cache_push = NULL;
1150 gchar *rcfile;
1151 gint iter = 0;
1152 gchar *import = NULL;
1153 gint default_timeout;
1154 #ifndef DEBUG
1155 #ifdef HAVE_SETRLIMIT
1156 struct rlimit rl;
1158 rl.rlim_cur = rl.rlim_max = 0;
1160 if (setrlimit(RLIMIT_CORE, &rl) != 0)
1161 err(EXIT_FAILURE, "setrlimit()");
1162 #endif
1163 #endif
1165 rcfile = g_strdup_printf("%s/.pwmdrc", pw->pw_dir);
1167 if ((page_size = sysconf(_SC_PAGESIZE)) == -1)
1168 err(EXIT_FAILURE, "sysconf()");
1170 cache_size = page_size;
1172 #ifdef HAVE_MLOCKALL
1174 * Default to using mlockall().
1176 use_mlock = 1;
1177 #endif
1179 while ((opt = getopt(argc, argv, "I:hvf:")) != EOF) {
1180 switch (opt) {
1181 case 'I':
1182 import = optarg;
1183 break;
1184 case 'f':
1185 g_free(rcfile);
1186 rcfile = g_strdup(optarg);
1187 break;
1188 case 'v':
1189 printf("%s\n%s\n", PACKAGE_STRING, PACKAGE_BUGREPORT);
1190 exit(EXIT_SUCCESS);
1191 case 'h':
1192 default:
1193 usage(argv[0]);
1197 if ((keyfileh = parse_rcfile(rcfile)) == NULL)
1198 exit(EXIT_FAILURE);
1200 g_key_file_set_list_separator(keyfileh, ',');
1202 if ((p = g_key_file_get_string(keyfileh, "default", "socket_path", NULL)) == NULL)
1203 errx(EXIT_FAILURE, "%s: socket_path not defined", rcfile);
1205 if (*p == '~') {
1206 p++;
1207 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1208 g_free(p);
1209 socketarg = g_strdup(buf);
1211 else
1212 socketarg = p;
1214 if ((p = g_key_file_get_string(keyfileh, "default", "data_directory", NULL)) == NULL)
1215 errx(EXIT_FAILURE, "%s: data_directory not defined", rcfile);
1217 if (*p == '~') {
1218 p++;
1219 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1220 g_free(p);
1221 datadir = g_strdup(buf);
1223 else
1224 datadir = p;
1226 if (g_key_file_has_key(keyfileh, "default", "cache_timeout", NULL) == TRUE)
1227 default_timeout = g_key_file_get_integer(keyfileh, "default", "cache_timeout", NULL);
1228 else
1229 default_timeout = -1;
1231 if (g_key_file_has_key(keyfileh, "default", "cache_size", NULL) == TRUE) {
1232 cache_size = g_key_file_get_integer(keyfileh, "default", "cache_size", NULL);
1234 if (cache_size < page_size || cache_size % page_size)
1235 errx(EXIT_FAILURE, "cache size must be in multiples of %li.", page_size);
1238 if (g_key_file_has_key(keyfileh, "default", "iterations", NULL) == TRUE)
1239 iter = g_key_file_get_integer(keyfileh, "default", "iterations", NULL);
1241 #ifdef HAVE_MLOCKALL
1242 if (g_key_file_has_key(keyfileh, "default", "disable_mlockall", NULL) == TRUE)
1243 use_mlock = g_key_file_get_integer(keyfileh, "default", "disable_mlockall", NULL);
1244 #endif
1246 if (g_key_file_has_key(keyfileh, "default", "log_path", NULL) == TRUE) {
1247 if (g_key_file_has_key(keyfileh, "default", "enable_logging", NULL) == TRUE) {
1248 n = g_key_file_get_boolean(keyfileh, "default", "enable_logging", NULL);
1250 if (n == TRUE) {
1251 p = g_key_file_get_string(keyfileh, "default", "log_path", NULL);
1253 if (*p == '~') {
1254 p++;
1255 snprintf(buf, sizeof(buf), "%s%s", g_get_home_dir(), p--);
1256 g_free(p);
1257 logfile = g_strdup(buf);
1259 else
1260 logfile = p;
1265 if (g_key_file_has_key(keyfileh, "default", "cache_push", NULL) == TRUE)
1266 cache_push = g_key_file_get_string_list(keyfileh, "default", "cache_push", NULL, NULL);
1268 if (strchr(socketarg, '/') == NULL) {
1269 socketdir = g_get_current_dir();
1270 socketname = g_strdup(socketarg);
1271 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1273 else {
1274 socketname = g_strdup(strrchr(socketarg, '/'));
1275 socketname++;
1276 socketarg[strlen(socketarg) - strlen(socketname) -1] = 0;
1277 socketdir = g_strdup(socketarg);
1278 socketpath = g_strdup_printf("%s/%s", socketdir, socketname);
1281 snprintf(buf, sizeof(buf), "%s", datadir);
1283 if (mkdir(buf, 0700) == -1 && errno != EEXIST)
1284 err(EXIT_FAILURE, "%s", buf);
1286 #ifdef MMAP_ANONYMOUS_SHARED
1287 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE,
1288 MAP_SHARED|MAP_ANONYMOUS, -1, 0)) == NULL) {
1289 err(EXIT_FAILURE, "mmap()");
1291 #else
1292 snprintf(buf, sizeof(buf), "pwmd.%i", pw->pw_uid);
1294 if ((fd = shm_open(buf, O_CREAT|O_RDWR|O_EXCL, 0600)) == -1)
1295 err(EXIT_FAILURE, "shm_open(): %s", buf);
1298 * Should be enough for the file cache.
1300 if (ftruncate(fd, cache_size) == -1)
1301 err(EXIT_FAILURE, "ftruncate()");
1303 if ((shm_data = mmap(NULL, cache_size, PROT_READ|PROT_WRITE, MAP_SHARED,
1304 fd, 0)) == NULL) {
1305 shm_unlink(buf);
1306 err(EXIT_FAILURE, "mmap()");
1309 close(fd);
1310 #endif
1312 if (mlock(shm_data, cache_size) == -1)
1313 warn("mlock()");
1315 memset(shm_data, 0, cache_size);
1316 setup_gcrypt();
1318 if (import) {
1319 opt = xml_import(import, iter);
1320 g_free(socketpath);
1322 if (munmap(shm_data, cache_size) == -1)
1323 log_write("munmap(): %s", strerror(errno));
1325 #ifndef MMAP_ANONYMOUS_SHARED
1326 if (shm_unlink(buf) == -1)
1327 log_write("shm_unlink(): %s: %s", buf, strerror(errno));
1328 #endif
1330 exit((opt) == FALSE ? EXIT_FAILURE : EXIT_SUCCESS);
1334 * bind() doesn't like the full pathname of the socket or any non alphanum
1335 * characters so change to the directory where the socket is wanted then
1336 * create it then change to datadir.
1338 if (chdir(socketdir))
1339 err(EXIT_FAILURE, "%s", socketdir);
1341 g_free(socketdir);
1343 if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
1344 err(EXIT_FAILURE, "socket()");
1346 addr.sun_family = AF_UNIX;
1347 snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socketname);
1348 g_free(--socketname);
1350 if (bind(sfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)) == -1)
1351 err(EXIT_FAILURE, "bind()");
1353 if (chdir(datadir)) {
1354 close(sfd);
1355 unlink(socketpath);
1356 err(EXIT_FAILURE, "%s", datadir);
1359 g_free(datadir);
1362 * Set the cache entry for a file. Prompts for the password.
1364 if (cache_push) {
1365 guchar *md5file;
1366 guchar *key;
1367 gint timeout = default_timeout;
1369 for (opt = 0; cache_push[opt]; opt++) {
1370 p = cache_push[opt];
1372 while (isspace(*p))
1373 p++;
1375 if (!*p)
1376 continue;
1378 md5file = gcry_malloc(16);
1379 key = gcry_malloc(gcrykeysize);
1380 gcry_md_hash_buffer(GCRY_MD_MD5, md5file, p, strlen(p));
1382 if (access(p, R_OK|W_OK) != 0) {
1383 if (errno != ENOENT) {
1384 unlink(socketpath);
1385 err(EXIT_FAILURE, "%s", p);
1388 warn("%s", p);
1389 continue;
1392 if (get_input(p, key) == FALSE) {
1393 gcry_free(key);
1394 gcry_free(md5file);
1395 continue;
1398 if (cache_add_file(md5file, key) == FALSE) {
1399 gcry_free(key);
1400 errx(EXIT_FAILURE, "%s: couldn't add file (cache_size?)", p);
1403 timeout = get_key_file_integer(p, "cache_timeout");
1404 cache_set_timeout(md5file, timeout);
1405 fprintf(stderr, "Added.\n");
1406 gcry_free(key);
1407 gcry_free(md5file);
1410 g_strfreev(cache_push);
1411 fprintf(stderr, "Done! Daemonizing...\n");
1414 //g_key_file_free(kf);
1415 g_free(rcfile);
1417 if (listen(sfd, 0) == -1)
1418 err(EXIT_FAILURE, "listen()");
1420 signal(SIGCHLD, catchsig);
1421 signal(SIGTERM, catchsig);
1422 signal(SIGINT, catchsig);
1423 signal(SIGHUP, catchsig);
1424 signal(SIGALRM, catchsig);
1425 alarm(1);
1427 #ifndef DEBUG
1428 close(0);
1429 close(1);
1430 close(2);
1431 #endif
1433 log_write("%s starting: %li slots available", PACKAGE_STRING, cache_size / sizeof(file_cache_t));
1435 while (!quit) {
1436 socklen_t slen = sizeof(struct sockaddr_un);
1437 struct sockaddr_un raddr;
1438 pid_t pid;
1440 if ((fd = accept(sfd, (struct sockaddr_un *)&raddr, &slen)) == -1) {
1441 if (errno == EAGAIN)
1442 continue;
1444 if (!quit)
1445 log_write("accept(): %s", strerror(errno));
1447 continue;
1450 switch ((pid = fork())) {
1451 case -1:
1452 log_write("fork(): %s", strerror(errno));
1453 break;
1454 case 0:
1455 doit(fd);
1456 break;
1457 default:
1458 break;
1461 log_write("new connection: fd=%i, pid=%i", fd, pid);
1464 unlink(socketpath);
1465 g_free(socketpath);
1467 if (munmap(shm_data, cache_size) == -1)
1468 log_write("munmap(): %s", strerror(errno));
1470 #ifndef MMAP_ANONYMOUS_SHARED
1471 if (shm_unlink(buf) == -1)
1472 log_write("shm_unlink(): %s: %s", buf, strerror(errno));
1473 #endif
1475 log_write("pwmd exiting normally");
1476 exit(EXIT_SUCCESS);