getnameinfo(3) and access controls

Jun-ichiro itojun Hagino, KAME Project
$Id: index.html,v 1.5 2003/05/27 13:24:15 jinmei Exp $
This is a little warning on the use of getnameinfo(3) in the context of access control.

If your software, for example, has access control list (ACL) implemented as string comparison:

	char *permit[] = {
		"10.1.1.1",
		"foo.bar.com",
		NULL,
	};
You would perform getpeername(3) to get the peer's address, and then you will compare it with the ACL.
	int s;
	struct sockaddr_storage ss;
	socklen_t sslen;
	char name[NI_MAXHOST];
	int i;
	hasptr fqdn;

	sslen = sizeof(ss);
	getpeername(s, (struct sockaddr *)&ss, &sslen);
	if (getnameinfo((struct sockaddr *)&ss, sslen, name, sizeof(name),
	    NULL, 0, NI_NAMEREQD) == 0) {
		hasptr = 1;
	} else {
		getnameinfo((struct sockaddr *)&ss, sslen, name, sizeof(name),
		    NULL, 0, NI_NUMERICHOST);
		hasptr = 0;
	}

	for (i = 0; permit[i]; i++) {
		if (strcmp(name, permit[i]) == 0)
			return 1;	/*in the ACL*/
	}
	return 0;	/*not in the ACL*/
However, the code is vulnerable to malicious PTR records, configured as follows:
	x.x.x.x.in-addr.arpa.	IN PTR	10.1.1.1
To protect against such an attack, the return value from getnameinfo(3) has to be verified in "hasptr == 1" case.
	...
	if (getnameinfo((struct sockaddr *)&ss, sslen, name, sizeof(name),
	    NULL, 0, NI_NAMEREQD) == 0) {
		/*
		 * if "name" contains numeric IP address representation,
		 * it is a malicious PTR record.
		 */
		memset(&hints, 0, sizeof(hints));
		hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
		hints.ai_flags = AI_NUMERICHOST;
		if (getaddrinfo(name, "0", &hints, &res) == 0) {
			freeaddrinfo(res);
			return 0;	/*invalid PTR*/
		}
		hasptr = 1;
	} else {
		...
	}
	...
Also, with hasptr == 1 case you may want to verified it by usual double-reverse lookup for integrity.

Note: In any event, the result of reverse lookup is not very trustworthy, and thus it is not advisable to rely on the result for access control purposes. The simplest and safest way would be to allow only numeric addresses in the ACL list, and to specify NI_NUMERICHOST for getnameinfo(3), which should be called just once.