gmsv_gatekeeper - Lua controlled server authentication

Update 11/13/11 - VoiDeD / Chrisaster took over development way more than a year ago and will be way more helpful than I can

GateKeeper allows Lua scripts to override the source server password check, allowing replacements of or additions to the normal sv_password check. This allows servers to have additional valid passwords or to use an authentication database that allows per-user or per-ip passwords. For private servers, this could help to eliminate password leaks, as the source of the leak would be immediately known. Furthermore, the function that it exposes completely invalidates the need for reserved slots in a server, as an unwanted player can be immediately dropped after a password has been verified, allowing a player to join an otherwise full server without receiving an error message.

Features:
Adds one new hook: “PlayerPasswordAuth”
Adds five new functions: “gatekeeper.Drop”, “gatekeeper.DropAllClients”, “gatekeeper.GetNumClients”, “gatekeeper.GetUserByAddress”, and “gatekeeper.ForceProtocol”

Usage:

Hook: PlayerPasswordAuth
Arguments: string username, string password, string steamid, string ipaddress
Returns: bool allowed OR string denyreason OR table { bool allowed [, string denyreason] }

Notes: Returning nil allows the default server password check to execute. Returning true forces the password to be accepted, while false forces it to be denied. If a string is returned, the client is denied access with the message contained in the string. If a table is used as the return value, the second member can be used as an error message to be displayed to the client in place of the typical “Bad password.” message.

Example:
[lua]
– This function will print the username and provided password of all connection attempts.
local function CheckPassword(user, pass, steam, ip)
print(Format(“User: ‘%s’ Pass: ‘%s’ SteamID: ‘%s’”, name, pass, steam))

-- Force all password comparisons to fail and display a custom error.
return {false, Format("'%s' is not the password I was looking for, %s", pass, name)}
-- The above line is equivalent to:
-- return Format("'%s' is not the password I was looking for, %s", pass, name)
-- Or, if you do not want to specify an error message and simply want clients kicked:
-- return false

end
hook.Add(“PlayerPasswordAuth”, “Example”, CheckPassword)
[/lua]

Function: gatekeeper.Drop
Arguments: number userid (Not the entity index!), string reason
Returns: bool dropped (true if player found, false if userid was not connected)

Notes: This takes place instantly; when the function has returned the client has already been disconnected. This allows for the server to drop a player during password verification and have the slot be immediately filled by the client attempting to have their password verified. Reserved slots are a thing of the past! This can happen at any time, and is not limited to PlayerPasswordAuth. Be wary of this, however, as a call to this immediately removes the client from the server, so if done in a hook like PlayerSay, this can crash the server if you allow the message to go through. Only use this when you can ensure that nothing will be done with the player’s object for the rest of the hook. This is safe to call in concommands. This is useful for kick messages in general.

Example:
[lua]
– Server sterilization
for k,v in pairs(player.GetAll()) do
if v:Name() == “SamuraiMushroom” then
gatekeeper.Drop(v:UserID(), “Worthless”)
end
end
[/lua]

Function: gatekeeper.GetNumClients
Arguments: none
Returns: table { active = ?, spawning = ?, total = ? }

Notes: Gets a table returning information about the clients connected to the server: How many there are, how many are ingame, and how many are currently connecting.

Example:
[lua]
local clients = gatekeeper.GetNumClients()
print(clients.active) – prints the number of clients currently active (have spawned) in the server
print(clients.spawning) – prints the number of clients in the connection process
print(clients.total) – prints the total number of clients in the server (connecting AND in game)
[/lua]

Function: gatekeeper.DropAllClients
Arguments: string disconnectreason
Returns: nil

Notes: Self explanatory

Example:
[lua]
hook.Add(“Think”, “Teh Kieranator”, function()
gatekeeper.DropAllClients(“You have been Kieranated”)
end)
[/lua]

Function: gatekeeper.GetUserByAddress
Arguments: string ipaddress (in form ip:port)
Returns: int userid (if ip was valid and connected) or nil (no user connected from that ip)

Example: This will not work in PlayerPasswordAuth because the connecting player does not have a userid yet. At any other point except possibly PlayerConnect (untested), it should function as expected.

[lua]
for k,v in pairs(player.GetAll()) do
print(v:UserID(), gatekeeper.GetUserByAddress(v:IPAddress())) – should be the same
end
[/lua]

Long, drawn out example:
[lua]
– GateKeeper V4 Example File
– ComWalk

– This file demonstrates adding two additional passwords, one of which will
– force the server to make room for the client, invalidating the need for
– reserved slots to be used.

require(“gatekeeper”)

– The cvar that will act as an additional password
local pass_new = CreateConVar(“sv_password_new”, “”, FCVAR_PROTECTED | FCVAR_NOTIFY)

