summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarel Zak2007-03-10 01:28:10 +0100
committerKarel Zak2007-03-10 01:28:10 +0100
commitea6c190a661170d905ca63e5deae9f0a399f039e (patch)
tree79dd7423a3963e6373861e21c003b4b671e1507b
parentlogin: add regression test for IP address checking code (diff)
downloadkernel-qcow2-util-linux-ea6c190a661170d905ca63e5deae9f0a399f039e.tar.gz
kernel-qcow2-util-linux-ea6c190a661170d905ca63e5deae9f0a399f039e.tar.xz
kernel-qcow2-util-linux-ea6c190a661170d905ca63e5deae9f0a399f039e.zip
login: add IPv6 support
This support includes: * non-PAM version supports IPv6 ranges in /etc/usertty * utmp records with IPv6 addresses Based on patch by: Milan Zazrivec <mzazrivec@redhat.com> Signed-off-by: Karel Zak <kzak@redhat.com>
-rw-r--r--login-utils/checktty.c149
-rw-r--r--login-utils/login.17
-rw-r--r--login-utils/login.c37
-rw-r--r--login-utils/login.h3
-rw-r--r--tests/expected/ts-login-checktty3
5 files changed, 159 insertions, 40 deletions
diff --git a/login-utils/checktty.c b/login-utils/checktty.c
index f47b7ad4d..16f9f2e88 100644
--- a/login-utils/checktty.c
+++ b/login-utils/checktty.c
@@ -21,6 +21,7 @@
#include <malloc.h>
#include <netdb.h>
#include <sys/syslog.h>
+#include <ctype.h>
#include "nls.h"
#include <sys/sysmacros.h>
@@ -149,22 +150,20 @@ isapty(const char *tty)
return 0;
}
-/* match the hostname hn against the pattern pat */
+
+/* IPv4 -- pattern is x.x.x.x/y.y.y.y (net/mask)*/
static int
-hnmatch(const char *hn, const char *pat)
+hnmatch_ip4(const char *pat)
{
- int x1, x2, x3, x4, y1, y2, y3, y4;
- unsigned long p, mask, a;
- unsigned char *ha;
- int n, m;
+ int x1, x2, x3, x4, y1, y2, y3, y4;
+ unsigned long p, mask, a;
+ unsigned char *ha;
- if ((hn == NULL) && (strcmp(pat, "localhost") == 0)) return 1;
- if ((hn == NULL) || hn[0] == 0) return 0;
-
- if (pat[0] >= '0' && pat[0] <= '9') {
/* pattern is an IP QUAD address and a mask x.x.x.x/y.y.y.y */
- sscanf(pat, "%d.%d.%d.%d/%d.%d.%d.%d", &x1, &x2, &x3, &x4,
- &y1, &y2, &y3, &y4);
+ if (sscanf(pat, "%d.%d.%d.%d/%d.%d.%d.%d",
+ &x1, &x2, &x3, &x4, &y1, &y2, &y3, &y4) < 8)
+ return 0;
+
p = (((unsigned long)x1<<24)+((unsigned long)x2<<16)
+((unsigned long)x3<<8)+((unsigned long)x4));
mask = (((unsigned long)y1<<24)+((unsigned long)y2<<16)
@@ -177,18 +176,108 @@ hnmatch(const char *hn, const char *pat)
a = (((unsigned long)ha[0]<<24)+((unsigned long)ha[1]<<16)
+((unsigned long)ha[2]<<8)+((unsigned long)ha[3]));
return ((p & mask) == (a & mask));
- } else {
- /* pattern is a suffix of a FQDN */
- n = strlen(pat);
- m = strlen(hn);
- if (n > m) return 0;
- return (strcasecmp(pat, hn + m - n) == 0);
- }
+}
+
+/* IPv6 -- pattern is [hex:hex:....]/number ([net]/mask) */
+static int
+hnmatch_ip6(const char *pat)
+{
+ char *patnet;
+ char *patmask;
+ struct in6_addr addr;
+ struct addrinfo hints, *res;
+ struct sockaddr_in6 net;
+ int mask_len, i = 0;
+ char *p;
+
+ if (pat == NULL || *pat != '[')
+ return 0;
+
+ memcpy(&addr, hostaddress, sizeof(addr));
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+
+ patnet = strdup(pat);
+
+ /* match IPv6 address against [netnumber]/prefixlen */
+ if (!(p = strchr(patnet, ']')))
+ goto mismatch;
+
+ *p++ = '\0';
+
+ if (! (*p == '/' && isdigit((unsigned char) *(p + 1))))
+ goto mismatch;
+
+ patmask = p + 1;
+
+ /* prepare net address */
+ if (getaddrinfo(patnet + 1, NULL, &hints, &res) != 0)
+ goto mismatch;
+
+ memcpy(&net, res->ai_addr, sizeof(net));
+ freeaddrinfo(res);
+
+ /* convert mask to number */
+ if ((mask_len = atoi(patmask)) < 0 || mask_len > 128)
+ goto mismatch;
+
+ /* compare */
+ while (mask_len > 0) {
+ if (mask_len < 32) {
+ u_int32_t mask = htonl(~(0xffffffff >> mask_len));
+
+ if ((*(u_int32_t *)&addr.s6_addr[i] & mask) !=
+ (*(u_int32_t *)&net.sin6_addr.s6_addr[i] & mask))
+ goto mismatch;
+ break;
+ }
+ if (*(u_int32_t *)&addr.s6_addr[i] !=
+ *(u_int32_t *)&net.sin6_addr.s6_addr[i])
+ goto mismatch;
+ i += 4;
+ mask_len -= 32;
+ }
+
+ free(patnet);
+ return 1;
+
+mismatch:
+ free(patnet);
+ return 0;
+}
+
+/* match the hostname hn against the pattern pat */
+static int
+hnmatch(const char *hn, const char *pat)
+{
+
+ if ((hn == NULL) && (strcmp(pat, "localhost") == 0))
+ return 1;
+ if ((hn == NULL) || *hn == '\0')
+ return 0;
+
+ if (*pat >= '0' && *pat <= '9')
+ return hostfamily == AF_INET ? hnmatch_ip4(pat) : 0;
+ else if (*pat == '[')
+ return hostfamily == AF_INET6 ? hnmatch_ip6(pat) : 0;
+ else {
+ /* pattern is a suffix of a FQDN */
+ int n = strlen(pat),
+ m = strlen(hn);
+
+ if (n > m)
+ return 0;
+ return (strcasecmp(pat, hn + m - n) == 0);
+ }
}
#ifdef MAIN_TEST_CHECKTTY
-char hostaddress[4];
+char hostaddress[16];
+sa_family_t hostfamily;
char *hostname;
void sleepexit(int eval) {} /* dummy for this test */
void badlogin(const char *s) {} /* dummy for this test */
@@ -201,16 +290,19 @@ main(int argc, char **argv)
const char *range;
const char *ip;
} alist[] = {
- { "130.225.16.0/255.255.254.0", "130.225.16.1" },
- { "130.225.16.0/255.255.254.0", "10.20.30.1" },
- { "130.225.0.0/255.254.0.0", "130.225.16.1" },
- { "130.225.0.0/255.254.0.0", "130.225.17.1" },
- { "130.225.0.0/255.254.0.0", "150.160.170.180" },
+ { "130.225.16.0/255.255.254.0", "130.225.16.1" },
+ { "130.225.16.0/255.255.254.0", "10.20.30.1" },
+ { "130.225.0.0/255.254.0.0", "130.225.16.1" },
+ { "130.225.0.0/255.254.0.0", "130.225.17.1" },
+ { "130.225.0.0/255.254.0.0", "150.160.170.180" },
+ { "[3ffe:505:2:1::]/64", "3ffe:505:2:1::" },
+ { "[3ffe:505:2:1::]/64", "3ffe:505:2:2::" },
+ { "[3ffe:505:2:1::]/64", "3ffe:505:2:1:ffff:ffff::" },
{ NULL, NULL }
}, *item;
memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_INET;
+ hints.ai_family = AF_UNSPEC;
hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE | AI_ADDRCONFIG;
hints.ai_socktype = SOCK_STREAM;
@@ -225,14 +317,13 @@ main(int argc, char **argv)
memcpy(hostaddress, &(sa->sin_addr),
sizeof(sa->sin_addr));
}
-/***
- if (info->ai_family == AF_INET6) {
+ else if (info->ai_family == AF_INET6) {
struct sockaddr_in6 *sa =
(struct sockaddr_in6 *) info->ai_addr;
memcpy(hostaddress, &(sa->sin6_addr),
sizeof(sa->sin6_addr));
}
-***/
+ hostfamily = info->ai_family;
freeaddrinfo(info);
printf("%s\n", hnmatch("dummy", item->range) ?
"match" : "mismatch");
diff --git a/login-utils/login.1 b/login-utils/login.1
index 29590f946..f77ab0e57 100644
--- a/login-utils/login.1
+++ b/login-utils/login.1
@@ -256,6 +256,13 @@ to compare with the IP address of the remote host. For example
any host whose IP address is in the range 130.225.16.0 \-
130.225.17.255.
.PP
+.IP o
+An range of IPv6 addresses, written @[n:n:n:n:n:n:n:n]/m is interpreted as a
+[net]/prefixlen pair. An IPv6 host address is matched if prefixlen bits of
+net is equal to the prefixlen bits of the address. For example, the
+[net]/prefixlen pattern [3ffe:505:2:1::]/64 matches every address in the
+range 3ffe:505:2:1:: through 3ffe:505:2:1:ffff:ffff:ffff:ffff.
+.PP
Any of the above origins may be prefixed by a time specification
according to the syntax:
.PP
diff --git a/login-utils/login.c b/login-utils/login.c
index d5ec1fc09..e3b4f6fd0 100644
--- a/login-utils/login.c
+++ b/login-utils/login.c
@@ -172,7 +172,8 @@ struct passwd *pwd;
#ifdef HAVE_SECURITY_PAM_MISC_H
static struct passwd pwdcopy;
#endif
-char hostaddress[4]; /* used in checktty.c */
+char hostaddress[16]; /* used in checktty.c */
+sa_family_t hostfamily; /* used in checktty.c */
char *hostname; /* idem */
static char *username, *tty_name, *tty_number;
static char thishost[100];
@@ -283,7 +284,7 @@ logbtmp(const char *line, const char *username, const char *hostname) {
if (hostname) {
xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
if (hostaddress[0])
- memcpy(&ut.ut_addr, hostaddress, sizeof(ut.ut_addr));
+ memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
}
#if HAVE_UPDWTMP /* bad luck for ancient systems */
updwtmp(_PATH_BTMP, &ut);
@@ -391,13 +392,29 @@ main(int argc, char **argv)
hostname = strdup(optarg); /* strdup: Ambrose C. Li */
{
- struct hostent *he = gethostbyname(hostname);
-
- /* he points to static storage; copy the part we use */
- hostaddress[0] = 0;
- if (he && he->h_addr_list && he->h_addr_list[0])
- memcpy(hostaddress, he->h_addr_list[0],
- sizeof(hostaddress));
+ struct addrinfo hints, *info = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_ADDRCONFIG;
+
+ hostaddress[0] = 0;
+
+ if (getaddrinfo(hostname, NULL, &hints, &info)==0 && info) {
+ if (info->ai_family == AF_INET) {
+ struct sockaddr_in *sa =
+ (struct sockaddr_in *) info->ai_addr;
+ memcpy(hostaddress, &(sa->sin_addr),
+ sizeof(sa->sin_addr));
+ }
+ else if (info->ai_family == AF_INET6) {
+ struct sockaddr_in6 *sa =
+ (struct sockaddr_in6 *) info->ai_addr;
+ memcpy(hostaddress, &(sa->sin6_addr),
+ sizeof(sa->sin6_addr));
+ }
+ hostfamily = info->ai_family;
+ freeaddrinfo(info);
+ }
}
break;
@@ -892,7 +909,7 @@ Michael Riepe <michael@stud.uni-hannover.de>
if (hostname) {
xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
if (hostaddress[0])
- memcpy(&ut.ut_addr, hostaddress, sizeof(ut.ut_addr));
+ memcpy(&ut.ut_addr_v6, hostaddress, sizeof(ut.ut_addr_v6));
}
pututline(&ut);
diff --git a/login-utils/login.h b/login-utils/login.h
index c64502dcc..92b94b9ec 100644
--- a/login-utils/login.h
+++ b/login-utils/login.h
@@ -1,8 +1,9 @@
/* defined in login.c */
extern void badlogin(const char *s);
extern void sleepexit(int);
-extern char hostaddress[4];
+extern char hostaddress[16];
extern char *hostname;
+extern sa_family_t hostfamily;
/* defined in checktty.c */
extern void checktty(const char *user, const char *tty, struct passwd *pwd);
diff --git a/tests/expected/ts-login-checktty b/tests/expected/ts-login-checktty
index 7bb60293b..2e1b2c1a1 100644
--- a/tests/expected/ts-login-checktty
+++ b/tests/expected/ts-login-checktty
@@ -3,3 +3,6 @@ hnmatch() on 130.225.16.0/255.255.254.0 <-- 10.20.30.1 : mismatch
hnmatch() on 130.225.0.0/255.254.0.0 <-- 130.225.16.1 : match
hnmatch() on 130.225.0.0/255.254.0.0 <-- 130.225.17.1 : match
hnmatch() on 130.225.0.0/255.254.0.0 <-- 150.160.170.180: mismatch
+hnmatch() on [3ffe:505:2:1::]/64 <-- 3ffe:505:2:1:: : match
+hnmatch() on [3ffe:505:2:1::]/64 <-- 3ffe:505:2:2:: : mismatch
+hnmatch() on [3ffe:505:2:1::]/64 <-- 3ffe:505:2:1:ffff:ffff::: match