From: Karel Zak Date: Sat, 10 Mar 2007 00:28:10 +0000 (+0100) Subject: login: add IPv6 support X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ea6c190a661170d905ca63e5deae9f0a399f039e;p=util-linux 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 Signed-off-by: Karel Zak --- diff --git a/login-utils/checktty.c b/login-utils/checktty.c index f47b7ad4..16f9f2e8 100644 --- a/login-utils/checktty.c +++ b/login-utils/checktty.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "nls.h" #include @@ -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 29590f94..f77ab0e5 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 d5ec1fc0..e3b4f6fd 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 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 c64502dc..92b94b9e 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 7bb60293..2e1b2c1a 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