– The cvar that will make room for a client if they attempt to join
local pass_makeroom = CreateConVar(“sv_password_makeroom”, “”, FCVAR_PROTECTED | FCVAR_NOTIFY)

local function PasswordCheck(name, pass, steam, ip)
print(Format(“User: ‘%s’ Pass: ‘%s’ SteamID: ‘%s’”, name, pass, steam))

-- To make use of the new error messages, you are forced to
-- return a table. Garry's hook module does not support hooks
-- that return multiple values, and this is the Next Best Thing.
-- Valid return values are booleans and tables following the 
-- following format: { bool allow [, string reason] }
-- The reason will only be used by the module if the client
-- is not allowed in the server.
if name == "SamuraiMushroom" then
	return {false, "Entry automatically denied: Worthless"}
elseif steam == "STEAM_0:1:16258120" then
	return {false, "Changing your name won't help either."}
elseif steam == "STEAM_0:0:9249431" then
	return {false, "Beat you to the punch on this one too."}
end

if pass_new:GetString() == pass and pass_new:GetString() != "" then
	-- Since the given password matches the 'new' password
	-- and the new password has been set, returning true
	-- will prevent the normal password check code
	-- from being executed.
	
	return true
elseif pass_makeroom:GetString() == pass and pass_makeroom:GetString() != "" then
	local players = gatekeeper.GetNumClients().total
	
	-- There is no need to drop anybody if the server isn't full
	if players == MaxPlayers() then
		local dropme = players[math.random(1, #players)]
		
		-- Gatekeeper exposes a new function to Lua, gatekeeper.Drop,
		-- which allows for the server to drop a client with a custom reason.
		-- Unlike using RunConsoleCommand to kick the unwanted player, this
		-- takes place instantly, and because the maxplayers check takes place
		-- after the password check, any player that provides this password to
		-- the server will successfully join, without any error message.
		
		gatekeeper.Drop(dropme:UserID(), Format("Auto-kicking to make room for '%s'", name))
	end
	
	-- Provided password was valid; force success and prevent
	-- the default password check from being executed.
	
	return true
end

-- Returning nil will allow the default password check to take place
-- and, in the case of this example, check sv_password as well.
-- In the event of a custom authentication system in which
-- the default check doesn't need to be run at all, simply
-- returning false here will suffice.
return

end
hook.Add(“PlayerPasswordAuth”, “test”, PasswordCheck)
[/lua]

Download:
Win32
Linux

Looks cool, I’ll start on trying to make a web based portal for temporary passwords etc.

Very nice!

Now I can kick people without the "Kicked by Console: " prefix :smiley:

Thanks you very muchly.

This is exactly what I needed! Thank you very much!

FUcking awesome, i have been waiting for this.

By damn this is the best module yet released. This would be absolutely PERFECT for private servers.

Is username and ip the only things you can get from the authing player? Would it be possible to get their steam id as well?

Anyways, a very nice module!

Oh god, this is great.

The steamid of a connecting client is not made available until well into the connection process, so it unfortunately is not possible at this time.

Edit: Actually, it is available during connection due to a recent engine update, however not until after PlayerPasswordAuth is called and a player slot is secured for the client, so it remains impractical.

I suppose you could keep the password stored and compare it with the steamid later on.

I fucking love this but for the sake of argument

For all the times I’ve wanted to say it:

Sexy, just what I needed.

Awesome addon,
I’m replacing all kick commands with the drop command, now I can send multiline kick-reasons.

Very nice.

It would be great if you could get the SteamID in the auth hook, too.

I think i could find some use for this, very good. Thanks!

http://sa.tweek.us/emots/images/emot-whip.gif

This is fucking awesome.
Just yesterday i found that a server where using some sort of modified kick message, Never would have thought it was released.

I’ve done a major overhaul of gatekeeper, and have added support for custom ‘Bad password’ messages and also provide the steamid of a connecting player to the PlayerPasswordAuth hook. I will be releasing it within a day or so, but in the mean time, it would help me ensure that several things are working properly if you would attempt to join 98.232.218.93:27015 with a random password; the more people who do this the better, as it will help me determine how reliably the steamid will be provided in the hook. In my limited testing it has worked every time, but more testing could never hurt.

Attempt to join 98.232.218.93:27015 with a random password if you’d like to help me test! If you get a password denied message, don’t worry, that means that it worked!

Added, to be released shortly:
[ul]
[li]Support for second return value in PlayerPasswordAuth that acts as an error message[/li][li]SteamID is now the third argument to PlayerPasswordAuth, making gatekeeper much more viable for use with client authentication![/li][li]C++ code is actually commented this time around[/li][/ul]

[quote=“ComWalk, post:18, topic:4498”]

[li]SteamID is now the third argument to PlayerPasswordAuth, making gatekeeper much more viable for use with client authentication![/li][/QUOTE]

Win.

Whohooo!