From b9807541c77782f4543090bd6a53fe3c577791c7 Mon Sep 17 00:00:00 2001 From: Julien BLACHE Date: Fri, 12 Dec 2008 15:51:15 +0000 Subject: [PATCH] Add a data_portrange configuration file option to saned. --- ChangeLog | 9 ++- backend/saned.conf.in | 26 +++++-- doc/saned.man | 41 ++++++++--- frontend/saned.c | 167 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 211 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index 4888b0811..16df4d143 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,14 @@ +2008-12-12 Julien Blache + * frontend/saned.c: add a data_portrange config file option to + saned to specify a port range for the data connection. Based on a + patch contributed by Oren Held. + * backend/saned.conf.in: add the data_portrange option to the + config file and rework the comments. + * doc/saned.man: document the data_portrange option. + 2008-12-11 Stéphane Voltz * backend/rts8891.c doc/sane-rts8891.man doc/descriptions/rts8891.desc: scan register setting fix, documentation update - 2008-12-10 m. allan noah * backend/fujitsu.[ch]: backend v85 diff --git a/backend/saned.conf.in b/backend/saned.conf.in index e6312e473..ea8cc47d4 100644 --- a/backend/saned.conf.in +++ b/backend/saned.conf.in @@ -1,21 +1,31 @@ -# # saned.conf +# Configuration for the saned daemon + +## Daemon options +# Port range for the data connection. Choose a range inside [1024 - 65535]. +# Avoid specifying too large a range, for performance reasons. # -# The contents of the saned.conf file is a list of host names, IP -# addresses or IP subnets (CIDR notation) that are permitted to use local -# SANE devices. IPv6 addresses must be enclosed in brackets, and should -# always be specified in their compressed form. +# ONLY use this if your saned server is sitting behind a firewall. If your +# firewall is a Linux machine, we strongly recommend using the +# Netfilter nf_conntrack_sane connection tracking module instead. +# +# data_portrange = 10000 - 10100 + + +## Access list +# A list of host names, IP addresses or IP subnets (CIDR notation) that +# are permitted to use local SANE devices. IPv6 addresses must be enclosed +# in brackets, and should always be specified in their compressed form. # # The hostname matching is not case-sensitive. -# + #scan-client.somedomain.firm #192.168.0.1 #192.168.0.1/29 #[2001:7a8:185e::42:12] #[2001:7a8:185e::42:12]/64 -# + # NOTE: /etc/inetd.conf (or /etc/xinetd.conf) and # /etc/services must also be properly configured to start # the saned daemon as documented in saned(8), services(4) # and inetd.conf(4) (or xinetd.conf(5)). - diff --git a/doc/saned.man b/doc/saned.man index 4dbfd7188..761cf402c 100644 --- a/doc/saned.man +++ b/doc/saned.man @@ -1,4 +1,4 @@ -.TH saned 8 "14 Jul 2008" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" +.TH saned 8 "12 Dec 2008" "@PACKAGEVERSION@" "SANE Scanner Access Now Easy" .IX saned .SH NAME saned \- SANE network daemon @@ -76,19 +76,38 @@ install .B saned as setuid root. .PP -The contents of the +The .I saned.conf -file is a list of host names, IP addresses or IP subnets (CIDR notation) that -are permitted to use local SANE devices. IPv6 addresses must be enclosed in -brackets, and should always be specified in their compressed form. -Connections from localhost are always permitted. -Empty lines and lines starting with a hash mark (#) are ignored. A line -containing the single character ``+'' is interpreted to match any hostname. -This allows any remote machine to use your scanner and may present a security -risk, so this shouldn't be used unless you know what you're doing. A sample -configuration file is shown below: +configuration file contains both options for the daemon and the access +list. +.TP +\fBdata_portrange\fP = \fImin_port\fP - \fImax_port\fP +Specify the port range to use for the data connection. Pick a port +range between 1024 and 65535; don't pick a too large port range, as it +may have performance issues. Use this option if your \fBsaned\fP +server is sitting behind a firewall. If that firewall is a Linux +machine, we strongly recommend using the Netfilter +\fInf_conntrack_sane\fP module instead. +.PP +The access list is a list of host names, IP addresses or IP subnets +(CIDR notation) that are permitted to use local SANE devices. IPv6 +addresses must be enclosed in brackets, and should always be specified +in their compressed form. Connections from localhost are always +permitted. Empty lines and lines starting with a hash mark (#) are +ignored. A line containing the single character ``+'' is interpreted +to match any hostname. This allows any remote machine to use your +scanner and may present a security risk, so this shouldn't be used +unless you know what you're doing. +.PP +A sample configuration file is shown below: .PP .RS +# Daemon options +.br +data_portrange = 10000 - 10100 +.br +# Access list +.br scan\-client.somedomain.firm .br # this is a comment diff --git a/frontend/saned.c b/frontend/saned.c index 31b0b6dec..345bbdf98 100644 --- a/frontend/saned.c +++ b/frontend/saned.c @@ -252,6 +252,10 @@ byte_order; static const char *default_username = "saned-user"; static char *remote_ip; +/* data port range */ +static in_port_t data_port_lo; +static in_port_t data_port_hi; + #ifdef SANED_USES_AF_INDEP static struct sockaddr_storage remote_address; static int remote_address_len; @@ -925,10 +929,13 @@ check_host (int fd) { config_line = config_line_buf; /* from now on, use a pointer */ DBG (DBG_DBG, "check_host: config file line: `%s'\n", config_line); - if (config_line[0] == '#') /* ignore line comments */ - continue; + if (config_line[0] == '#') + continue; /* ignore comments */ + + if (strchr (config_line, '=')) + continue; /* ignore lines with an = sign */ + len = strlen (config_line); - if (!len) continue; /* ignore empty lines */ @@ -1214,13 +1221,16 @@ check_host (int fd) { config_line = config_line_buf; /* from now on, use a pointer */ DBG (DBG_DBG, "check_host: config file line: `%s'\n", config_line); - if (config_line[0] == '#') /* ignore line comments */ - continue; + if (config_line[0] == '#') + continue; /* ignore comments */ + + if (strchr (config_line, '=')) + continue; /* ignore lines with an = sign */ + len = strlen (config_line); - if (!len) continue; /* ignore empty lines */ - + /* look for a subnet specification */ netmask = strchr (config_line, '/'); if (netmask != NULL) @@ -1385,6 +1395,8 @@ start_scan (Wire * w, int h, SANE_Start_Reply * reply) #endif /* ENABLE_IPV6 */ SANE_Handle be_handle; int fd, len; + in_port_t data_port; + int ret; be_handle = handle[h].handle; @@ -1410,19 +1422,41 @@ start_scan (Wire * w, int h, SANE_Start_Reply * reply) { case AF_INET: sin = (struct sockaddr_in *) &ss; - sin->sin_port = 0; break; #ifdef ENABLE_IPV6 case AF_INET6: sin6 = (struct sockaddr_in6 *) &ss; - sin6->sin6_port = 0; break; #endif /* ENABLE_IPV6 */ default: break; } - if (bind (fd, (struct sockaddr *) &ss, len) < 0) + /* Try to bind a port between data_port_lo and data_port_hi for the data connection */ + for (data_port = data_port_lo; data_port <= data_port_hi; data_port++) + { + switch (SS_FAMILY(ss)) + { + case AF_INET: + sin->sin_port = htons(data_port); + break; +#ifdef ENABLE_IPV6 + case AF_INET6: + sin6->sin6_port = htons(data_port); + break; +#endif /* ENABLE_IPV6 */ + default: + break; + } + + DBG (DBG_INFO, "start_scan: trying to bind data port %d\n", data_port); + + ret = bind (fd, (struct sockaddr *) &ss, len); + if (ret == 0) + break; + } + + if (ret < 0) { DBG (DBG_ERR, "start_scan: failed to bind address (%s)\n", strerror (errno)); @@ -1482,6 +1516,8 @@ start_scan (Wire * w, int h, SANE_Start_Reply * reply) struct sockaddr_in sin; SANE_Handle be_handle; int fd, len; + in_port_t data_port; + int ret; be_handle = handle[h].handle; @@ -1503,8 +1539,19 @@ start_scan (Wire * w, int h, SANE_Start_Reply * reply) return -1; } - sin.sin_port = 0; - if (bind (fd, (struct sockaddr *) &sin, len) < 0) + /* Try to bind a port between data_port_lo and data_port_hi for the data connection */ + for (data_port = data_port_lo; data_port <= data_port_hi; data_port++) + { + sin.sin_port = htons(data_port); + + DBG(DBG_INFO, "start_scan: trying to bind data port %d\n", data_port); + + ret = bind (fd, (struct sockaddr *) &sin, len); + if (ret == 0) + break; + } + + if (ret < 0) { DBG (DBG_ERR, "start_scan: failed to bind address (%s)\n", strerror (errno)); @@ -2510,6 +2557,100 @@ saned_avahi_callback (AvahiClient *c, AvahiClientState state, void *userdata) #endif /* WITH_AVAHI */ +static void +read_config (void) +{ + char config_line[PATH_MAX]; + const char *optval; + char *endval; + long val; + FILE *fp; + int len; + + DBG (DBG_INFO, "read_config: searching for config file\n"); + fp = sanei_config_open (SANED_CONFIG_FILE); + if (fp) + { + while (sanei_config_read (config_line, sizeof (config_line), fp)) + { + if (config_line[0] == '#') + continue; /* ignore line comments */ + + optval = strchr (config_line, '='); + if (optval == NULL) + continue; /* only interested in options, skip hosts */ + + len = strlen (config_line); + if (!len) + continue; /* ignore empty lines */ + + /* + * Check for saned options. + * Anything that isn't an option is a client. + */ + if (strstr(config_line, "data_portrange") != NULL) + { + optval = sanei_config_skip_whitespace (++optval); + if ((optval != NULL) && (*optval != '\0')) + { + val = strtol (optval, &endval, 10); + if (optval == endval) + { + DBG (DBG_ERR, "read_config: invalid value for data_portrange\n"); + continue; + } + else if ((val < 0) || (val > 65535)) + { + DBG (DBG_ERR, "read_config: data_portrange start port is invalid\n"); + continue; + } + + optval = strchr (endval, '-'); + if (optval == NULL) + { + DBG (DBG_ERR, "read_config: no end port value for data_portrange\n"); + continue; + } + + optval = sanei_config_skip_whitespace (++optval); + + data_port_lo = val; + + val = strtol (optval, &endval, 10); + if (optval == endval) + { + DBG (DBG_ERR, "read_config: invalid value for data_portrange\n"); + data_port_lo = 0; + continue; + } + else if ((val < 0) || (val > 65535)) + { + DBG (DBG_ERR, "read_config: data_portrange end port is invalid\n"); + data_port_lo = 0; + continue; + } + else if (val < data_port_lo) + { + DBG (DBG_ERR, "read_config: data_portrange end port is less than start port\n"); + data_port_lo = 0; + continue; + } + + data_port_hi = val; + + DBG (DBG_INFO, "read_config: data port range: %d - %d\n", data_port_lo, data_port_hi); + } + } + } + fclose (fp); + DBG (DBG_INFO, "read_config: done reading config\n"); + } + else + DBG (DBG_ERR, "read_config: could not open config file (%s): %s\n", + SANED_CONFIG_FILE, strerror (errno)); +} + + #ifdef SANED_USES_AF_INDEP static void do_bindings (int *nfds, struct pollfd **fds) @@ -2998,6 +3139,8 @@ main (int argc, char *argv[]) if (log_to_syslog) openlog ("saned", LOG_PID | LOG_CONS, LOG_DAEMON); + read_config (); + byte_order.w = 0; byte_order.ch = 1;