changeset 4078:265655f270df HEAD

Added "allow_nets" extra field. If set, the user can log in only from within the given networks (hope the code is correct...)
author Timo Sirainen <timo.sirainen@movial.fi>
date Mon, 27 Feb 2006 20:46:29 +0200
parents 73573b9ecb00
children b033db13c2e0
files src/auth/auth-request.c src/auth/auth-request.h
diffstat 2 files changed, 105 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/auth/auth-request.c	Mon Feb 27 18:30:39 2006 +0200
+++ b/src/auth/auth-request.c	Mon Feb 27 20:46:29 2006 +0200
@@ -17,6 +17,8 @@
 #include "passdb-cache.h"
 #include "password-scheme.h"
 
+#include <stdlib.h>
+
 struct auth_request *
 auth_request_new(struct auth *auth, struct mech_module *mech,
 		 mech_callback_t *callback, void *context)
@@ -63,6 +65,12 @@
 {
 	i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE);
 
+	if (request->passdb_failure) {
+		/* password was valid, but some other check failed. */
+		auth_request_fail(request);
+		return;
+	}
+
 	request->state = AUTH_REQUEST_STATE_FINISHED;
 	request->successful = TRUE;
 	request->callback(request, AUTH_CLIENT_RESULT_SUCCESS,
@@ -582,6 +590,97 @@
         return request->requested_login_user != NULL;
 }
 
+static int is_ip_in_network(const char *network, const struct ip_addr *ip)
+{
+	const uint32_t *ip1, *ip2;
+	struct ip_addr net_ip;
+	const char *p;
+	unsigned int max_bits, bits, pos, i;
+	uint32_t mask;
+
+	max_bits = IPADDR_IS_V4(ip) ? 32 : 128;
+	p = strchr(network, '/');
+	if (p == NULL) {
+		/* full IP address must match */
+		bits = max_bits;
+	} else {
+		/* get the network mask */
+		network = t_strdup_until(network, p);
+		bits = strtoul(p+1, NULL, 10);
+		if (bits > max_bits)
+			bits = max_bits;
+	}
+
+	if (net_addr2ip(network, &net_ip) < 0)
+		return -1;
+
+	if (IPADDR_IS_V4(ip) && !IPADDR_IS_V4(&net_ip)) {
+		/* one is IPv6 and one is IPv4 */
+		return 0;
+	}
+	i_assert(IPADDR_IS_V6(ip) == IPADDR_IS_V6(&net_ip));
+
+	ip1 = (const uint32_t *)&ip->ip;
+	ip2 = (const uint32_t *)&net_ip.ip;
+
+	/* check first the full 32bit ints */
+	for (pos = 0, i = 0; pos + 32 <= bits; pos += 32, i++) {
+		if (ip1[i] != ip2[i])
+			return 0;
+	}
+
+	/* check the last full bytes */
+	for (mask = 0xff; pos + 8 <= bits; pos += 8, mask <<= 8) {
+		if ((ip1[i] & mask) != (ip2[i] & mask))
+			return 0;
+	}
+
+	/* check the last bits, they're reversed in bytes */
+	bits -= pos;
+	for (mask = 0x80 << (pos % 32); bits > 0; bits--, mask >>= 1) {
+		if ((ip1[i] & mask) != (ip2[i] & mask))
+			return 0;
+	}
+	return 1;
+}
+
+static void auth_request_validate_networks(struct auth_request *request,
+					   const char *networks)
+{
+	const char *const *net;
+	bool found = FALSE;
+
+	if (request->remote_ip.family == 0) {
+		/* IP not known */
+		auth_request_log_info(request, "passdb",
+			"allow_nets check failed: Remote IP not known");
+		request->passdb_failure = TRUE;
+		return;
+	}
+
+	t_push();
+	for (net = t_strsplit_spaces(networks, ", "); *net != NULL; net++) {
+		switch (is_ip_in_network(*net, &request->remote_ip)) {
+		case 1:
+			found = TRUE;
+			break;
+		case -1:
+			auth_request_log_info(request, "passdb",
+				"allow_nets: Invalid network '%s'", *net);
+			break;
+		default:
+			break;
+		}
+	}
+	t_pop();
+
+	if (!found) {
+		auth_request_log_info(request, "passdb",
+			"allow_nets check failed: IP not in allowed networks");
+	}
+	request->passdb_failure = !found;
+}
+
 void auth_request_set_field(struct auth_request *request,
 			    const char *name, const char *value,
 			    const char *default_scheme)
@@ -633,6 +732,11 @@
 		return;
 	}
 
+	if (strcmp(name, "allow_nets") == 0) {
+		auth_request_validate_networks(request, value);
+		return;
+	}
+
 	if (strcmp(name, "nologin") == 0) {
 		/* user can't actually login - don't keep this
 		   reply for master */
--- a/src/auth/auth-request.h	Mon Feb 27 18:30:39 2006 +0200
+++ b/src/auth/auth-request.h	Mon Feb 27 20:46:29 2006 +0200
@@ -64,6 +64,7 @@
         struct auth_master_connection *master;
 
 	unsigned int successful:1;
+	unsigned int passdb_failure:1;
 	unsigned int internal_failure:1;
 	unsigned int passdb_internal_failure:1;
 	unsigned int delayed_failure:1;