Browse Source

Added wip blacklist module filtering and validation logic

Toby Chui 3 years ago
parent
commit
2969fdb479

+ 4 - 4
hardware.power.go

@@ -61,9 +61,9 @@ func hardware_power_poweroff(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	passwordCorrect := authAgent.ValidateUsernameAndPassword(userinfo.Username, password)
+	passwordCorrect, rejectionReason := authAgent.ValidateUsernameAndPasswordWithReason(userinfo.Username, password)
 	if !passwordCorrect {
-		sendErrorResponse(w, "Password Incorrect")
+		sendErrorResponse(w, rejectionReason)
 		return
 	}
 
@@ -128,9 +128,9 @@ func hardware_power_restart(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	passwordCorrect := authAgent.ValidateUsernameAndPassword(userinfo.Username, password)
+	passwordCorrect, rejectionReason := authAgent.ValidateUsernameAndPasswordWithReason(userinfo.Username, password)
 	if !passwordCorrect {
-		sendErrorResponse(w, "Password Incorrect")
+		sendErrorResponse(w, rejectionReason)
 		return
 	}
 

+ 12 - 6
mod/auth/auth.go

@@ -176,7 +176,7 @@ func (a *AuthAgent) HandleLogin(w http.ResponseWriter, r *http.Request) {
 	}
 
 	//Check the database and see if this user is in the database
-	passwordCorrect := a.ValidateUsernameAndPassword(username, password)
+	passwordCorrect, rejectionReason := a.ValidateUsernameAndPasswordWithReason(username, password)
 	//The database contain this user information. Check its password if it is correct
 	if passwordCorrect {
 		//Password correct
@@ -188,27 +188,33 @@ func (a *AuthAgent) HandleLogin(w http.ResponseWriter, r *http.Request) {
 		sendOK(w)
 	} else {
 		//Password incorrect
-		log.Println(username + " has entered an invalid username or password")
-		sendErrorResponse(w, "Invalid username or password")
+		log.Println(username + " login request rejected: " + rejectionReason)
+		sendErrorResponse(w, rejectionReason)
 		a.Logger.LogAuth(r, false)
 		return
 	}
 }
 
 func (a *AuthAgent) ValidateUsernameAndPassword(username string, password string) bool {
+	succ, _ := a.ValidateUsernameAndPasswordWithReason(username, password)
+	return succ
+}
+
+//validate the username and password, return reasons if the auth failed
+func (a *AuthAgent) ValidateUsernameAndPasswordWithReason(username string, password string) (bool, string) {
 	hashedPassword := Hash(password)
 	var passwordInDB string
 	err := a.Database.Read("auth", "passhash/"+username, &passwordInDB)
 	if err != nil {
 		//User not found or db exception
 		//log.Println("[System Auth] " + username + " login with incorrect password")
-		return false
+		return false, "Invalid username or password"
 	}
 
 	if passwordInDB == hashedPassword {
-		return true
+		return true, ""
 	} else {
-		return false
+		return false, "Invalid username or password"
 	}
 }
 

+ 137 - 0
mod/auth/blacklist/blacklist.go

@@ -0,0 +1,137 @@
+package blacklist
+
+import (
+	"errors"
+	"net"
+	"strconv"
+	"strings"
+
+	db "imuslab.com/arozos/mod/database"
+)
+
+/*
+
+	ArozOS Blacklist Module
+	Author: tobychui
+
+	This module record the IP blacklist of users trying to enter the
+	system without permission
+
+*/
+
+type BlackList struct {
+	Enabled  bool
+	database *db.Database
+}
+
+func NewBlacklistManager(sysdb *db.Database) *BlackList {
+	sysdb.NewTable("ipblacklist")
+
+	return &BlackList{
+		Enabled:  true,
+		database: sysdb,
+	}
+}
+
+func (bl *BlackList) IsBanned(ip string) bool {
+	if bl.database.KeyExists("ipblacklist", ip) {
+		return true
+	}
+	return false
+}
+
+func (bl *BlackList) SetBan(ipRange string) error {
+	//Check if the IP range is correct
+	err := validateIpRange(ipRange)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func ipInRange(ip string, ipRange string) bool {
+	ip = strings.TrimSpace(ip)
+	ipRange = strings.TrimSpace(ipRange)
+	if ip == ipRange {
+		//For fields that the ipRange is the ip itself
+		return true
+	}
+
+	//Try matching range
+	if strings.Contains(ipRange, "-") {
+		//For range, in A.B.C.D, the A.B.C part must be the same, so do prefix matching
+		ips := strings.Split(ipRange, "-")
+		startSubnet := ips[0][:strings.LastIndex(ips[0], ".")]
+		targetIpSubnet := ip[:strings.LastIndex(ip, ".")]
+		if startSubnet == targetIpSubnet {
+			//Check the last value in range
+			StartDval, _ := strconv.Atoi(ips[0][strings.LastIndex(ips[0], ".")+1:])
+			EndDval, _ := strconv.Atoi(ips[1][strings.LastIndex(ips[1], ".")+1:])
+			thisDVal, _ := strconv.Atoi(ip[strings.LastIndex(ip, ".")+1:])
+
+			if thisDVal > StartDval && thisDVal < EndDval {
+				//In range
+				return true
+			} else {
+				//Not in range
+				return false
+			}
+		} else {
+			//Subnet different. Must be not in this range
+			return false
+		}
+
+	}
+	return false
+}
+
+//Check if the given IP Range string is actually an IP range
+func validateIpRange(ipRange string) error {
+	ipRange = strings.TrimSpace(ipRange)
+	ipRange = strings.ReplaceAll(ipRange, " ", "")
+	if strings.Contains(ipRange, "-") {
+		//This is a range
+		if strings.Count(ipRange, "-") != 1 {
+			//Invalid range defination
+			return errors.New("Invalid ip range defination")
+		}
+		ips := strings.Split(ipRange, "-")
+		//Check if the starting IP and ending IP are both valid
+		if net.ParseIP(ips[0]) == nil {
+			return errors.New("Starting ip is invalid")
+		}
+
+		if net.ParseIP(ips[1]) == nil {
+			return errors.New("Ending ip is invalid")
+		}
+
+		//Check if the ending IP is larger than the starting IP
+		startingIpInt, _ := strconv.Atoi(strings.ReplaceAll(ips[0], ".", ""))
+		endingIpInt, _ := strconv.Atoi(strings.ReplaceAll(ips[1], ".", ""))
+
+		if startingIpInt >= endingIpInt {
+			return errors.New("Invalid ip range: Starting IP is larger or equal to ending ip")
+		}
+
+		//Check if they are in the same subnet
+		startSubnet := ips[0][:strings.LastIndex(ips[0], ".")]
+		endSubnet := ips[1][:strings.LastIndex(ips[1], ".")]
+
+		if startSubnet != endSubnet {
+			//They are not in the same subnet
+			return errors.New("IP range subnet mismatch")
+		}
+
+	} else {
+		//This is a single IP instead of range. Check if it is a valid IP addr
+		if net.ParseIP(ipRange) != nil {
+			//Ok
+			return nil
+		} else {
+			return errors.New("Invalid ip given")
+		}
+	}
+
+	return nil
+}

+ 67 - 0
mod/auth/blacklist/blacklist_test.go

@@ -0,0 +1,67 @@
+package blacklist
+
+import (
+	"testing"
+)
+
+func TestIpInRange(t *testing.T) {
+	r := ipInRange("192.168.1.128", "192.168.1.100 - 192.168.1.200")
+	if r == false {
+		t.Fatal("Correct IP in range reported as error")
+	}
+
+	r = ipInRange("192.168.1.128", "192.168.1.128 ")
+	if r == false {
+		t.Fatal("Correct IP in range reported as error")
+	}
+
+	r = ipInRange("192.168.1.128", "192.168.1.1 - 192.168.1.100")
+	if r == true {
+		t.Fatal("Invalid IP in range reported as correct")
+	}
+
+}
+
+func TestSingleIP(t *testing.T) {
+	err := validateIpRange("192.168.1.128")
+	if err != nil {
+		t.Fatal("Correct IP range reported as error", err)
+	}
+
+	err = validateIpRange("192.168.1.asd")
+	if err == nil {
+		t.Fatal("Invalid ip reported as correct", err)
+	}
+
+	err = validateIpRange("192.168.1.100.123.234")
+	if err == nil {
+		t.Fatal("Invalid ip reported as correct", err)
+	}
+
+}
+func TestIPRange(t *testing.T) {
+	err := validateIpRange("192.168.1.150 - 192.168.1.250")
+	if err != nil {
+		t.Fatal("Correct IP range reported as error", err)
+	}
+
+	err = validateIpRange("192.168.1.1 - 192.168.1.100")
+	if err != nil {
+		t.Fatal("Correct IP range reported as error", err)
+	}
+
+	err = validateIpRange("192.168.1.255 - 192.168.1.250")
+	if err == nil {
+		t.Fatal("Invalid correct resp on starting ip > ending ip", err)
+	}
+
+	err = validateIpRange("192.168.1.120 -192.168.2.100")
+	if err == nil {
+		t.Fatal("Invalid ip range reported as correct", err)
+	}
+
+	err = validateIpRange("d037:b377:039a:b621:145b:0d10:3d38:982f - 4fe9:1561:c37c:1f66:f696:948d:c452:73a3")
+	if err == nil {
+		t.Fatal("Not supported ip range reported as correct", err)
+	}
+}

+ 3 - 3
mod/storage/webdav/webdav.go

@@ -229,11 +229,11 @@ func (s *Server) HandleRequest(w http.ResponseWriter, r *http.Request) {
 
 	//validate username and password
 	authAgent := s.userHandler.GetAuthAgent()
-	passwordValid := authAgent.ValidateUsernameAndPassword(username, password)
+	passwordValid, rejectionReason := authAgent.ValidateUsernameAndPasswordWithReason(username, password)
 	if !passwordValid {
 		authAgent.Logger.LogAuthByRequestInfo(username, r.RemoteAddr, time.Now().Unix(), false, "webdav")
-		log.Println("Someone from " + r.RemoteAddr + " try to log into " + username + " WebDAV endpoint with incorrect password")
-		http.Error(w, "Invalid username or password", http.StatusUnauthorized)
+		log.Println("Someone from " + r.RemoteAddr + " try to log into " + username + " WebDAV endpoint but got rejected: " + rejectionReason)
+		http.Error(w, rejectionReason, http.StatusUnauthorized)
 		return
 	}