From 48d8deb3ad494f24d814414b52153bbe599c9fe9 Mon Sep 17 00:00:00 2001
From: Nigel Kukard <nkukard@lbsd.net>
Date: Sun, 16 Nov 2008 07:26:31 +0000
Subject: [PATCH] * Pulled in files that were no checked in from sitemanager

---
 FEATURES       |  42 ++++++
 OPERATORS      |  91 +++++++++++++
 smradiusd      | 347 ++++++++++++++++++++++++++++++-------------------
 smradiusd.conf |  47 +++++++
 4 files changed, 394 insertions(+), 133 deletions(-)
 create mode 100644 FEATURES
 create mode 100644 OPERATORS

diff --git a/FEATURES b/FEATURES
new file mode 100644
index 00000000..126acc79
--- /dev/null
+++ b/FEATURES
@@ -0,0 +1,42 @@
+FEATURES
+--------
+
+Network Engine:
+* Prefork design
+
+
+Database/Storage:
+* Database independent design
+	- Support for MySQL, PostgreSQL, Oracle, Sqlite3
+	- Replicated database support
+* Custom database queries allowing usage of returned results as macros
+
+
+Authentication:
+* Plugin: PAP
+* Plugin: CHAP
+* Plugin: MSCHAP (Supports MSCHAPv1 & MSCHAPv2)
+
+
+Radius Protocol:
+* Vendor specific attributes
+* Supported attribute operators include  =, :=, ==, +=, !=, >, >=, <, <=, =~, !~, =*, !*
+
+
+Enhanced features:
+* Plugin: Topups
+* Plugin: Auto-topups
+* Plugin: Usage/Time caps
+* Plugin: Prepaid accounting based on usage/time 
+* Plugin: Creation of accounting START records when no START record has been received but an interim update has - helps on slow/lossly links
+* Plugin: Notifications, % based or approximate time based
+* Plugin: User blacklists
+
+Todo:
+* Radius proxy - with filtering of requests/values ... etc in both directions + adding items.
+* Port 1700 proxy back to NAS's.
+
+Ideas:
+* E-commerce integration & API - very popular request
+* Database load balancing .... none of our largest clients have requested this, we talking 1 million users + ... *shrug*
+* IP Pools (in database)
diff --git a/OPERATORS b/OPERATORS
new file mode 100644
index 00000000..af158238
--- /dev/null
+++ b/OPERATORS
@@ -0,0 +1,91 @@
+Op 	 Example and documentation
+= 	
+
+Attribute = Value
+    Not allowed as a check item for RADIUS protocol attributes. It is allowed for server configuration attributes (Auth-Type, etc), and sets the value of on attribute, only if there is no other item of the same attribute. 
+
+    As a reply item, it means "add the item to the reply list, but only if there is no other item of the same attribute. 
+
+ := 	
+
+Attribute := Value
+    Always matches as a check item, and replaces in the configuration items any attribute of the same name. If no attribute of that name appears in the request, then this attribute is added. 
+
+    As a reply item, it has an identical meaning, but for the reply items, instead of the request items. 
+
+== 	
+
+Attribute == Value
+    As a check item, it matches if the named attribute is present in the request, AND has the given value. 
+
+    Not allowed as a reply item. 
+
++= 	
+
+Attribute += Value
+    Always matches as a check item, and adds the current attribute with value to the list of configuration items. 
+
+    As a reply item, it has an identical meaning, but the attribute is added to the reply items. 
+
+ != 	
+
+Attribute != Value
+    As a check item, matches if the given attribute is in the request, AND does not have the given value. 
+
+    Not allowed as a reply item. 
+
+> 	
+
+Attribute > Value
+    As a check item, it matches if the request contains an attribute with a value greater than the one given. 
+
+    Not allowed as a reply item. 
+
+>= 	
+
+Attribute >= Value
+    As a check item, it matches if the request contains an attribute with a value greater than, or equal to the one given. 
+
+    Not allowed as a reply item. 
+
+< 	
+
+Attribute < Value
+    As a check item, it matches if the request contains an attribute with a value less than the one given. 
+
+    Not allowed as a reply item. 
+
+<= 	
+
+Attribute <= Value
+    As a check item, it matches if the request contains an attribute with a value less than, or equal to the one given. 
+
+    Not allowed as a reply item. 
+
+=~ 	
+
+Attribute =~ Expression
+    As a check item, it matches if the request contains an attribute which matches the given regular expression. This operator may only be applied to string attributes. 
+
+    Not allowed as a reply item. 
+
+ !~ 	
+
+Attribute !~ Expression
+    As a check item, it matches if the request contains an attribute which does not match the given regular expression. This operator may only be applied to string attributes. 
+
+    Not allowed as a reply item. 
+
+=* 	
+
+Attribute =* Value
+    As a check item, it matches if the request contains the named attribute, no matter what the value is. 
+
+    Not allowed as a reply item. 
+
+ !* 	
+
+Attribute !* Value
+    As a check item, it matches if the request does not contain the named attribute, no matter what the value is. 
+
+    Not allowed as a reply item. 
diff --git a/smradiusd b/smradiusd
index 41b81ed1..de17bd67 100755
--- a/smradiusd
+++ b/smradiusd
@@ -344,6 +344,7 @@ sub child_finish_hook {
 sub process_request {
 	my $self = shift;
 	my $server = $self->{'server'};
+	my $client = $self->{'client'};
 	my $log = defined($server->{'config'}{'logging'}{'modules'});
 
 
@@ -363,12 +364,81 @@ sub process_request {
 	# VERIFY SOURCE SERVER
 	$self->log(LOG_DEBUG,"[SMRADIUS] Packet From = > ".$server->{'peeraddr'}."\n");
 
+
+#LOGIN
+#Service-Type:        Login-User
+#User-Name:           joe
+#User-Password:       \x{d3}\x{df}\x{10}\x{8c}\x{a0}r.\x{fd}=\x{ff}\x{96}\x{a}\x{86}\x{91}\x{e}c
+#Calling-Station-Id:  10.254.254.242
+#NAS-Identifier:      lbsd-test
+#NAS-IP-Address:      10.254.254.239
+
+#PPPOE:
+#Service-Type:        Framed-User
+#Framed-Protocol:     PPP
+#NAS-Port:            19
+#NAS-Port-Type:       Ethernet
+#User-Name:           nigel
+#Calling-Station-Id:  00:E0:4D:2A:72:35
+#Called-Station-Id:   pppoe-24
+#NAS-Port-Id:         ether1
+#NAS-Identifier:      lbsd-test
+#NAS-IP-Address:      10.254.254.239
+
+
+
+#
+# User Authentication
+#
+
+# Authentication
+#a. SELECT ID, Password FROM Users WHERE Username = %u
+# Optional Items:
+# 	'Disabled' - Indicates the user is disabled
+#
+# Save the query result, so we can use it as macros....  ${user.<column name>}  below...
+
+
+#
+# Authorization: Attribute checks
+#
+
+# User attributes
+#b. SELECT Attribute, OP, Value FROM UserAttributes WHERE UserID = ${user.id}
+
+# Attribute groups
+#c. SELECT Group FROM UsersToGroups WHERE UserID
+# Save the query result, so we can use it as macros... ${group.<column name>} below...
+
+# Group attributes
+#d. SELECT Attribute, OP, Value FROM GroupAttributes WHERE GroupID = ${group.id}
+# Loop with groups and do the query ...
+
+#
+# Authentication procedure
+#
+
+# On user AUTH ....
+#1. Execute query (a), set query result in hash
+#2. Check if password matches
+#	- if not reject
+#3. Pull in query (c) & (d)
+#4. Merge in query (b)
+#5. Check attributes that need checking
+#	- reject if fail
+#6. Return attributes that need to be returned
+
+
+
+
+
 	# GET SECRET FROM DB, cache for 5 mins
 
 	# DECODE PASSWORD
 
 	# QUERY USER & USERDATA IN DB
 	# CHECK IF PASS MATCHES 
+	$pkt->dump;
 
 
 	# Is this an accounting request
@@ -390,7 +460,7 @@ sub process_request {
 			# Try authenticate
 			if ($module->{'Auth_try'}) {
 				$self->log(LOG_INFO,"[SMRADIUS] Trying authentication plugin '".$module->{'Name'}."'");
-				my $res = $module->{'Auth_try'}($server,$user,$pkt);
+				my $res = $module->{'Auth_try'}($self,$user,$pkt);
 
 				# Check result
 				if (!defined($res)) {
@@ -418,6 +488,17 @@ sub process_request {
 			}
 		}
 
+		# Check if we authenticated or not
+		if ($authenticated) {
+			my $resp = Radius::Packet->new($self->{'radius'}->{'dictionary'});
+		   	$resp->set_code('Access-Accept');
+			$resp->set_identifier($pkt->identifier);
+			$resp->set_authenticator($pkt->authenticator);
+			$resp->set_attr('Framed-IP-Address' => "192.168.0.233");
+			$udp_packet = auth_resp($resp->pack, "test");
+			$server->{'client'}->send($udp_packet);
+		}
+
 	# We don't know how to handle this
 	} else {
 		$self->log(LOG_WARN,"[SMRADIUS] We cannot handle code: '".$pkt->code."'");
@@ -425,141 +506,141 @@ sub process_request {
 
 return;
 #	$pkt->dump;
-
-	# PAP
-	if ((my $rawPassword = $pkt->attr('User-Password'))) {
-
-
-		print(STDERR "RECEIVED\n");
-		print(STDERR "User-Pass: len = ".length($rawPassword).", hex = ".unpack("H*",$rawPassword)."\n");
-		print(STDERR "\n\n");
-
-		my $result = $pkt->password("test","User-Password");
-	
-		print(STDERR "CALC\n");
-		print(STDERR "Result   : len = ".length($result).", hex = ".unpack("H*",$result).", password = $result\n");
-
-	}
-
-	# CHAP
-	if ((my $rawChallenge = $pkt->attr('CHAP-Challenge')) && (my $rawPassword = $pkt->attr('CHAP-Password'))) {
-		print(STDERR "This is a CHAP challenge....\n");
-	
-		print(STDERR "RECEIVED\n");
-		print(STDERR "Challenge: len = ".length($rawChallenge).", hex = ".unpack("H*",$rawChallenge)."\n");
-		print(STDERR "Password : len = ".length($rawPassword).", hex = ".unpack("H*",$rawPassword)."\n");
-		print(STDERR "\n\n");
-
-		my $id = substr($rawPassword,0,1);
-		print(STDERR "ID: ".length($id).", hex = ".unpack("H*",$id)."\n");
-
-		my $result = encode_chap($id,$rawChallenge,"mytest");
-		
-		print(STDERR "CALC\n");
-		print(STDERR "Result   : len = ".length($result).", hex = ".unpack("H*",$result)."\n");
-		print(STDERR "\n\n");
-	}
-
-
-	# Is this a MSCHAP autehentication attempt?
-	if ((my $rawChallenge = $pkt->vsattr("311",'MS-CHAP-Challenge'))) {
-		print(STDERR "This is a MS-CHAP challenge....\n");
-
-		# MSCHAPv1
-		if (my $rawResponse = $pkt->vsattr("311",'MS-CHAP-Response')) {
-			my $challenge = @{$rawChallenge}[0];
-			my $response = substr(@{$rawResponse}[0],2);
-
-			print(STDERR "RECEIVED\n");
-			print(STDERR "Challenge: len = ".length($challenge).", hex = ".unpack("H*",$challenge)."\n");
-			print(STDERR "Reponse  : len = ".length($response).", hex = ".unpack("H*",$response)."\n");
-			print(STDERR "\n\n");
-
-
-
-			print(STDERR "CHOPPED OFFF!!\n");
+#
+#	# PAP
+#	if ((my $rawPassword = $pkt->attr('User-Password'))) {
+#
+#
+#		print(STDERR "RECEIVED\n");
+#		print(STDERR "User-Pass: len = ".length($rawPassword).", hex = ".unpack("H*",$rawPassword)."\n");
+#		print(STDERR "\n\n");
+#
+#		my $result = $pkt->password("test","User-Password");
+#	
+#		print(STDERR "CALC\n");
+#		print(STDERR "Result   : len = ".length($result).", hex = ".unpack("H*",$result).", password = $result\n");
+#
+#	}
+#
+#	# CHAP
+#	if ((my $rawChallenge = $pkt->attr('CHAP-Challenge')) && (my $rawPassword = $pkt->attr('CHAP-Password'))) {
+#		print(STDERR "This is a CHAP challenge....\n");
+#	
+#		print(STDERR "RECEIVED\n");
+#		print(STDERR "Challenge: len = ".length($rawChallenge).", hex = ".unpack("H*",$rawChallenge)."\n");
+#		print(STDERR "Password : len = ".length($rawPassword).", hex = ".unpack("H*",$rawPassword)."\n");
+#		print(STDERR "\n\n");
+#
+#		my $id = substr($rawPassword,0,1);
+#		print(STDERR "ID: ".length($id).", hex = ".unpack("H*",$id)."\n");
+#
+#		my $result = encode_chap($id,$rawChallenge,"mytest");
+#		
+#		print(STDERR "CALC\n");
+#		print(STDERR "Result   : len = ".length($result).", hex = ".unpack("H*",$result)."\n");
+#		print(STDERR "\n\n");
+#	}
+#
+#
+#	# Is this a MSCHAP autehentication attempt?
+#	if ((my $rawChallenge = $pkt->vsattr("311",'MS-CHAP-Challenge'))) {
+#		print(STDERR "This is a MS-CHAP challenge....\n");
+#
+#		# MSCHAPv1
+#		if (my $rawResponse = $pkt->vsattr("311",'MS-CHAP-Response')) {
+#			my $challenge = @{$rawChallenge}[0];
+#			my $response = substr(@{$rawResponse}[0],2);
+#
+#			print(STDERR "RECEIVED\n");
+#			print(STDERR "Challenge: len = ".length($challenge).", hex = ".unpack("H*",$challenge)."\n");
+#			print(STDERR "Reponse  : len = ".length($response).", hex = ".unpack("H*",$response)."\n");
+#			print(STDERR "\n\n");
+#
+#
+#
+#			print(STDERR "CHOPPED OFFF!!\n");
+##			my $peerChallenge = substr($response,0,16);
+#			my $NtResponse = substr($response,24,24);
+##			print(STDERR "Challenge: len = ".length($peerChallenge).", hex = ".unpack("H*",$peerChallenge)."\n");
+#			print(STDERR "NTRespons: len = ".length($NtResponse).", hex = ".unpack("H*",$NtResponse)."\n");
+#			print(STDERR "\n\n");
+#
+#			my $unipass = "mytest";
+#			$unipass =~ s/(.)/$1\0/g; # convert ASCII to unicaode
+#			my $username = "nigel";
+#
+#			print(STDERR "TEST\n");
+##			my $ourChallenge = ChallengeHash($peerChallenge,$challenge,$username);
+#			my $ourResponse = NtChallengeResponse($challenge,$unipass);
+#			print(STDERR "Calculate: len = ".length($ourResponse).", hex = ".unpack("H*",$ourResponse)."\n");
+#			print(STDERR "\n\n");
+#
+#
+#		# MSCHAPv2
+#		} elsif (my $rawResponse = $pkt->vsattr("311",'MS-CHAP2-Response')) {
+#			my $challenge = @{$rawChallenge}[0];
+#			my $response = substr(@{$rawResponse}[0],2);
+#
+#			print(STDERR "RECEIVED\n");
+#			print(STDERR "Challenge: len = ".length($challenge).", hex = ".unpack("H*",$challenge)."\n");
+#			print(STDERR "Reponse  : len = ".length($response).", hex = ".unpack("H*",$response)."\n");
+#			print(STDERR "\n\n");
+#
+#
+#
+#			print(STDERR "CHOPPED OFFF!!\n");
 #			my $peerChallenge = substr($response,0,16);
-			my $NtResponse = substr($response,24,24);
+#			my $NtRespnse = substr($response,24,24);
 #			print(STDERR "Challenge: len = ".length($peerChallenge).", hex = ".unpack("H*",$peerChallenge)."\n");
-			print(STDERR "NTRespons: len = ".length($NtResponse).", hex = ".unpack("H*",$NtResponse)."\n");
-			print(STDERR "\n\n");
-
-			my $unipass = "mytest";
-			$unipass =~ s/(.)/$1\0/g; # convert ASCII to unicaode
-			my $username = "nigel";
-
-			print(STDERR "TEST\n");
+#			print(STDERR "NTRespons: len = ".length($NtRespnse).", hex = ".unpack("H*",$NtRespnse)."\n");
+#			print(STDERR "\n\n");
+#
+#			my $unipass = "mytest";
+#			$unipass =~ s/(.)/$1\0/g; # convert ASCII to unicaode
+#			my $username = "nigel";
+#
+#			print(STDERR "TEST\n");
 #			my $ourChallenge = ChallengeHash($peerChallenge,$challenge,$username);
-			my $ourResponse = NtChallengeResponse($challenge,$unipass);
-			print(STDERR "Calculate: len = ".length($ourResponse).", hex = ".unpack("H*",$ourResponse)."\n");
-			print(STDERR "\n\n");
-
-
-		# MSCHAPv2
-		} elsif (my $rawResponse = $pkt->vsattr("311",'MS-CHAP2-Response')) {
-			my $challenge = @{$rawChallenge}[0];
-			my $response = substr(@{$rawResponse}[0],2);
-
-			print(STDERR "RECEIVED\n");
-			print(STDERR "Challenge: len = ".length($challenge).", hex = ".unpack("H*",$challenge)."\n");
-			print(STDERR "Reponse  : len = ".length($response).", hex = ".unpack("H*",$response)."\n");
-			print(STDERR "\n\n");
-
-
-
-			print(STDERR "CHOPPED OFFF!!\n");
-			my $peerChallenge = substr($response,0,16);
-			my $NtRespnse = substr($response,24,24);
-			print(STDERR "Challenge: len = ".length($peerChallenge).", hex = ".unpack("H*",$peerChallenge)."\n");
-			print(STDERR "NTRespons: len = ".length($NtRespnse).", hex = ".unpack("H*",$NtRespnse)."\n");
-			print(STDERR "\n\n");
-
-			my $unipass = "mytest";
-			$unipass =~ s/(.)/$1\0/g; # convert ASCII to unicaode
-			my $username = "nigel";
-
-			print(STDERR "TEST\n");
-			my $ourChallenge = ChallengeHash($peerChallenge,$challenge,$username);
-			my $ourResponse = NtChallengeResponse($ourChallenge,$unipass);
-			print(STDERR "Calculate: len = ".length($ourResponse).", hex = ".unpack("H*",$ourResponse)."\n");
-			print(STDERR "\n\n");
-
-
-
-		}
-	}
-
-
-
-#	printf("GOT PACKET: user = %s/%s, nas-ip = %s, nas-port-type = %s, nas-port = %s, connect-info = %s, service-type = %s\n",
-#		$pkt->attr('User-Name'), $pkt->password('test'),
-#		$pkt->attr('NAS-IP-Address'),
-#		$pkt->attr('NAS-Port-Type'),
-#		$pkt->attr('NAS-Port'),
-#		$pkt->attr('Connect-Info'),
-#		$pkt->attr('Service-Type')
-#	);
-
-
-	if ($pkt->code eq "Accounting-Request") {
-		my $resp = Radius::Packet->new($self->{'config'}->{'dictionary'});
-		$resp->set_code('Accounting-Response');
-		$resp->set_identifier($pkt->identifier);
-		$resp->set_authenticator($pkt->authenticator);
-		$udp_packet = auth_resp($resp->pack, "test");
-		$server->{'client'}->send($udp_packet);
-
-
-	} elsif ($pkt->code eq "Access-Request") {
-		my $resp = Radius::Packet->new($self->{'config'}->{'dictionary'});
-    	$resp->set_code('Access-Accept');
-	    $resp->set_identifier($pkt->identifier);
-	    $resp->set_authenticator($pkt->authenticator);
-	    $resp->set_attr('Framed-IP-Address' => "192.168.0.233");
-		$udp_packet = auth_resp($resp->pack, "test");
-		$server->{'client'}->send($udp_packet);
-	}
-
+#			my $ourResponse = NtChallengeResponse($ourChallenge,$unipass);
+#			print(STDERR "Calculate: len = ".length($ourResponse).", hex = ".unpack("H*",$ourResponse)."\n");
+#			print(STDERR "\n\n");
+#
+#
+#
+#		}
+#	}
+#
+#
+#
+##	printf("GOT PACKET: user = %s/%s, nas-ip = %s, nas-port-type = %s, nas-port = %s, connect-info = %s, service-type = %s\n",
+##		$pkt->attr('User-Name'), $pkt->password('test'),
+##		$pkt->attr('NAS-IP-Address'),
+##		$pkt->attr('NAS-Port-Type'),
+##		$pkt->attr('NAS-Port'),
+##		$pkt->attr('Connect-Info'),
+##		$pkt->attr('Service-Type')
+##	);
+#
+#
+#	if ($pkt->code eq "Accounting-Request") {
+#		my $resp = Radius::Packet->new($self->{'config'}->{'dictionary'});
+#		$resp->set_code('Accounting-Response');
+#		$resp->set_identifier($pkt->identifier);
+#		$resp->set_authenticator($pkt->authenticator);
+#		$udp_packet = auth_resp($resp->pack, "test");
+#		$server->{'client'}->send($udp_packet);
+#
+#
+#	} elsif ($pkt->code eq "Access-Request") {
+#		my $resp = Radius::Packet->new($self->{'config'}->{'dictionary'});
+#    	$resp->set_code('Access-Accept');
+#	    $resp->set_identifier($pkt->identifier);
+#	    $resp->set_authenticator($pkt->authenticator);
+#	    $resp->set_attr('Framed-IP-Address' => "192.168.0.233");
+#		$udp_packet = auth_resp($resp->pack, "test");
+#		$server->{'client'}->send($udp_packet);
+#	}
+#
 
 }
 
diff --git a/smradiusd.conf b/smradiusd.conf
index 0a0688f3..2e06055e 100644
--- a/smradiusd.conf
+++ b/smradiusd.conf
@@ -99,6 +99,53 @@ plugins=<<EOT
 mod_auth_pap
 mod_auth_chap
 mod_auth_mschap
+mod_auth_mac  # Always runs if successfull & verifies?
 EOT
 
+sql_password_query=<<EOT
+SELECT ID, Password FROM Users WHERE Username = %u
+EOT
+
+sql_failure_query=<<EOT
+INSERT INTO AuthFail (Username) VALUES (%u)
+EOT
+
+
+
+[authorization]
+sql_user_attribute_query=<<EOT
+SELECT Attribute, OP, Value FROM UserAttributes WHERE UserID = ${user.id}
+EOT
+
+sql_group_query=<<EOT
+SELECT GroupID FROM UsersToGroups WHERE UserID = ${user.id}
+EOT
+
+sql_group_attribute_query=<<EOT
+SELECT Attribute, OP, Value FROM GroupAttributes WHERE GroupID = ${group.id}
+EOT
+
+
+
+[accounting]
+plugins=<<EOT
+mod_acct_topups
+mod_acct_capping  # use a special field, which includes topups
+EOT
+
+sql_accounting_query=<<EOT
+SELECT xyz FROM Accounting WHERE
+EOT
+
+sql_accounting_insert=<<EOT
+INSERT INTO Accounting () VALUES ()
+EOT
+
+sql_accounting_update=<<EOT
+UPDATE Accounting SET
+EOT
+
+
+
+
 
-- 
GitLab