Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 17 Mar 2001 23:37:58 +0900 (JST)
From:      Hajimu UMEMOTO <ume@FreeBSD.org>
To:        audit@FreeBSD.org, net@FreeBSD.org
Subject:   [CFR] IPv6 support for skeyaccess(3)
Message-ID:  <20010317.233758.21880242.ume@FreeBSD.org>

next in thread | raw e-mail | index | archive | help
Hi,

I wish to support IPv6 for skeyaccess(3).  With this patch, you can
specify IPv6 address using `internet' keyword into /etc/skey.access.
Please review it.

Index: lib/libskey/skeyaccess.c
diff -u lib/libskey/skeyaccess.c.orig lib/libskey/skeyaccess.c
--- lib/libskey/skeyaccess.c.orig	Mon Oct 26 20:54:36 1998
+++ lib/libskey/skeyaccess.c	Fri Mar 16 03:04:48 2001
@@ -63,8 +63,8 @@
 static int match_group __P((struct login_info *));
 static int match_token __P((char *));
 static int is_internet_addr __P((char *));
-static struct in_addr *convert_internet_addr __P((char *));
-static struct in_addr *lookup_internet_addr __P((char *));
+static struct addrinfo *convert_internet_addr __P((char *));
+static struct addrinfo *lookup_internet_addr __P((char *));
 
 #define MAX_ADDR	32
 #define PERMIT		1
@@ -79,7 +79,7 @@
 
 struct login_info {
     char   *host_name;			/* host name */
-    struct in_addr *internet_addr;	/* null terminated list */
+    struct addrinfo *internet_addr;	/* addrinfo chain */
     char   *user;			/* user name */
     char   *port;			/* login port */
 };
@@ -120,22 +120,22 @@
     login_info.user = user;
     login_info.port = port;
 
-    if (host != 0 && !is_internet_addr(host)) {
+    if (host != NULL && !is_internet_addr(host)) {
 	login_info.host_name = host;
     } else {
-	login_info.host_name = 0;
+	login_info.host_name = NULL;
     }
 
-    if (addr != 0 && is_internet_addr(addr)) {
+    if (addr != NULL && is_internet_addr(addr)) {
 	login_info.internet_addr = convert_internet_addr(addr);
-    } else if (host != 0) {
+    } else if (host != NULL) {
 	if (is_internet_addr(host)) {
 	    login_info.internet_addr = convert_internet_addr(host);
 	} else {
 	    login_info.internet_addr = lookup_internet_addr(host);
 	}
     } else {
-	login_info.internet_addr = 0;
+	login_info.internet_addr = NULL;
     }
 
     /*
@@ -146,19 +146,23 @@
     printf("user: %s\n", login_info.user);
     printf("host: %s\n", login_info.host_name ? login_info.host_name : "none");
     printf("addr: ");
-    if (login_info.internet_addr == 0) {
+    if (login_info.internet_addr == NULL) {
 	printf("none\n");
     } else {
-	int     i;
+	struct addrinfo *res;
+	char haddr[NI_MAXHOST];
 
-	for (i = 0; login_info.internet_addr[i].s_addr; i++)
-	    printf("%s%s", login_info.internet_addr[i].s_addr == -1 ?
-		 "(see error log)" : inet_ntoa(login_info.internet_addr[i]),
-		   login_info.internet_addr[i + 1].s_addr ? " " : "\n");
+	for (res = login_info.internet_addr; res; res = res->ai_next) {
+	    getnameinfo(res->ai_addr, res->ai_addrlen, haddr, sizeof(haddr),
+			NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
+	    printf("%s%s", haddr, res->ai_next ? " " : "\n");
+	}
     }
 #endif
     result = _skeyaccess(fp, &login_info);
     fclose(fp);
+    if (login_info.internet_addr)
+	freeaddrinfo(login_info.internet_addr);
     return (result);
 }
 
@@ -226,33 +230,99 @@
     return (match ? permission : DENY);
 }
 
+/* translate IPv4 mapped IPv6 address to IPv4 address */
+
+static void
+ai_unmapped(struct addrinfo *ai)
+{
+    struct sockaddr_in6 *sin6;
+    struct sockaddr_in *sin4;
+    u_int32_t addr;
+    int port;
+
+    if (ai->ai_family != AF_INET6)
+	return;
+    sin6 = (struct sockaddr_in6 *)ai->ai_addr;
+    if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+	return;
+    sin4 = (struct sockaddr_in *)ai->ai_addr;
+    addr = *(u_int32_t *)&sin6->sin6_addr.s6_addr[12];
+    port = sin6->sin6_port;
+    memset(sin4, 0, sizeof(struct sockaddr_in));
+    sin4->sin_addr.s_addr = addr;
+    sin4->sin_port = port;
+    sin4->sin_family = AF_INET;
+    sin4->sin_len = sizeof(struct sockaddr_in);
+    ai->ai_family = AF_INET;
+    ai->ai_addrlen = sizeof(struct sockaddr_in);
+}
+
 /* match_internet_addr - match internet network address */
 
 static int match_internet_addr(login_info)
 struct login_info *login_info;
 {
-    char *     tok;
-    u_int32_t  pattern;
-    u_int32_t  mask;
-    struct in_addr *addrp;
+    char *tok;
+    struct addrinfo *res;
+    struct sockaddr_storage pattern, mask;
+    struct sockaddr_in *addr4, *pattern4, *mask4;
+    struct sockaddr_in6 *addr6, *pattern6, *mask6;
+    int i, match;
 
-    if (login_info->internet_addr == 0)
+    if (login_info->internet_addr == NULL)
 	return (0);
     if ((tok = need_internet_addr()) == 0)
 	return (0);
-    pattern = inet_addr(tok);
+    if ((res = convert_internet_addr(tok)) == NULL)
+	return (0);
+    memcpy(&pattern, res->ai_addr, res->ai_addrlen);
+    freeaddrinfo(res);
     if ((tok = need_internet_addr()) == 0)
 	return (0);
-    mask = inet_addr(tok);
+    if ((res = convert_internet_addr(tok)) == NULL)
+	return (0);
+    memcpy(&mask, res->ai_addr, res->ai_addrlen);
+    freeaddrinfo(res);
+    if (pattern.ss_family != mask.ss_family)
+	return (0);
+    mask4 = (struct sockaddr_in *)&mask;
+    pattern4 = (struct sockaddr_in *)&pattern;
+    mask6 = (struct sockaddr_in6 *)&mask;
+    pattern6 = (struct sockaddr_in6 *)&pattern;
 
     /*
      * See if any of the addresses matches a pattern in the control file. We
      * have already tried to drop addresses that belong to someone else.
      */
 
-    for (addrp = login_info->internet_addr; addrp->s_addr; addrp++)
-	if (addrp->s_addr != INADDR_NONE && (addrp->s_addr & mask) == pattern)
-	    return (1);
+    for (res = login_info->internet_addr; res; res = res->ai_next) {
+	ai_unmapped(res);
+	if (res->ai_family != pattern.ss_family)
+	    continue;
+	switch (res->ai_family) {
+	case AF_INET:
+	    addr4 = (struct sockaddr_in *)res->ai_addr;
+	    if (addr4->sin_addr.s_addr != INADDR_NONE &&
+		(addr4->sin_addr.s_addr & mask4->sin_addr.s_addr) == pattern4->sin_addr.s_addr)
+		return (1);
+	    break;
+	case AF_INET6:
+	    addr6 = (struct sockaddr_in6 *)res->ai_addr;
+	    if (pattern6->sin6_scope_id != 0 &&
+		addr6->sin6_scope_id != pattern6->sin6_scope_id)
+		break;
+	    match = 1;
+	    for (i = 0; i < 16; ++i) {
+		if ((addr6->sin6_addr.s6_addr[i] & mask6->sin6_addr.s6_addr[i]) != pattern6->sin6_addr.s6_addr[i]) {
+		    match = 0;
+		    break;
+		}
+	    }
+	    if (match)
+		return (1);
+	    break;
+	}
+    }
     return (0);
 }
 
@@ -369,53 +439,49 @@
 static int is_internet_addr(str)
 char   *str;
 {
-    int     in_run = 0;
-    int     runs = 0;
+    struct addrinfo *res;
 
-    /* Count the number of runs of characters between the dots. */
-
-    while (*str) {
-	if (*str == '.') {
-	    in_run = 0;
-	} else {
-	    if (!isdigit(*str))
-		return (0);
-	    if (in_run == 0) {
-		in_run = 1;
-		runs++;
-	    }
-	}
-	str++;
+    if ((res = convert_internet_addr(str)) != NULL)
+	freeaddrinfo(res);
+    return (res != NULL);
+}
+
+/*
+ * Nuke addrinfo entry from list.
+ * XXX: Depending on the implementation of KAME's getaddrinfo(3).
+ */
+static void nuke_ai(rp, res)
+struct addrinfo **rp, *res;
+{
+    *rp = res->ai_next;
+    if (res->ai_canonname) {
+	if (res->ai_next && !res->ai_next->ai_canonname)
+	    res->ai_next->ai_canonname = res->ai_canonname;
+	else
+	    free(res->ai_canonname);
     }
-    return (runs == 4);
+    free(res);
 }
 
 /* lookup_internet_addr - look up internet addresses with extreme prejudice */
 
-static struct in_addr *lookup_internet_addr(host)
+static struct addrinfo *lookup_internet_addr(host)
 char   *host;
 {
-    struct hostent *hp;
-    static struct in_addr list[MAX_ADDR + 1];
-    char    buf[MAXHOSTNAMELEN + 1];
-    int     length;
-    int     i;
-
-    if ((hp = gethostbyname(host)) == 0 || hp->h_addrtype != AF_INET)
-	return (0);
-
-    /*
-     * Save a copy of the results before gethostbyaddr() clobbers them.
-     */
-
-    for (i = 0; i < MAX_ADDR && hp->h_addr_list[i]; i++)
-	memcpy((char *) &list[i],
-	       hp->h_addr_list[i], (size_t)hp->h_length);
-    list[i].s_addr = 0;
-
-    strncpy(buf, hp->h_name, MAXHOSTNAMELEN);
-    buf[MAXHOSTNAMELEN] = 0;
-    length = hp->h_length;
+    struct addrinfo hints, *res0, *res, **rp;
+    char hname[NI_MAXHOST], haddr[NI_MAXHOST];
+    int error;
+
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
+    if (getaddrinfo(host, NULL, &hints, &res0) != 0)
+	return (NULL);
+    if (res0->ai_canonname == NULL) {
+	freeaddrinfo(res0);
+	return (NULL);
+    }
 
     /*
      * Wipe addresses that appear to belong to someone else. We will get
@@ -425,31 +491,51 @@
 #define NEQ(x,y)	(strcasecmp((x),(y)) != 0)
 #define NEQ3(x,y,n)	(strncasecmp((x),(y), (n)) != 0)
 
-    while (--i >= 0) {
-	if ((hp = gethostbyaddr((char *) &list[i], length, AF_INET)) == 0) {
+    rp = &res0;
+    for (res = res0; res; res = res->ai_next) {
+	if (res->ai_family != AF_INET && res->ai_family != AF_INET6) {
+	    nuke_ai(rp, res);
+	    continue;
+	}
+	error = getnameinfo(res->ai_addr, res->ai_addrlen,
+			    hname, sizeof(hname),
+			    NULL, 0, NI_NAMEREQD | NI_WITHSCOPEID);
+	if (error) {
+	    getnameinfo(res->ai_addr, res->ai_addrlen, haddr, sizeof(haddr),
+			NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
 	    syslog(LOG_ERR, "address %s not registered for host %s",
-		   inet_ntoa(list[i]), buf);
-	    list[i].s_addr = (u_int32_t) -1;
+		   haddr, res0->ai_canonname);
+	    nuke_ai(rp, res);
+	    continue;
 	}
-	if (NEQ(buf, hp->h_name) && NEQ3(buf, "localhost.", 10)) {
+	if (NEQ(res0->ai_canonname, hname) &&
+	    NEQ3(res0->ai_canonname, "localhost.", 10)) {
+	    getnameinfo(res->ai_addr, res->ai_addrlen, haddr, sizeof(haddr),
+			NULL, 0, NI_NUMERICHOST | NI_WITHSCOPEID);
 	    syslog(LOG_ERR, "address %s registered for host %s and %s",
-		   inet_ntoa(list[i]), hp->h_name, buf);
-	    list[i].s_addr = (u_int32_t) -1;
+		   haddr, hname, res0->ai_canonname);
+	    nuke_ai(rp, res);
+	    continue;
 	}
+	rp = &res->ai_next;
     }
-    return (list);
+    return (res0);
 }
 
 /* convert_internet_addr - convert string to internet address */
 
-static struct in_addr *convert_internet_addr(string)
+static struct addrinfo *convert_internet_addr(string)
 char   *string;
 {
-    static struct in_addr list[2];
+    struct addrinfo hints, *res;
 
-    list[0].s_addr = inet_addr(string);
-    list[1].s_addr = 0;
-    return (list);
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = PF_UNSPEC;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+    if (getaddrinfo(string, NULL, &hints, &res) != 0)
+	return (NULL);
+    return (res);
 }
 
 #ifdef TEST
@@ -458,7 +544,7 @@
 int     argc;
 char  **argv;
 {
-    struct hostent *hp;
+    struct addrinfo hints, *res;
     char    host[MAXHOSTNAMELEN + 1];
     int     verdict;
     char   *user;
@@ -475,8 +561,18 @@
     user = argv[1];
     port = argv[2];
     if (argv[3]) {
-	strncpy(host, (hp = gethostbyname(argv[3])) ?
-		hp->h_name : argv[3], MAXHOSTNAMELEN);
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = PF_UNSPEC;
+	hints.ai_socktype = SOCK_STREAM;
+	hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
+	if (getaddrinfo(argv[3], NULL, &hints, &res) == 0) {
+	    if (res->ai_canonname == NULL)
+		strncpy(host, argv[3], MAXHOSTNAMELEN);
+	    else
+		strncpy(host, res->ai_canonname, MAXHOSTNAMELEN);
+	    freeaddrinfo(res);
+	} else
+	    strncpy(host, argv[3], MAXHOSTNAMELEN);
 	host[MAXHOSTNAMELEN] = 0;
     }
     verdict = skeyaccess(user, port, argv[3] ? host : (char *) 0, (char *) 0);

--
Hajimu UMEMOTO @ Internet Mutual Aid Society Yokohama, Japan
ume@mahoroba.org  ume@bisd.hitachi.co.jp  ume@{,jp.}FreeBSD.org
http://www.imasy.org/~ume/

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-audit" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20010317.233758.21880242.ume>