kopia lustrzana https://gitlab.com/sane-project/backends
* frontend/saned.c: added subnet support to saned ; added
check_v4_in_range and check_v6_in_range functions. * backend/saned.conf: updated the config file. * doc/saned.man: updated the manpage for saned.merge-requests/1/head
rodzic
e37c7b9007
commit
9695362397
|
@ -1,3 +1,9 @@
|
|||
2003-10-19 Julien Blache <jb@jblache.org>
|
||||
* frontend/saned.c: added subnet support to saned ; added
|
||||
check_v4_in_range and check_v6_in_range functions.
|
||||
* backend/saned.conf: updated the config file.
|
||||
* doc/saned.man: updated the manpage for saned.
|
||||
|
||||
2003-10-17 Henning Meier-Geinitz <henning@meier-geinitz.de>
|
||||
|
||||
* doc/descriptions/unsupported.desc: Added Reflecta iScan
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
#
|
||||
# saned.conf
|
||||
#
|
||||
# The contents of the saned.conf file is a list of host
|
||||
# names or IP addresses that are permitted by saned to
|
||||
# use local SANE devices in a networked configuration.
|
||||
# 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.
|
||||
#
|
||||
# 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
|
||||
|
|
|
@ -54,8 +54,10 @@ as setuid root.
|
|||
.PP
|
||||
The contents of the
|
||||
.I saned.conf
|
||||
file is a list of host names or IP addresses that are permitted to
|
||||
use local SANE devices. Connections from localhost are always permitted.
|
||||
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
|
||||
|
@ -69,12 +71,15 @@ scan-client.somedomain.firm
|
|||
.br
|
||||
192.168.0.1
|
||||
.br
|
||||
::1
|
||||
192.168.2.12/29
|
||||
.br
|
||||
[::1]
|
||||
.br
|
||||
[2001:7a8:185e::42:12]/64
|
||||
.RE
|
||||
.PP
|
||||
The case of the host names does not matter, so AHost.COM is considered
|
||||
identical to ahost.com. IPv6 addresses should always be specified in their
|
||||
compressed form.
|
||||
identical to ahost.com.
|
||||
|
||||
For
|
||||
.B saned
|
||||
|
|
314
frontend/saned.c
314
frontend/saned.c
|
@ -467,6 +467,181 @@ decode_handle (Wire * w, const char *op)
|
|||
return h;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Convert a number of bits to an 8-bit bitmask */
|
||||
static unsigned int cidrtomask4[9] = { 0x00, 0x80, 0xC0, 0xE0, 0xF0,
|
||||
0xF8, 0xFC, 0xFE, 0xFF };
|
||||
|
||||
#ifdef SANED_USES_AF_INDEP
|
||||
static SANE_Bool
|
||||
check_v4_in_range (struct sockaddr_in *sin, char *base_ip, char *netmask)
|
||||
{
|
||||
int cidr;
|
||||
int i, err;
|
||||
char *end;
|
||||
in_addr_t mask;
|
||||
struct sockaddr_in *base;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res;
|
||||
SANE_Bool ret = SANE_FALSE;
|
||||
|
||||
cidr = -1;
|
||||
cidr = strtol (netmask, &end, 10);
|
||||
|
||||
/* sanity check the cidr value */
|
||||
if ((cidr < 0) || (cidr > 32) || (end == netmask))
|
||||
{
|
||||
DBG (DBG_ERR, "check_v4_in_range: invalid CIDR value (%s) !\n", netmask);
|
||||
return SANE_FALSE;
|
||||
}
|
||||
|
||||
mask = 0;
|
||||
cidr -= 8;
|
||||
|
||||
for (i = 3; cidr >= 0; i--)
|
||||
{
|
||||
mask |= (htonl (0xff) << (8 * i));
|
||||
cidr -= 8;
|
||||
}
|
||||
|
||||
if (cidr < 0)
|
||||
mask |= (htonl (cidrtomask4[cidr + 8]) << (8 * i));
|
||||
|
||||
/* get a sockaddr_in representing the base IP address */
|
||||
memset (&hints, 0, sizeof (struct addrinfo));
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = PF_INET;
|
||||
|
||||
err = getaddrinfo (base_ip, NULL, &hints, &res);
|
||||
if (err)
|
||||
{
|
||||
DBG (DBG_DBG, "check_v4_in_range: getaddrinfo() failed: %s\n", gai_strerror (err));
|
||||
return SANE_FALSE;
|
||||
}
|
||||
|
||||
base = (struct sockaddr_in *)res->ai_addr;
|
||||
|
||||
if ((base->sin_addr.s_addr & mask) == (sin->sin_addr.s_addr & mask))
|
||||
ret = SANE_TRUE;
|
||||
|
||||
freeaddrinfo (res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
# ifdef ENABLE_IPV6
|
||||
/* Convert a number of bits to a 16-bit bitmask */
|
||||
static unsigned int cidrtomask6[17] = { 0x0000, 0x8000, 0xC000, 0xE000,
|
||||
0xF000, 0xF800, 0xFC00, 0xFE00,
|
||||
0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
|
||||
0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE,
|
||||
0xFFFF };
|
||||
|
||||
static SANE_Bool
|
||||
check_v6_in_range (struct sockaddr_in6 *sin6, char *base_ip, char *netmask)
|
||||
{
|
||||
int cidr;
|
||||
int i, err;
|
||||
unsigned int mask[8];
|
||||
char *end;
|
||||
struct sockaddr_in6 *base;
|
||||
struct addrinfo hints;
|
||||
struct addrinfo *res;
|
||||
SANE_Bool ret = SANE_TRUE;
|
||||
|
||||
cidr = -1;
|
||||
cidr = strtol (netmask, &end, 10);
|
||||
|
||||
/* sanity check the cidr value */
|
||||
if ((cidr < 0) || (cidr > 128) || (end == netmask))
|
||||
{
|
||||
DBG (DBG_ERR, "check_v6_in_range: invalid CIDR value (%s) !\n", netmask);
|
||||
return SANE_FALSE;
|
||||
}
|
||||
|
||||
memset (mask, 0, (8 * sizeof (unsigned int)));
|
||||
cidr -= 8;
|
||||
|
||||
for (i = 0; cidr >= 0; i++)
|
||||
{
|
||||
mask[i] = htonl (0xffff);
|
||||
cidr -= 16;
|
||||
}
|
||||
|
||||
if (cidr < 0)
|
||||
mask[i] = htonl (cidrtomask6[cidr + 16]);
|
||||
|
||||
/* get a sockaddr_in6 representing the base IP address */
|
||||
memset (&hints, 0, sizeof (struct addrinfo));
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = PF_INET6;
|
||||
|
||||
err = getaddrinfo (base_ip, NULL, &hints, &res);
|
||||
if (err)
|
||||
{
|
||||
DBG (DBG_DBG, "check_v6_in_range: getaddrinfo() failed: %s\n", gai_strerror (err));
|
||||
return SANE_FALSE;
|
||||
}
|
||||
|
||||
base = (struct sockaddr_in6 *)res->ai_addr;
|
||||
|
||||
for (i = 0; i < 8; i++)
|
||||
{
|
||||
if ((base->sin6_addr.s6_addr16[i] & mask[i]) != (sin6->sin6_addr.s6_addr16[i] & mask[i]))
|
||||
{
|
||||
ret = SANE_FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo (res);
|
||||
|
||||
return ret;
|
||||
}
|
||||
# endif /* ENABLE_IPV6 */
|
||||
#else /* !SANED_USES_AF_INDEP */
|
||||
static SANE_Bool
|
||||
check_v4_in_range (struct in_addr *inaddr, struct in_addr *base, char *netmask)
|
||||
{
|
||||
int cidr;
|
||||
int i;
|
||||
char *end;
|
||||
in_addr_t mask;
|
||||
SANE_Bool ret = SANE_FALSE;
|
||||
|
||||
cidr = -1;
|
||||
cidr = strtol (netmask, &end, 10);
|
||||
|
||||
/* sanity check the cidr value */
|
||||
if ((cidr < 0) || (cidr > 32) || (end == netmask))
|
||||
{
|
||||
DBG (DBG_ERR, "check_v4_in_range: invalid CIDR value (%s) !\n", netmask);
|
||||
return SANE_FALSE;
|
||||
}
|
||||
|
||||
mask = 0;
|
||||
cidr -= 8;
|
||||
|
||||
for (i = 3; cidr >= 0; i--)
|
||||
{
|
||||
mask |= (htonl (0xff) << (8 * i));
|
||||
cidr -= 8;
|
||||
}
|
||||
|
||||
if (cidr < 0)
|
||||
mask |= (htonl (cidrtomask4[cidr + 8]) << (8 * i));
|
||||
|
||||
if ((base->s_addr & mask) == (inaddr->s_addr & mask))
|
||||
ret = SANE_TRUE;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* SANED_USES_AF_INDEP */
|
||||
|
||||
|
||||
|
||||
/* Access control */
|
||||
#ifdef SANED_USES_AF_INDEP
|
||||
static SANE_Status
|
||||
|
@ -483,16 +658,16 @@ check_host (int fd)
|
|||
int err;
|
||||
char text_addr[64];
|
||||
#ifdef ENABLE_IPV6
|
||||
SANE_Bool IPv4map = SANE_FALSE;
|
||||
char *remote_ipv4 = NULL; /* in case we have an IPv4-mapped address (eg ::ffff:127.0.0.1) */
|
||||
char *tmp;
|
||||
struct addrinfo *remote_ipv4_addr = NULL;
|
||||
#endif /* ENABLE_IPV6 */
|
||||
char config_line[1024];
|
||||
char config_line_buf[1024];
|
||||
char *config_line;
|
||||
char *netmask;
|
||||
char hostname[MAXHOSTNAMELEN];
|
||||
|
||||
#ifdef ENABLE_IPV6
|
||||
SANE_Bool IPv4map = SANE_FALSE;
|
||||
#endif /* ENABLE_IPV6 */
|
||||
|
||||
int len;
|
||||
FILE *fp;
|
||||
|
||||
|
@ -641,12 +816,14 @@ check_host (int fd)
|
|||
DBG (DBG_MSG, "check_host: remote host has same addr as local: access granted\n");
|
||||
|
||||
freeaddrinfo (res);
|
||||
res = NULL;
|
||||
|
||||
return SANE_STATUS_GOOD;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo (res);
|
||||
res = NULL;
|
||||
|
||||
DBG (DBG_DBG,
|
||||
"check_host: remote host doesn't have same addr as local\n");
|
||||
|
@ -670,9 +847,10 @@ check_host (int fd)
|
|||
continue;
|
||||
}
|
||||
|
||||
while (!access_ok && sanei_config_read (config_line,
|
||||
sizeof (config_line), fp))
|
||||
while (!access_ok && sanei_config_read (config_line_buf,
|
||||
sizeof (config_line_buf), fp))
|
||||
{
|
||||
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;
|
||||
|
@ -681,6 +859,33 @@ check_host (int fd)
|
|||
if (!len)
|
||||
continue; /* ignore empty lines */
|
||||
|
||||
/* look for a subnet specification */
|
||||
netmask = strchr (config_line, '/');
|
||||
if (netmask != NULL)
|
||||
{
|
||||
*netmask = '\0';
|
||||
netmask++;
|
||||
DBG (DBG_DBG, "check_host: subnet with base IP = %s, CIDR netmask = %s\n",
|
||||
config_line, netmask);
|
||||
}
|
||||
|
||||
#ifdef ENABLE_IPV6
|
||||
/* IPv6 addresses are enclosed in [] */
|
||||
if (*config_line == '[')
|
||||
{
|
||||
config_line++;
|
||||
tmp = strchr (config_line, ']');
|
||||
if (tmp == NULL)
|
||||
{
|
||||
DBG (DBG_ERR,
|
||||
"check_host: malformed IPv6 address in config file, skipping: [%s\n",
|
||||
config_line);
|
||||
continue;
|
||||
}
|
||||
*tmp = '\0';
|
||||
}
|
||||
#endif /* ENABLE_IPV6 */
|
||||
|
||||
if (strcmp (config_line, "+") == 0)
|
||||
{
|
||||
access_ok = 1;
|
||||
|
@ -701,6 +906,72 @@ check_host (int fd)
|
|||
DBG (DBG_DBG,
|
||||
"check_host: access granted from IP address %s (IPv4-mapped)\n", remote_ip);
|
||||
}
|
||||
/* handle IP ranges, take care of the IPv4map stuff */
|
||||
else if (netmask != NULL)
|
||||
{
|
||||
if (strchr (config_line, ':') != NULL) /* is a v6 address */
|
||||
{
|
||||
if (SS_FAMILY(remote_address) == AF_INET6)
|
||||
{
|
||||
if (check_v6_in_range (sin6, config_line, netmask))
|
||||
{
|
||||
access_ok = 1;
|
||||
DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet [%s]/%s)\n",
|
||||
remote_ip, config_line, netmask);
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* is a v4 address */
|
||||
{
|
||||
if (IPv4map == SANE_TRUE)
|
||||
{
|
||||
/* get a sockaddr_in representing the v4-mapped IP address */
|
||||
memset (&hints, 0, sizeof (struct addrinfo));
|
||||
hints.ai_flags = AI_NUMERICHOST;
|
||||
hints.ai_family = PF_INET;
|
||||
|
||||
err = getaddrinfo (remote_ipv4, NULL, &hints, &res);
|
||||
if (err)
|
||||
DBG (DBG_DBG, "check_host: getaddrinfo() failed: %s\n", gai_strerror (err));
|
||||
else
|
||||
sin = (struct sockaddr_in *)res->ai_addr;
|
||||
}
|
||||
|
||||
if ((SS_FAMILY(remote_address) == AF_INET) ||
|
||||
(IPv4map == SANE_TRUE))
|
||||
{
|
||||
|
||||
if (check_v4_in_range (sin, config_line, netmask))
|
||||
{
|
||||
DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet %s/%s)\n",
|
||||
((IPv4map == SANE_TRUE) ? remote_ipv4 : remote_ip), config_line, netmask);
|
||||
access_ok = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* restore the old sin pointer */
|
||||
sin = (struct sockaddr_in *)&remote_address;
|
||||
}
|
||||
|
||||
if (res != NULL)
|
||||
{
|
||||
freeaddrinfo (res);
|
||||
res = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else /* !ENABLE_IPV6 */
|
||||
/* handle IP ranges */
|
||||
else if (netmask != NULL)
|
||||
{
|
||||
if (check_v4_in_range (sin, config_line, netmask))
|
||||
{
|
||||
access_ok = 1;
|
||||
DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet %s/%s)\n",
|
||||
remote_ip, config_line, netmask);
|
||||
}
|
||||
}
|
||||
#endif /* ENABLE_IPV6 */
|
||||
else
|
||||
{
|
||||
|
@ -747,6 +1018,7 @@ check_host (int fd)
|
|||
break;
|
||||
}
|
||||
freeaddrinfo (res);
|
||||
res = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -768,7 +1040,9 @@ check_host (int fd)
|
|||
int j, access_ok = 0;
|
||||
struct hostent *he;
|
||||
char text_addr[64];
|
||||
char config_line[1024];
|
||||
char config_line_buf[1024];
|
||||
char *config_line;
|
||||
char *netmask;
|
||||
char hostname[MAXHOSTNAMELEN];
|
||||
char *r_hostname;
|
||||
static struct in_addr config_line_address;
|
||||
|
@ -862,9 +1136,10 @@ check_host (int fd)
|
|||
continue;
|
||||
}
|
||||
|
||||
while (!access_ok && sanei_config_read (config_line,
|
||||
sizeof (config_line), fp))
|
||||
while (!access_ok && sanei_config_read (config_line_buf,
|
||||
sizeof (config_line_buf), fp))
|
||||
{
|
||||
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;
|
||||
|
@ -873,6 +1148,16 @@ check_host (int fd)
|
|||
if (!len)
|
||||
continue; /* ignore empty lines */
|
||||
|
||||
/* look for a subnet specification */
|
||||
netmask = strchr (config_line, '/');
|
||||
if (netmask != NULL)
|
||||
{
|
||||
*netmask = '\0';
|
||||
netmask++;
|
||||
DBG (DBG_DBG, "check_host: subnet with base IP = %s, CIDR netmask = %s\n",
|
||||
config_line, netmask);
|
||||
}
|
||||
|
||||
if (strcmp (config_line, "+") == 0)
|
||||
{
|
||||
access_ok = 1;
|
||||
|
@ -886,6 +1171,15 @@ check_host (int fd)
|
|||
if (memcmp (&remote_address.s_addr,
|
||||
&config_line_address.s_addr, 4) == 0)
|
||||
access_ok = 1;
|
||||
else if (netmask != NULL)
|
||||
{
|
||||
if (check_v4_in_range (&remote_address, &config_line_address, netmask))
|
||||
{
|
||||
access_ok = 1;
|
||||
DBG (DBG_DBG, "check_host: access granted from IP address %s (in subnet %s/%s)\n",
|
||||
remote_ip, config_line, netmask);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Ładowanie…
Reference in New Issue