summaryrefslogtreecommitdiff
path: root/frontend/saned.c
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/saned.c')
-rw-r--r--frontend/saned.c169
1 files changed, 119 insertions, 50 deletions
diff --git a/frontend/saned.c b/frontend/saned.c
index 108512d..3bb99bb 100644
--- a/frontend/saned.c
+++ b/frontend/saned.c
@@ -82,6 +82,8 @@
#include <pwd.h>
#include <grp.h>
+#include "lgetopt.h"
+
#if defined(HAVE_SYS_POLL_H) && defined(HAVE_POLL)
# include <sys/poll.h>
#else
@@ -196,16 +198,19 @@ static AvahiEntryGroup *avahi_group = NULL;
#endif
#ifdef ENABLE_IPV6
-# define SANE_IN6_IS_ADDR_LOOPBACK(a) \
+# ifndef IN6_IS_ADDR_LOOPBACK
+# define IN6_IS_ADDR_LOOPBACK(a) \
(((const uint32_t *) (a))[0] == 0 \
&& ((const uint32_t *) (a))[1] == 0 \
&& ((const uint32_t *) (a))[2] == 0 \
&& ((const uint32_t *) (a))[3] == htonl (1))
-
-#define SANE_IN6_IS_ADDR_V4MAPPED(a) \
+# endif
+# ifndef IN6_IS_ADDR_V4MAPPED
+# define IN6_IS_ADDR_V4MAPPED(a) \
((((const uint32_t *) (a))[0] == 0) \
&& (((const uint32_t *) (a))[1] == 0) \
&& (((const uint32_t *) (a))[2] == htonl (0xffff)))
+# endif
#endif /* ENABLE_IPV6 */
#ifndef MAXHOSTNAMELEN
@@ -247,6 +252,7 @@ static int num_handles;
static int debug;
static int run_mode;
static Handle *handle;
+static char *bind_addr;
static union
{
int w;
@@ -786,7 +792,7 @@ check_host (int fd)
#ifdef ENABLE_IPV6
sin6 = &remote_address.sin6;
- if (SANE_IN6_IS_ADDR_V4MAPPED (sin6->sin6_addr.s6_addr))
+ if (IN6_IS_ADDR_V4MAPPED ((struct in6_addr *)sin6->sin6_addr.s6_addr))
{
DBG (DBG_DBG, "check_host: detected an IPv4-mapped address\n");
remote_ipv4 = remote_ip + 7;
@@ -843,7 +849,7 @@ check_host (int fd)
break;
#ifdef ENABLE_IPV6
case AF_INET6:
- if (SANE_IN6_IS_ADDR_LOOPBACK (sin6->sin6_addr.s6_addr))
+ if (IN6_IS_ADDR_LOOPBACK ((struct in6_addr *)sin6->sin6_addr.s6_addr))
{
DBG (DBG_MSG,
"check_host: remote host is IN6_LOOPBACK: access granted\n");
@@ -1428,7 +1434,7 @@ start_scan (Wire * w, int h, SANE_Start_Reply * reply)
SANE_Handle be_handle;
int fd, len;
in_port_t data_port;
- int ret;
+ int ret = -1;
be_handle = handle[h].handle;
@@ -1986,6 +1992,38 @@ process_request (Wire * w)
return 1;
}
+ /* Addresses CVE-2017-6318 (#315576, Debian BTS #853804) */
+ /* This is done here (rather than in sanei/sanei_wire.c where
+ * it should be done) to minimize scope of impact and amount
+ * of code change.
+ */
+ if (w->direction == WIRE_DECODE
+ && req.value_type == SANE_TYPE_STRING
+ && req.action == SANE_ACTION_GET_VALUE)
+ {
+ if (req.value)
+ {
+ /* FIXME: If req.value contains embedded NUL
+ * characters, this is wrong but we do not have
+ * access to the amount of memory allocated in
+ * sanei/sanei_wire.c at this point.
+ */
+ w->allocated_memory -= (1 + strlen (req.value));
+ free (req.value);
+ }
+ req.value = malloc (req.value_size);
+ if (!req.value)
+ {
+ w->status = ENOMEM;
+ DBG (DBG_ERR,
+ "process_request: (control_option) "
+ "h=%d (%s)\n", req.handle, strerror (w->status));
+ return 1;
+ }
+ memset (req.value, 0, req.value_size);
+ w->allocated_memory += req.value_size;
+ }
+
can_authorize = 1;
memset (&reply, 0, sizeof (reply)); /* avoid leaking bits */
@@ -2807,13 +2845,13 @@ do_bindings (int *nfds, struct pollfd **fds)
hints.ai_flags = AI_PASSIVE;
hints.ai_socktype = SOCK_STREAM;
- err = getaddrinfo (NULL, SANED_SERVICE_NAME, &hints, &res);
+ err = getaddrinfo (bind_addr, SANED_SERVICE_NAME, &hints, &res);
if (err)
{
DBG (DBG_WARN, "do_bindings: \" %s \" service unknown on your host; you should add\n", SANED_SERVICE_NAME);
DBG (DBG_WARN, "do_bindings: %s %d/tcp saned # SANE network scanner daemon\n", SANED_SERVICE_NAME, SANED_SERVICE_PORT);
DBG (DBG_WARN, "do_bindings: to your /etc/services file (or equivalent). Proceeding anyway.\n");
- err = getaddrinfo (NULL, SANED_SERVICE_PORT_S, &hints, &res);
+ err = getaddrinfo (bind_addr, SANED_SERVICE_PORT_S, &hints, &res);
if (err)
{
DBG (DBG_ERR, "do_bindings: getaddrinfo() failed even with numeric port: %s\n", gai_strerror (err));
@@ -2891,7 +2929,10 @@ do_bindings (int *nfds, struct pollfd **fds)
memset (&sin, 0, sizeof (sin));
sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = INADDR_ANY;
+ if(bind_addr)
+ sin.sin_addr.s_addr = inet_addr(bind_addr);
+ else
+ sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = port;
DBG (DBG_DBG, "do_bindings: socket ()\n");
@@ -2923,7 +2964,7 @@ do_bindings (int *nfds, struct pollfd **fds)
static void
-run_standalone (int argc, char **argv)
+run_standalone (char *user)
{
struct pollfd *fds = NULL;
struct pollfd *fdp = NULL;
@@ -2944,13 +2985,13 @@ run_standalone (int argc, char **argv)
if (run_mode != SANED_RUN_DEBUG)
{
- if (argc > 2)
+ if (user)
{
- pwent = getpwnam(argv[2]);
+ pwent = getpwnam(user);
if (pwent == NULL)
{
- DBG (DBG_ERR, "FATAL ERROR: user %s not found on system\n", argv[2]);
+ DBG (DBG_ERR, "FATAL ERROR: user %s not found on system\n", user);
bail_out (1);
}
@@ -2981,7 +3022,7 @@ run_standalone (int argc, char **argv)
while (grp->gr_mem[i])
{
- if (strcmp(grp->gr_mem[i], argv[2]) == 0)
+ if (strcmp(grp->gr_mem[i], user) == 0)
{
int need_to_add = 1, j;
@@ -3172,7 +3213,7 @@ run_standalone (int argc, char **argv)
static void
-run_inetd (int argc, char **argv)
+run_inetd (char __sane_unused__ *sock)
{
int fd = -1;
@@ -3238,18 +3279,13 @@ run_inetd (int argc, char **argv)
close (dave_null);
}
-#ifndef HAVE_OS2_H
- /* Unused in this function */
- argc = argc;
- argv = argv;
-
-#else
+#ifdef HAVE_OS2_H
/* under OS/2, the socket handle is passed as argument on the command
line; the socket handle is relative to IBM TCP/IP, so a call
to impsockethandle() is required to add it to the EMX runtime */
- if (argc == 2)
+ if (sock)
{
- fd = _impsockhandle (atoi (argv[1]), 0);
+ fd = _impsockhandle (atoi (sock), 0);
if (fd == -1)
perror ("impsockhandle");
}
@@ -3258,11 +3294,44 @@ run_inetd (int argc, char **argv)
handle_connection(fd);
}
+static void usage(char *me, int err)
+{
+ fprintf (stderr,
+ "Usage: %s [OPTIONS]\n\n"
+ " Options:\n\n"
+ " -a, --alone[=user] run standalone and fork in background as `user'\n"
+ " -d, --debug[=level] run foreground with output to stdout\n"
+ " and debug level `level' (default is 2)\n"
+ " -s, --syslog[=level] run foreground with output to syslog\n"
+ " and debug level `level' (default is 2)\n"
+ " -b, --bind=addr bind address `addr'\n"
+ " -h, --help show this help message and exit\n", me);
+
+ exit(err);
+}
+
+static int debug;
+
+static struct option long_options[] =
+{
+/* These options set a flag. */
+ {"help", no_argument, 0, 'h'},
+ {"alone", optional_argument, 0, 'a'},
+ {"debug", optional_argument, 0, 'd'},
+ {"syslog", optional_argument, 0, 's'},
+ {"bind", required_argument, 0, 'b'},
+ {0, 0, 0, 0 }
+};
int
main (int argc, char *argv[])
{
char options[64] = "";
+ char *user = NULL;
+ char *sock = NULL;
+ int c;
+ int long_index = 0;
+
debug = DBG_WARN;
prog_name = strrchr (argv[0], '/');
@@ -3274,34 +3343,30 @@ main (int argc, char *argv[])
numchildren = 0;
run_mode = SANED_RUN_INETD;
- if (argc >= 2)
+ while((c = getopt_long(argc, argv,"ha::d::s::b:", long_options, &long_index )) != -1)
{
- if (strncmp (argv[1], "-a", 2) == 0)
+ switch(c) {
+ case 'a':
run_mode = SANED_RUN_ALONE;
- else if (strncmp (argv[1], "-d", 2) == 0)
- {
- run_mode = SANED_RUN_DEBUG;
- log_to_syslog = SANE_FALSE;
- }
- else if (strncmp (argv[1], "-s", 2) == 0)
+ user = optarg;
+ break;
+ case 'd':
+ log_to_syslog = SANE_FALSE;
+ case 's':
run_mode = SANED_RUN_DEBUG;
- else
- {
- printf ("Usage: saned [ -a [ username ] | -d [ n ] | -s [ n ] ] | -h\n");
- if ((strncmp (argv[1], "-h", 2) == 0) ||
- (strncmp (argv[1], "--help", 6) == 0))
- exit (EXIT_SUCCESS);
- else
- exit (EXIT_FAILURE);
- }
- }
-
- if (run_mode == SANED_RUN_DEBUG)
- {
- if (argv[1][2])
- debug = atoi (argv[1] + 2);
-
- DBG (DBG_WARN, "main: starting debug mode (level %d)\n", debug);
+ if(optarg)
+ debug = atoi(optarg);
+ break;
+ case 'b':
+ bind_addr = optarg;
+ break;
+ case 'h':
+ usage(argv[0], EXIT_SUCCESS);
+ break;
+ default:
+ usage(argv[0], EXIT_FAILURE);
+ break;
+ }
}
if (log_to_syslog)
@@ -3342,11 +3407,15 @@ main (int argc, char *argv[])
if ((run_mode == SANED_RUN_ALONE) || (run_mode == SANED_RUN_DEBUG))
{
- run_standalone(argc, argv);
+ run_standalone(user);
}
else
{
- run_inetd(argc, argv);
+#ifdef HAVE_OS2_H
+ if (argc == 2)
+ sock = argv[1];
+#endif
+ run_inetd(sock);
}
DBG (DBG_WARN, "saned exiting\n");