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_increment_elapsed()
132 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
133 memcpy(&f
, p
, sizeof(file_cache_t
));
135 if (f
.timeout
>= 0) {
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
)
152 static void catchsig(gint sig
)
157 log_write("caught signal %i (%s)", sig
, strsignal(sig
));
161 cache_increment_elapsed();
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 };
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
);
690 if (use_mlock
&& mlockall(MCL_FUTURE
) == -1) {
691 log_write("mlockall(): %s", strerror(errno
));
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
));
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
);
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
));
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
);
721 shutdown(fd
, SHUT_RDWR
);
722 log_write("exiting");
726 static void set_rcfile_defaults(GKeyFile
*kf
)
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
);
755 warnx("%s: %s", filename
, error
->message
);
756 g_clear_error(&error
);
764 static gboolean
try_xml_decrypt(gint fd
, struct stat st
, guchar
*key
)
770 guchar tkey
[gcrykeysize
];
772 struct file_header_s
{
774 guchar iv
[gcryblocksize
];
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
));
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
));
792 if ((gcryerrno
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
793 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
794 gcry_cipher_close(gh
);
800 if ((gcryerrno
= gcry_cipher_setkey(gh
, key
, gcrykeysize
))) {
801 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
802 gcry_cipher_close(gh
);
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
);
816 if ((gcryerrno
= gcry_cipher_setkey(gh
, tkey
, gcrykeysize
))) {
817 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
818 gcry_cipher_close(gh
);
824 iter
= file_header
.iter
;
827 if ((gcryerrno
= gcry_cipher_setiv(gh
, iv
, gcryblocksize
))) {
828 warnx("%s(%i): %s", __FUNCTION__
, __LINE__
, gcry_strerror(gcryerrno
));
829 gcry_cipher_close(gh
);
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
);
844 gcry_cipher_close(gh
);
847 if (g_strncasecmp(inbuf
, "<?xml version=\"1.0\"?>", 21) != 0) {
856 static gchar
*get_password(const gchar
*prompt
)
858 gchar buf
[LINE_MAX
], *p
;
859 struct termios told
, tnew
;
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
);
881 tcsetattr(STDIN_FILENO
, TCSANOW
, &told
);
882 p
[strlen(p
) - 1] = 0;
887 key
= gcry_malloc(strlen(p
) + 1);
888 sprintf(key
, "%s", p
);
892 static gboolean
get_input(const gchar
*filename
, guchar
*key
)
900 if ((fd
= open_file(filename
, &st
)) == -1) {
901 warn("%s", filename
);
905 if (st
.st_size
== 0) {
906 fprintf(stderr
, "Skipping empty file '%s'.\n", filename
);
911 if ((prompt
= xmalloc(strlen(filename
) + strlen("Password for '") + 4)) == NULL
) {
917 sprintf(prompt
, "Password for '%s': ", filename
);
920 if ((password
= get_password(prompt
)) == NULL
) {
921 fprintf(stderr
, "Skipping.\n");
927 gcry_md_hash_buffer(GCRY_MD_SHA256
, key
, password
, strlen(password
));
930 if (try_xml_decrypt(fd
, st
, key
) == FALSE
) {
932 fprintf(stderr
, "Invalid password. Skipping file.\n");
938 fprintf(stderr
, "Invalid password.\n");
948 gint
cache_file_count()
955 for (p
= shm_data
, len
= 0; len
<= cache_size
;) {
956 memcpy(&f
, p
, sizeof(file_cache_t
));
961 p
+= sizeof(file_cache_t
);
962 len
+= sizeof(file_cache_t
);
964 if (len
+ sizeof(file_cache_t
) > cache_size
)
971 gboolean
cache_add_file(const guchar
*md5file
, const guchar
*shakey
)
975 gint nfiles
= cache_file_count();
979 * Make sure there is enough secure memory.
981 if (!md5file
|| (nfiles
+ 1) * sizeof(file_cache_t
) > cache_size
)
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
));
994 memcpy(&f
.key
, shakey
, sizeof(f
.key
));
997 f
.elapsed
= f
.when
= 0;
999 memcpy(p
, &f
, sizeof(file_cache_t
));
1003 p
+= sizeof(file_cache_t
);
1004 len
+= sizeof(file_cache_t
);
1006 if (len
+ sizeof(file_cache_t
) > cache_size
)
1013 static gboolean
xml_import(const gchar
*filename
, gint iter
)
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()");
1031 if (stat(filename
, &st
) == -1) {
1032 warn("%s", filename
);
1036 xmlMemSetup(xfree
, xmalloc
, xrealloc
, xstrdup
);
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
));
1045 if ((key
= get_password("New password: ")) == NULL
) {
1046 fprintf(stderr
, "Invalid password.\n");
1050 if ((key2
= get_password("Verify password: ")) == NULL
) {
1051 fprintf(stderr
, "Passwords do not match.\n");
1056 if (strcmp(key
, key2
) != 0) {
1057 fprintf(stderr
, "Passwords do not match.\n");
1065 if ((fd
= open(filename
, O_RDONLY
)) == -1) {
1067 warn("%s", filename
);
1071 if ((xmlbuf
= gcry_malloc(st
.st_size
+1)) == NULL
) {
1074 warnx("gcry_malloc() failed");
1078 read(fd
, xmlbuf
, st
.st_size
);
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");
1094 xmlDocDumpMemory(doc
, &xml
, &len
);
1096 gcry_md_hash_buffer(GCRY_MD_SHA256
, shakey
, key
, strlen(key
));
1099 if (do_xml_encrypt(NULL
, gh
, NULL
, xml
, len
, shakey
, iter
) == FALSE
) {
1100 memset(shakey
, 0, sizeof(shakey
));
1105 memset(shakey
, 0, sizeof(shakey
));
1110 gint
get_key_file_integer(const gchar
*section
, const gchar
*what
)
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
);
1119 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1120 g_clear_error(&gerror
);
1124 if (g_key_file_has_key(keyfileh
, "default", what
, NULL
) == TRUE
) {
1125 val
= g_key_file_get_integer(keyfileh
, "default", what
, &gerror
);
1128 log_write("%s(%i): %s", __FILE__
, __LINE__
, gerror
->message
);
1129 g_clear_error(&gerror
);
1137 int main(int argc
, char *argv
[])
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
;
1149 gchar
**cache_push
= NULL
;
1152 gchar
*import
= NULL
;
1153 gint default_timeout
;
1155 #ifdef HAVE_SETRLIMIT
1158 rl
.rlim_cur
= rl
.rlim_max
= 0;
1160 if (setrlimit(RLIMIT_CORE
, &rl
) != 0)
1161 err(EXIT_FAILURE
, "setrlimit()");
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().
1179 while ((opt
= getopt(argc
, argv
, "I:hvf:")) != EOF
) {
1186 rcfile
= g_strdup(optarg
);
1189 printf("%s\n%s\n", PACKAGE_STRING
, PACKAGE_BUGREPORT
);
1197 if ((keyfileh
= parse_rcfile(rcfile
)) == NULL
)
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
);
1207 snprintf(buf
, sizeof(buf
), "%s%s", g_get_home_dir(), p
--);
1209 socketarg
= g_strdup(buf
);
1214 if ((p
= g_key_file_get_string(keyfileh
, "default", "data_directory", NULL
)) == NULL
)
1215 errx(EXIT_FAILURE
, "%s: data_directory not defined", rcfile
);
1219 snprintf(buf
, sizeof(buf
), "%s%s", g_get_home_dir(), p
--);
1221 datadir
= g_strdup(buf
);
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
);
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
);
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
);
1251 p
= g_key_file_get_string(keyfileh
, "default", "log_path", NULL
);
1255 snprintf(buf
, sizeof(buf
), "%s%s", g_get_home_dir(), p
--);
1257 logfile
= g_strdup(buf
);
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
);
1274 socketname
= g_strdup(strrchr(socketarg
, '/'));
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()");
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
,
1306 err(EXIT_FAILURE
, "mmap()");
1312 if (mlock(shm_data
, cache_size
) == -1)
1315 memset(shm_data
, 0, cache_size
);
1319 opt
= xml_import(import
, iter
);
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
));
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
);
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
)) {
1356 err(EXIT_FAILURE
, "%s", datadir
);
1362 * Set the cache entry for a file. Prompts for the password.
1367 gint timeout
= default_timeout
;
1369 for (opt
= 0; cache_push
[opt
]; opt
++) {
1370 p
= cache_push
[opt
];
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
) {
1385 err(EXIT_FAILURE
, "%s", p
);
1392 if (get_input(p
, key
) == FALSE
) {
1398 if (cache_add_file(md5file
, key
) == FALSE
) {
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");
1410 g_strfreev(cache_push
);
1411 fprintf(stderr
, "Done! Daemonizing...\n");
1414 //g_key_file_free(kf);
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
);
1433 log_write("%s starting: %li slots available", PACKAGE_STRING
, cache_size
/ sizeof(file_cache_t
));
1436 socklen_t slen
= sizeof(struct sockaddr_un
);
1437 struct sockaddr_un raddr
;
1440 if ((fd
= accept(sfd
, (struct sockaddr_un
*)&raddr
, &slen
)) == -1) {
1441 if (errno
== EAGAIN
)
1445 log_write("accept(): %s", strerror(errno
));
1450 switch ((pid
= fork())) {
1452 log_write("fork(): %s", strerror(errno
));
1461 log_write("new connection: fd=%i, pid=%i", fd
, pid
);
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
));
1475 log_write("pwmd exiting normally");