1 /* vim:tw=78:ts=8:sw=4:set ft=c: */
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
26 #include <sys/socket.h>
35 #include <glib/gprintf.h>
47 #include <sys/resource.h>
53 #include "pwmd_error.h"
56 void send_to_client(struct client_s
*client
, const gchar
*fmt
, ...)
68 for (p
= client
->outbuf
, n
= 0; *p
; p
++, n
++);
70 if ((client
->outbuf
= g_realloc(client
->outbuf
, (n
+ 2) * sizeof(gchar
*))) == NULL
) {
75 client
->outbuf
[n
++] = g_strdup_vprintf(fmt
, ap
);
76 client
->outbuf
[n
] = NULL
;
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
, ...)
93 gint tofile
= !isatty(STDOUT_FILENO
);
99 if ((fd
= open(logfile
, O_WRONLY
|O_CREAT
|O_APPEND
, 0600)) == -1) {
106 g_vasprintf(&args
, fmt
, ap
);
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
));
121 fprintf(stderr
, "%s\n", args
);
126 static void cache_adjust_timer()
132 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
133 memcpy(&f
, p
, sizeof(file_cache_t
));
135 if (f
.timeout
>= 0 && f
.when
> 0) {
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
)
152 static void catchsig(gint sig
)
157 log_write("caught signal %i (%s)", sig
, strsignal(sig
));
161 cache_adjust_timer();
165 waitpid(-1, &status
, 0);
168 log_write("clearing file cache");
169 memset(shm_data
, 0, cache_size
);
173 shutdown(sfd
, SHUT_RDWR
);
179 static void usage(gchar
*pn
)
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"
186 " -h this help text\n",
191 gchar
**split_input_line(gchar
*str
, gchar
*delim
, gint n
)
196 return g_strsplit(str
, delim
, n
);
199 static int gcry_SecureCheck(const void *ptr
)
204 static void setup_gcrypt()
206 gcry_check_version(NULL
);
208 gcry_set_allocation_handler(xmalloc
, xmalloc
, gcry_SecureCheck
, xrealloc
,
211 if (gcry_cipher_algo_info(GCRY_CIPHER_AES256
, GCRYCTL_TEST_ALGO
, NULL
,
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
)
225 str
= g_strchug(str
);
230 while ((p
= strsep(&str
, "\n")) != NULL
) {
231 if (g_ascii_strcasecmp(p
, "QUIT") == 0)
233 else if (g_ascii_strcasecmp(p
, "HELP") == 0)
234 help_command(cl
, NULL
);
235 else if (g_ascii_strncasecmp(p
, "HELP ", 5) == 0) {
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
);
247 else if (g_ascii_strncasecmp(p
, "STORE ", 6) == 0) {
251 if (cl
->state
!= STATE_OPEN
)
252 send_error(cl
, EPWMD_NO_FILE
);
254 if ((req
= split_input_line(t
, "\t", 0)) != NULL
) {
255 if (store_command(cl
, req
) == TRUE
)
256 send_to_client(cl
, "OK \n");
259 send_error(cl
, EPWMD_COMMAND_SYNTAX
);
262 else if (g_ascii_strncasecmp(p
, "DELETE ", 7) == 0) {
265 if (cl
->state
!= STATE_OPEN
)
266 send_error(cl
, EPWMD_NO_FILE
);
268 if ((req
= split_input_line(t
, "\t", 0)) != NULL
) {
269 if (delete_command(cl
, req
) == TRUE
)
270 send_to_client(cl
, "OK \n");
273 send_error(cl
, EPWMD_COMMAND_SYNTAX
);
276 else if (g_ascii_strncasecmp(p
, "GET ", 4) == 0) {
280 if (cl
->state
!= STATE_OPEN
)
281 send_error(cl
, EPWMD_NO_FILE
);
283 if ((req
= split_input_line(t
, "\t", 0)) != NULL
)
284 get_command(cl
, &cl
->reader
, req
, 0);
286 send_error(cl
, EPWMD_COMMAND_SYNTAX
);
289 else if (g_ascii_strncasecmp(p
, "ATTR ", 5) == 0) {
293 if (cl
->state
!= STATE_OPEN
)
294 send_error(cl
, EPWMD_NO_FILE
);
296 if ((req
= split_input_line(t
, " ", 4)) != NULL
) {
297 if (attr_command(cl
, req
) == TRUE
)
298 send_to_client(cl
, "OK \n");
301 send_error(cl
, EPWMD_COMMAND_SYNTAX
);
304 else if (g_ascii_strncasecmp(p
, "OPEN ", 5) == 0) {
308 if (cl
->state
== STATE_OPEN
)
309 send_error(cl
, EPWMD_FILE_OPENED
);
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.
326 send_error(cl
, EPWMD_COMMAND_SYNTAX
);
329 else if (g_ascii_strncasecmp(p
, "SAVE", 4) == 0) {
333 send_error(cl
, EPWMD_COMMAND_SYNTAX
);
337 if (cl
->state
!= STATE_OPEN
)
338 send_error(cl
, EPWMD_NO_FILE
);
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) {
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
);
359 if (dump_command(cl
) == TRUE
)
360 send_to_client(cl
, "OK \n");
364 send_error(cl
, EPWMD_COMMAND_SYNTAX
);
375 static gboolean
source_prepare(GSource
*src
, gint
*to
)
377 if (cl
->gfd
.revents
& (G_IO_HUP
|G_IO_NVAL
|G_IO_ERR
))
383 static gboolean
source_check(GSource
*src
)
385 if (cl
->gfd
.revents
& (G_IO_IN
|G_IO_PRI
))
388 if (cl
->outbuf
&& (cl
->gfd
.revents
& (G_IO_OUT
)))
394 static gboolean
source_dispatch(GSource
*src
, GSourceFunc cb
, gpointer data
)
399 static gboolean
source_cb(gpointer data
)
404 GError
*gerror
= NULL
;
407 if (cl
->gfd
.revents
& (G_IO_HUP
|G_IO_NVAL
|G_IO_ERR
))
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
);
417 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gerror
->message
);
419 g_clear_error(&gerror
);
425 g_strfreev(cl
->outbuf
);
429 if (!cl
->gfd
.revents
& (G_IO_IN
))
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
)
438 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gerror
->message
);
439 g_clear_error(&gerror
);
443 line
[g_utf8_strlen(line
, -1) - 1] = 0;
445 switch (input_parser(line
)) {
449 memset(line
, 0, len
);
453 g_clear_error(&gerror
);
459 gcry_cipher_close(cl
->gh
);
465 xmlFreeTextReader(cl
->reader
);
471 g_free(cl
->filename
);
475 g_slist_free(memlist
);
478 g_main_loop_unref(gloop
);
479 g_main_loop_quit(gloop
);
485 memset(line
, 0, len
);
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
);
521 static struct memlist_s
*memlist_append(struct memlist_s
*list
, struct memlist_s
*new)
531 for (m
= list
; m
; m
= m
->next
) {
543 void xfree(void *ptr
)
550 for (m
= memlist
; m
; m
= m
->next
) {
553 fprintf(stderr
, "xfree(): %p %i\n", ptr
, m
->size
);
555 memlist
= memlist_remove(memlist
, m
);
560 warnx("xfree(): %p not found", ptr
);
564 void *xmalloc(size_t size
)
567 struct memlist_s
*new;
572 if ((new = malloc(sizeof(struct memlist_s
))) == NULL
)
575 if ((p
= malloc(size
)) == NULL
) {
582 memlist
= memlist_append(memlist
, new);
584 fprintf(stderr
, "xmalloc(): %p %i\n", p
, size
);
589 void *xcalloc(size_t nmemb
, size_t size
)
592 struct memlist_s
*new;
597 if ((new = malloc(sizeof(struct memlist_s
))) == NULL
)
600 if ((p
= calloc(nmemb
, size
)) == NULL
) {
607 memlist
= memlist_append(memlist
, new);
609 fprintf(stderr
, "xcalloc(): %p %i\n", p
, size
);
614 void *xrealloc(void *ptr
, size_t size
)
620 return xmalloc(size
);
625 for (m
= memlist
; m
; m
= m
->next
) {
627 if ((new = malloc(size
)) == NULL
)
630 memcpy(new, m
->ptr
, (size
< m
->size
) ? size
: m
->size
);
631 memset(m
->ptr
, 0, m
->size
);
636 fprintf(stderr
, "xrealloc(): %p %i\n", new, size
);
642 warnx("xrealloc(): %p not found", ptr
);
647 char *xstrdup(const char *str
)
657 len
= strlen(str
) + 1;
659 if ((new = xmalloc(len
* sizeof(char))) == NULL
)
662 for (p
= str
, np
= new; *p
; p
++)
667 fprintf(stderr
, "xstrdup(): %p\n", 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 };
682 signal(SIGCHLD
, SIG_DFL
);
683 signal(SIGHUP
, SIG_DFL
);
684 signal(SIGINT
, SIG_DFL
);
685 signal(SIGALRM
, SIG_DFL
);
688 if (use_mlock
&& mlockall(MCL_FUTURE
) == -1) {
689 log_write("mlockall(): %s", strerror(errno
));
694 gloop
= g_main_loop_new(NULL
, TRUE
);
695 cl
= g_malloc0(sizeof(struct client_s
));
696 cl
->src
= g_source_new(&gsrcf
, sizeof(GSource
));
698 cl
->state
= STATE_CONNECTED
;
699 cl
->ioc
= g_io_channel_unix_new(fd
);
700 g_source_add_poll(cl
->src
, &cl
->gfd
);
701 g_source_set_callback(cl
->src
, source_cb
, NULL
, NULL
);
702 g_source_attach(cl
->src
, NULL
);
704 xmlMemSetup(xfree
, xmalloc
, xrealloc
, xstrdup
);
707 if ((gcryerrno
= gcry_cipher_open(&cl
->gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
708 send_to_client(cl
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
709 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
713 // FIXME 100% CPU if removed (poll()).
714 send_to_client(cl
, "OK \n");
716 g_main_loop_run(gloop
);
717 g_io_channel_unref(cl
->ioc
);
719 shutdown(fd
, SHUT_RDWR
);
720 log_write("exiting");
724 static void set_rcfile_defaults(GKeyFile
*kf
)
728 snprintf(buf
, sizeof(buf
), "~/.pwmd/socket");
729 g_key_file_set_string(kf
, "default", "socket_path", buf
);
730 snprintf(buf
, sizeof(buf
), "~/.pwmd");
731 g_key_file_set_string(kf
, "default", "data_directory", buf
);
732 snprintf(buf
, sizeof(buf
), "~/.pwmd/.log");
733 g_key_file_set_string(kf
, "default", "log_path", buf
);
734 g_key_file_set_boolean(kf
, "default", "enable_logging", FALSE
);
735 g_key_file_set_integer(kf
, "default", "cache_size", cache_size
);
736 g_key_file_set_boolean(kf
, "default", "disable_mlockall", FALSE
);
737 g_key_file_set_integer(kf
, "default", "cache_timeout", -1);
738 g_key_file_set_integer(kf
, "default", "iterations", 0);
741 static GKeyFile
*parse_rcfile(const gchar
*filename
, int cmdline
)
743 GKeyFile
*kf
= g_key_file_new();
744 GError
*error
= NULL
;
746 if (g_key_file_load_from_file(kf
, filename
, G_KEY_FILE_NONE
, &error
) == FALSE
) {
747 warnx("%s: %s", filename
, error
->message
);
752 if (error
->code
== G_FILE_ERROR_NOENT
) {
753 g_clear_error(&error
);
754 set_rcfile_defaults(kf
);
758 g_clear_error(&error
);
765 static gboolean
try_xml_decrypt(gint fd
, struct stat st
, guchar
*key
)
771 guchar tkey
[gcrykeysize
];
773 struct file_header_s
{
775 guchar iv
[gcryblocksize
];
778 if ((gcryerrno
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
779 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
783 lseek(fd
, 0, SEEK_SET
);
784 insize
= st
.st_size
- sizeof(struct file_header_s
);
785 iv
= gcry_malloc(gcryblocksize
);
786 read(fd
, &file_header
, sizeof(struct file_header_s
));
787 memcpy(iv
, &file_header
.iv
, sizeof(file_header
.iv
));
788 inbuf
= gcry_malloc(insize
);
789 read(fd
, inbuf
, insize
);
790 memcpy(tkey
, key
, sizeof(tkey
));
793 if ((gcryerrno
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
794 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
795 gcry_cipher_close(gh
);
801 if ((gcryerrno
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
802 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
803 gcry_cipher_close(gh
);
809 if (decrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
810 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
811 gcry_cipher_close(gh
);
817 if ((gcryerrno
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
818 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
819 gcry_cipher_close(gh
);
825 iter
= file_header
.iter
;
828 if ((gcryerrno
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
829 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
830 gcry_cipher_close(gh
);
836 if (decrypt_xml(gh
, inbuf
, insize
, NULL
, 0) == FALSE
) {
837 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
838 gcry_cipher_close(gh
);
845 gcry_cipher_close(gh
);
848 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
857 static gchar
*get_password(const gchar
*prompt
)
859 gchar buf
[LINE_MAX
], *p
;
860 struct termios told
, tnew
;
863 if (tcgetattr(STDIN_FILENO
, &told
) == -1)
864 err(EXIT_FAILURE
, "tcgetattr()");
866 memcpy(&tnew
, &told
, sizeof(struct termios
));
867 tnew
.c_lflag
&= ~(ECHO
);
868 tnew
.c_lflag
|= ICANON
|ECHONL
;
870 if (tcsetattr(STDIN_FILENO
, TCSANOW
, &tnew
) == -1) {
871 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
872 err(EXIT_FAILURE
, "tcsetattr()");
875 fprintf(stderr
, "%s", prompt
);
877 if ((p
= fgets(buf
, sizeof(buf
), stdin
)) == NULL
) {
878 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
882 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
883 p
[strlen(p
) - 1] = 0;
888 key
= gcry_malloc(strlen(p
) + 1);
889 sprintf(key
, "%s", p
);
893 static gboolean
get_input(const gchar
*filename
, guchar
*key
)
901 if ((fd
= open_file(filename
, &st
)) == -1) {
902 warn("%s", filename
);
906 if (st
.st_size
== 0) {
907 fprintf(stderr
, "Skipping empty file '%s'.\n", filename
);
912 if ((prompt
= xmalloc(strlen(filename
) + strlen("Password for '") + 4)) == NULL
) {
918 sprintf(prompt
, "Password for '%s': ", filename
);
921 if ((password
= get_password(prompt
)) == NULL
) {
922 fprintf(stderr
, "Skipping.\n");
928 gcry_md_hash_buffer(GCRY_MD_SHA256
, key
, password
, strlen(password
));
931 if (try_xml_decrypt(fd
, st
, key
) == FALSE
) {
933 fprintf(stderr
, "Invalid password. Skipping file.\n");
939 fprintf(stderr
, "Invalid password.\n");
949 gint
cache_file_count()
956 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
957 memcpy(&f
, p
, sizeof(file_cache_t
));
962 p
+= sizeof(file_cache_t
);
963 len
+= sizeof(file_cache_t
);
965 if (len
+ sizeof(file_cache_t
) > cache_size
)
972 gboolean
cache_add_file(const guchar
*md5file
, const guchar
*shakey
)
976 gint nfiles
= cache_file_count();
980 * Make sure there is enough secure memory.
982 if (!md5file
|| (nfiles
+ 1) * sizeof(file_cache_t
) > cache_size
)
986 * Find the first available "slot".
988 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
989 memcpy(&f
, p
, sizeof(file_cache_t
));
991 if (f
.used
== FALSE
) {
992 memset(&f
, 0, sizeof(f
));
993 memcpy(&f
.filename
, md5file
, sizeof(f
.filename
));
996 memcpy(&f
.key
, shakey
, sizeof(f
.key
));
1001 memcpy(p
, &f
, sizeof(file_cache_t
));
1005 p
+= sizeof(file_cache_t
);
1006 len
+= sizeof(file_cache_t
);
1008 if (len
+ sizeof(file_cache_t
) > cache_size
)
1015 static gboolean
xml_import(const gchar
*filename
, gint iter
)
1025 guchar shakey
[gcrykeysize
];
1026 gcry_cipher_hd_t gh
;
1028 #ifdef HAVE_MLOCKALL
1029 if (use_mlock
&& mlockall(MCL_FUTURE
) == -1)
1030 err(EXIT_FAILURE
, "mlockall()");
1033 if (stat(filename
, &st
) == -1) {
1034 warn("%s", filename
);
1038 xmlMemSetup(xfree
, xmalloc
, xrealloc
, xstrdup
);
1041 if ((gcryerrno
= gcry_cipher_open(&gh
, GCRY_CIPHER_AES256
, GCRY_CIPHER_MODE_CBC
, 0))) {
1042 send_to_client(NULL
, "ERR %03i gcrypt: %s\n", EPWMD_ERROR
, gcry_strerror(gcryerrno
));
1043 log_write("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
1047 if ((key
= get_password("New password: ")) == NULL
) {
1048 fprintf(stderr
, "Invalid password.\n");
1052 if ((key2
= get_password("Verify password: ")) == NULL
) {
1053 fprintf(stderr
, "Passwords do not match.\n");
1058 if (strcmp(key
, key2
) != 0) {
1059 fprintf(stderr
, "Passwords do not match.\n");
1067 if ((fd
= open(filename
, O_RDONLY
)) == -1) {
1069 warn("%s", filename
);
1073 if ((xmlbuf
= gcry_malloc(st
.st_size
+1)) == NULL
) {
1076 warnx("gcry_malloc() failed");
1080 read(fd
, xmlbuf
, st
.st_size
);
1082 xmlbuf
[st
.st_size
] = 0;
1085 * Make sure the document validates.
1087 if ((doc
= xmlReadDoc(xmlbuf
, NULL
, "UTF-8", XML_PARSE_NOBLANKS
)) == NULL
) {
1088 warnx("xmlReadDoc() failed");
1096 xmlDocDumpMemory(doc
, &xml
, &len
);
1098 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, key
, strlen(key
));
1101 if (do_xml_encrypt(NULL
, gh
, NULL
, xml
, len
, shakey
, iter
) == FALSE
) {
1102 memset(shakey
, 0, sizeof(shakey
));
1107 memset(shakey
, 0, sizeof(shakey
));
1112 gchar
*get_key_file_string(const gchar
*section
, const gchar
*what
)
1115 GError
*gerror
= NULL
;
1117 if (g_key_file_has_key(keyfileh
, section
, what
, NULL
) == TRUE
) {
1118 val
= g_key_file_get_string(keyfileh
, section
, what
, &gerror
);
1121 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1122 g_clear_error(&gerror
);
1126 if (g_key_file_has_key(keyfileh
, "default", what
, NULL
) == TRUE
) {
1127 val
= g_key_file_get_string(keyfileh
, "default", what
, &gerror
);
1130 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1131 g_clear_error(&gerror
);
1139 gint
get_key_file_integer(const gchar
*section
, const gchar
*what
)
1142 GError
*gerror
= NULL
;
1144 if (g_key_file_has_key(keyfileh
, section
, what
, NULL
) == TRUE
) {
1145 val
= g_key_file_get_integer(keyfileh
, section
, what
, &gerror
);
1148 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1149 g_clear_error(&gerror
);
1153 if (g_key_file_has_key(keyfileh
, "default", what
, NULL
) == TRUE
) {
1154 val
= g_key_file_get_integer(keyfileh
, "default", what
, &gerror
);
1157 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1158 g_clear_error(&gerror
);
1166 gboolean
get_key_file_boolean(const gchar
*section
, const gchar
*what
)
1168 gboolean val
= FALSE
;
1169 GError
*gerror
= NULL
;
1171 if (g_key_file_has_key(keyfileh
, section
, what
, NULL
) == TRUE
) {
1172 val
= g_key_file_get_boolean(keyfileh
, section
, what
, &gerror
);
1175 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1176 g_clear_error(&gerror
);
1180 if (g_key_file_has_key(keyfileh
, "default", what
, NULL
) == TRUE
) {
1181 val
= g_key_file_get_boolean(keyfileh
, "default", what
, &gerror
);
1184 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1185 g_clear_error(&gerror
);
1193 gchar
*expand_homedir(gchar
*str
)
1196 gchar buf
[PATH_MAX
];
1200 snprintf(buf
, sizeof(buf
), "%s%s", g_get_home_dir(), p
--);
1201 return g_strdup(buf
);
1204 return g_strdup(str
);
1207 static gboolean
do_add_keyfile_key(const gchar
*filename
, const gchar
*value
)
1214 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, filename
, strlen(filename
));
1215 shakey
= gcry_malloc(gcrykeysize
);
1216 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, value
, strlen(value
));
1217 ret
= cache_add_file(md5file
, shakey
);
1219 timeout
= get_key_file_integer(filename
, "cache_timeout");
1220 cache_set_timeout(md5file
, timeout
);
1224 static gchar
*_getline(const gchar
*file
)
1227 gchar buf
[LINE_MAX
], *p
;
1230 if ((fp
= fopen(file
, "r")) == NULL
) {
1235 if ((p
= fgets(buf
, sizeof(buf
), fp
)) == NULL
) {
1236 warnx("%s: empty file?", file
);
1241 buf
[strlen(buf
) - 1] = 0;
1242 str
= gcry_malloc(strlen(p
) + 1);
1243 memcpy(str
, p
, strlen(p
));
1245 memset(&buf
, 0, sizeof(buf
));
1249 static gboolean
parse_keyfile_key()
1256 groups
= g_key_file_get_groups(keyfileh
, &n
);
1258 for (p
= groups
; *p
; p
++) {
1259 GError
*error
= NULL
;
1261 if (g_key_file_has_key(keyfileh
, *p
, "key", &error
) == TRUE
) {
1262 str
= g_key_file_get_string(keyfileh
, *p
, "key", &error
);
1266 warnx("%s", error
->message
);
1267 g_clear_error(&error
);
1273 do_add_keyfile_key(*p
, str
);
1279 warnx("%s", error
->message
);
1280 g_clear_error(&error
);
1284 if (g_key_file_has_key(keyfileh
, *p
, "key_file", &error
) == TRUE
) {
1286 gchar
*file
= g_key_file_get_string(keyfileh
, *p
, "key_file", &error
);
1290 warnx("%s", error
->message
);
1291 g_clear_error(&error
);
1297 t
= expand_homedir(file
);
1301 if ((str
= _getline(file
)) == NULL
) {
1307 do_add_keyfile_key(*p
, str
);
1313 warnx("%s", error
->message
);
1314 g_clear_error(&error
);
1322 int main(int argc
, char *argv
[])
1325 struct sockaddr_un addr
;
1326 struct passwd
*pw
= getpwuid(getuid());
1327 gchar buf
[PATH_MAX
];
1328 gchar
*socketpath
= NULL
, *socketdir
, *socketname
= NULL
;
1329 gchar
*socketarg
= NULL
;
1330 gchar
*datadir
= NULL
;
1334 gchar
**cache_push
= NULL
;
1337 gchar
*import
= NULL
;
1338 gint default_timeout
;
1339 gint rcfile_spec
= 0;
1340 gint estatus
= EXIT_FAILURE
;
1341 GMemVTable mtable
= { xmalloc
, xrealloc
, xfree
, xcalloc
, NULL
, NULL
};
1343 #ifdef HAVE_SETRLIMIT
1346 rl
.rlim_cur
= rl
.rlim_max
= 0;
1348 if (setrlimit(RLIMIT_CORE
, &rl
) != 0)
1349 err(EXIT_FAILURE
, "setrlimit()");
1353 g_mem_set_vtable(&mtable
);
1354 rcfile
= g_strdup_printf("%s/.pwmdrc", pw
->pw_dir
);
1356 if ((page_size
= sysconf(_SC_PAGESIZE
)) == -1)
1357 err(EXIT_FAILURE
, "sysconf()");
1359 cache_size
= page_size
;
1361 #ifdef HAVE_MLOCKALL
1363 * Default to using mlockall().
1368 while ((opt
= getopt(argc
, argv
, "I:hvf:")) != EOF
) {
1375 rcfile
= g_strdup(optarg
);
1379 printf("%s\n%s\n", PACKAGE_STRING
, PACKAGE_BUGREPORT
);
1387 if ((keyfileh
= parse_rcfile(rcfile
, rcfile_spec
)) == NULL
)
1390 g_key_file_set_list_separator(keyfileh
, ',');
1392 if ((p
= g_key_file_get_string(keyfileh
, "default", "socket_path", NULL
)) == NULL
)
1393 errx(EXIT_FAILURE
, "%s: socket_path not defined", rcfile
);
1397 snprintf(buf
, sizeof(buf
), "%s%s", g_get_home_dir(), p
--);
1399 socketarg
= g_strdup(buf
);
1404 if ((p
= g_key_file_get_string(keyfileh
, "default", "data_directory", NULL
)) == NULL
)
1405 errx(EXIT_FAILURE
, "%s: data_directory not defined", rcfile
);
1407 datadir
= expand_homedir(p
);
1410 if (g_key_file_has_key(keyfileh
, "default", "cache_timeout", NULL
) == TRUE
)
1411 default_timeout
= g_key_file_get_integer(keyfileh
, "default", "cache_timeout", NULL
);
1413 default_timeout
= -1;
1415 if (g_key_file_has_key(keyfileh
, "default", "cache_size", NULL
) == TRUE
) {
1416 cache_size
= g_key_file_get_integer(keyfileh
, "default", "cache_size", NULL
);
1418 if (cache_size
< page_size
|| cache_size
% page_size
)
1419 errx(EXIT_FAILURE
, "cache size must be in multiples of %li.", page_size
);
1422 if (g_key_file_has_key(keyfileh
, "default", "iterations", NULL
) == TRUE
)
1423 iter
= g_key_file_get_integer(keyfileh
, "default", "iterations", NULL
);
1425 #ifdef HAVE_MLOCKALL
1426 if (g_key_file_has_key(keyfileh
, "default", "disable_mlockall", NULL
) == TRUE
)
1427 use_mlock
= g_key_file_get_integer(keyfileh
, "default", "disable_mlockall", NULL
);
1430 if (g_key_file_has_key(keyfileh
, "default", "log_path", NULL
) == TRUE
) {
1431 if (g_key_file_has_key(keyfileh
, "default", "enable_logging", NULL
) == TRUE
) {
1432 n
= g_key_file_get_boolean(keyfileh
, "default", "enable_logging", NULL
);
1435 p
= g_key_file_get_string(keyfileh
, "default", "log_path", NULL
);
1439 snprintf(buf
, sizeof(buf
), "%s%s", g_get_home_dir(), p
--);
1441 logfile
= g_strdup(buf
);
1449 if (g_key_file_has_key(keyfileh
, "default", "cache_push", NULL
) == TRUE
)
1450 cache_push
= g_key_file_get_string_list(keyfileh
, "default", "cache_push", NULL
, NULL
);
1452 if (strchr(socketarg
, '/') == NULL
) {
1453 socketdir
= g_get_current_dir();
1454 socketname
= g_strdup(socketarg
);
1455 socketpath
= g_strdup_printf("%s/%s", socketdir
, socketname
);
1458 socketname
= g_strdup(strrchr(socketarg
, '/'));
1460 socketarg
[strlen(socketarg
) - strlen(socketname
) -1] = 0;
1461 socketdir
= g_strdup(socketarg
);
1462 socketpath
= g_strdup_printf("%s/%s", socketdir
, socketname
);
1465 snprintf(buf
, sizeof(buf
), "%s", datadir
);
1467 if (mkdir(buf
, 0700) == -1 && errno
!= EEXIST
)
1468 err(EXIT_FAILURE
, "%s", buf
);
1470 #ifdef MMAP_ANONYMOUS_SHARED
1471 if ((shm_data
= mmap(NULL
, cache_size
, PROT_READ
|PROT_WRITE
,
1472 MAP_SHARED
|MAP_ANONYMOUS
, -1, 0)) == NULL
) {
1473 err(EXIT_FAILURE
, "mmap()");
1476 snprintf(buf
, sizeof(buf
), "pwmd.%i", pw
->pw_uid
);
1478 if ((fd
= shm_open(buf
, O_CREAT
|O_RDWR
|O_EXCL
, 0600)) == -1)
1479 err(EXIT_FAILURE
, "shm_open(): %s", buf
);
1482 * Should be enough for the file cache.
1484 if (ftruncate(fd
, cache_size
) == -1) {
1485 warn("ftruncate()");
1490 if ((shm_data
= mmap(NULL
, cache_size
, PROT_READ
|PROT_WRITE
, MAP_SHARED
,
1500 if (mlock(shm_data
, cache_size
) == -1)
1503 memset(shm_data
, 0, cache_size
);
1507 opt
= xml_import(import
, iter
);
1510 if (munmap(shm_data
, cache_size
) == -1)
1511 log_write("munmap(): %s", strerror(errno
));
1513 #ifndef MMAP_ANONYMOUS_SHARED
1514 if (shm_unlink(buf
) == -1)
1515 log_write("shm_unlink(): %s: %s", buf
, strerror(errno
));
1518 exit((opt
) == FALSE
? EXIT_FAILURE
: EXIT_SUCCESS
);
1521 if (parse_keyfile_key() == FALSE
)
1524 getcwd(buf
, sizeof(buf
));
1527 * bind() doesn't like the full pathname of the socket or any non alphanum
1528 * characters so change to the directory where the socket is wanted then
1529 * create it then change to datadir.
1531 if (chdir(socketdir
)) {
1532 warn("%s", socketdir
);
1538 if ((sfd
= socket(PF_UNIX
, SOCK_STREAM
, 0)) == -1) {
1543 addr
.sun_family
= AF_UNIX
;
1544 snprintf(addr
.sun_path
, sizeof(addr
.sun_path
), "%s", socketname
);
1545 g_free(--socketname
);
1547 if (bind(sfd
, (struct sockaddr
*)&addr
, sizeof(struct sockaddr
)) == -1) {
1552 if (chdir(datadir
)) {
1553 warn("%s", datadir
);
1562 * Set the cache entry for a file. Prompts for the password.
1567 gint timeout
= default_timeout
;
1569 for (opt
= 0; cache_push
[opt
]; opt
++) {
1570 p
= cache_push
[opt
];
1578 md5file
= gcry_malloc(16);
1579 key
= gcry_malloc(gcrykeysize
);
1580 gcry_md_hash_buffer(GCRY_MD_MD5
, md5file
, p
, strlen(p
));
1582 if (cache_iscached(md5file
) == TRUE
) {
1583 warnx("%s: file already cached, skipping", p
);
1588 if (access(p
, R_OK
|W_OK
) != 0) {
1589 if (errno
!= ENOENT
) {
1599 if (get_input(p
, key
) == FALSE
) {
1605 if (cache_add_file(md5file
, key
) == FALSE
) {
1606 warnx("%s: couldn't add file (cache_size?)", p
);
1611 timeout
= get_key_file_integer(p
, "cache_timeout");
1612 cache_set_timeout(md5file
, timeout
);
1613 fprintf(stderr
, "Added.\n");
1618 g_strfreev(cache_push
);
1619 fprintf(stderr
, "Done! Daemonizing...\n");
1625 if (listen(sfd
, 0) == -1) {
1630 signal(SIGCHLD
, catchsig
);
1631 signal(SIGTERM
, catchsig
);
1632 signal(SIGINT
, catchsig
);
1633 signal(SIGHUP
, catchsig
);
1634 signal(SIGALRM
, catchsig
);
1643 log_write("%s starting: %li slots available", PACKAGE_STRING
, cache_size
/ sizeof(file_cache_t
));
1646 socklen_t slen
= sizeof(struct sockaddr_un
);
1647 struct sockaddr_un raddr
;
1650 if ((fd
= accept(sfd
, (struct sockaddr_un
*)&raddr
, &slen
)) == -1) {
1651 if (errno
== EAGAIN
)
1655 log_write("accept(): %s", strerror(errno
));
1660 switch ((pid
= fork())) {
1662 log_write("fork(): %s", strerror(errno
));
1671 log_write("new connection: fd=%i, pid=%i", fd
, pid
);
1674 estatus
= EXIT_SUCCESS
;
1684 g_key_file_free(keyfileh
);
1686 if (munmap(shm_data
, cache_size
) == -1)
1687 log_write("munmap(): %s", strerror(errno
));
1689 #ifndef MMAP_ANONYMOUS_SHARED
1690 if (shm_unlink(buf
) == -1)
1691 log_write("shm_unlink(): %s: %s", buf
, strerror(errno
));
1694 if (estatus
== EXIT_SUCCESS
)
1695 log_write("pwmd exiting normally");