diff options
author | Karel Zak | 2007-03-10 01:28:10 +0100 |
---|---|---|
committer | Karel Zak | 2007-03-10 01:28:10 +0100 |
commit | ea6c190a661170d905ca63e5deae9f0a399f039e (patch) | |
tree | 79dd7423a3963e6373861e21c003b4b671e1507b /login-utils/checktty.c | |
parent | login: add regression test for IP address checking code (diff) | |
download | kernel-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>
Diffstat (limited to 'login-utils/checktty.c')
-rw-r--r-- | login-utils/checktty.c | 149 |
1 files changed, 120 insertions, 29 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"); |