]> err.no Git - util-linux/commitdiff
login: add IPv6 support
authorKarel Zak <kzak@redhat.com>
Sat, 10 Mar 2007 00:28:10 +0000 (01:28 +0100)
committerKarel Zak <kzak@redhat.com>
Sat, 10 Mar 2007 00:28:10 +0000 (01:28 +0100)
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>
login-utils/checktty.c
login-utils/login.1
login-utils/login.c
login-utils/login.h
tests/expected/ts-login-checktty

index f47b7ad4d67bc0a5b87b62ed7bb6e9de8f484c3d..16f9f2e88b9626cb1eb2475e3b65cbbdc008d08f 100644 (file)
@@ -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");
index 29590f946dfce3b4243806216ef1a8572781dd8f..f77ab0e57f6a730cf62f1d77c13a5b9ec973af8c 100644 (file)
@@ -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
index d5ec1fc09fd14799296d34f79e8f5fe4e4069f59..e3b4f6fd086d6cbbd9ab7b51a99910506bc9b300 100644 (file)
@@ -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);
index c64502dccb8968d06506ff700bddd35843f9718a..92b94b9ec43d0dd842021a47689ecce761193f07 100644 (file)
@@ -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);
index 7bb60293bfe87a025830e26ba0cd5b0c68349364..2e1b2c1a1375ea289a7d6ed68521fe4dfb550c41 100644 (file)
@@ -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