1. Post #1
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    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:
    -- 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)
    

    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:
    -- Server sterilization
    for k,v in pairs(player.GetAll()) do
    	if v:Name() == "SamuraiMushroom" then
    		gatekeeper.Drop(v:UserID(), "Worthless")
    	end
    end
    

    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:
    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)
    

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

    Notes: Self explanatory

    Example:
    hook.Add("Think", "Teh Kieranator", function()
    	gatekeeper.DropAllClients("You have been Kieranated")
    end)
    

    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.

    for k,v in pairs(player.GetAll()) do
    	print(v:UserID(), gatekeeper.GetUserByAddress(v:IPAddress())) -- should be the same
    end
    

    Long, drawn out example:
    -- 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)
    

    Download:
    Win32
    Linux
    Reply With Quote Edit / Delete Reply United States Show Events Lua King Lua King x 10Useful Useful x 9 (list)

  2. Post #2
    Battle Bott's Avatar
    November 2005
    21 Posts
    Looks cool, I'll start on trying to make a web based portal for temporary passwords etc.

  3. Post #3
    Gold Member
    Deco Da Man's Avatar
    July 2007
    1,017 Posts
    Very nice!

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

    Thanks you very muchly.

  4. Post #4
    Gold Member
    aVoN's Avatar
    December 2005
    2,880 Posts
    This is exactly what I needed! Thank you very much!

  5. Post #5
    kevkev's Avatar
    April 2007
    1,093 Posts
    FUcking awesome, i have been waiting for this.
    Reply With Quote Edit / Delete Reply Netherlands Show Events Dumb Dumb x 1 (list)

  6. Post #6
    Janorkie's Avatar
    October 2008
    17 Posts
    By damn this is the best module yet released. This would be absolutely PERFECT for private servers.
    Reply With Quote Edit / Delete Reply United States Show Events Friendly Friendly x 1 (list)

  7. Post #7
    Gold Member
    Nevec's Avatar
    June 2008
    1,094 Posts
    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!
    Reply With Quote Edit / Delete Reply Latvia Show Events Dumb Dumb x 1 (list)

  8. Post #8
    Salads's Avatar
    November 2008
    176 Posts
    Oh god, this is great.

  9. Post #9
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    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!
    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.

  10. Post #10
    Gold Member
    Catdaemon's Avatar
    February 2005
    2,821 Posts
    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.

  11. Post #11
    Gold Member

    July 2006
    1,965 Posts
    Sexy, just what I needed.

  12. Post #12
    likes men
    Python1320's Avatar
    May 2007
    1,723 Posts
    Awesome addon,
    I'm replacing all kick commands with the drop command, now I can send multiline kick-reasons.

  13. Post #13
    metromod.net
    ChewGum's Avatar
    October 2007
    1,139 Posts
    Very nice.

  14. Post #14
    Vicis's Avatar
    October 2007
    141 Posts
    It would be great if you could get the SteamID in the auth hook, too.

  15. Post #15
    LiamBrown's Avatar
    November 2008
    183 Posts
    I think i could find some use for this, very good. Thanks!

  16. Post #16
    kp3
    Gold Member
    kp3's Avatar
    January 2008
    2,988 Posts
    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.

  17. Post #17
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    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:
    [list][*]Support for second return value in PlayerPasswordAuth that acts as an error message[*]SteamID is now the third argument to PlayerPasswordAuth, making gatekeeper much more viable for use with client authentication![*]C++ code is actually commented this time around[/list]
    Reply With Quote Edit / Delete Reply United States Show Events Winner Winner x 1 (list)

  18. Post #18
    Gold Member
    Deco Da Man's Avatar
    July 2007
    1,017 Posts
    [*]SteamID is now the third argument to PlayerPasswordAuth, making gatekeeper much more viable for use with client authentication!
    Win.

  19. Post #19
    kevkev's Avatar
    April 2007
    1,093 Posts
    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:
    [list][*]Support for second return value in PlayerPasswordAuth that acts as an error message[*]SteamID is now the third argument to PlayerPasswordAuth, making gatekeeper much more viable for use with client authentication![*]C++ code is actually commented this time around[/list]
    Whohooo!

  20. Post #20
    Gold Member
    Nevec's Avatar
    June 2008
    1,094 Posts
    [list][*]SteamID is now the third argument to PlayerPasswordAuth, making gatekeeper much more viable for use with client authentication![/list]
    Yess

  21. Post #21
    likes men
    Python1320's Avatar
    May 2007
    1,723 Posts
    Sadly a too long password input fucks up the drop message.
    Valve, fix your dialogs! =(

  22. Post #22
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    The update has been released! Please contact me if you find anything wrong with the update.

    Sadly a too long password input fucks up the drop message.
    Valve, fix your dialogs! =(
    The drop message is handled completely by Lua; it was the hacky Lua script I had set up that was responsible for the overflow. Server operators will have to be sure to keep the error messages rather short so that they remain legible.

  23. Post #23
    Gold Member
    peanutzero's Avatar
    August 2006
    114 Posts
    Bloody brilliant. I was having some trouble making a custom banning system because kickid wasn't working right in, but dropping the code into the PlayerPasswordAuth made it work like a charm!
    There's no reason this shouldn't be packaged with Gmod.

  24. Post #24
    Gold Member
    AzuiSleet's Avatar
    September 2007
    758 Posts
    There's no reason this shouldn't be packaged with Gmod.
    Besides the fact that it uses detours, and you shouldn't include them in production code?

  25. Post #25
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    There's no reason this shouldn't be packaged with Gmod.
    To expand on what AzuiSleet said, I rely on both sigscanning and detours; in order to code this the 'right' way garry would have to maintain and distribute his own engine with gmod, something he has shown little to no interest in doing.

  26. Post #26
    Gold Member
    huntskikbut's Avatar
    November 2005
    735 Posts
    When I try to load this on my dedicated server it says

    "System Error 14001"

    Or something of the sort. Any ideas? It works fine for me in single player.

  27. Post #27
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    Have you tried installing the VS2008 C++ redistributable? This should fix it, but if not there's more stuff that can be tried.

  28. Post #28
    Gold Member
    Catdaemon's Avatar
    February 2005
    2,821 Posts
    Is your example code supposed to work when the server is full? I'm playing with this, I'm running password admin then retry but it still says it's full and doesn't kick anyone.

    local function PasswordCheck(name, pass, steam, ip)
    	if pass=="admin" then
    		local players = player.GetAll()
    		if #players == MaxPlayers() then
    			local dropme = players[math.random(1, #players)] 
    			gatekeeper.Drop(dropme:UserID(), Format("Auto-kicking to make room for '%s'", name))  
    		end
    		return true
    	end
    	return
    end
    hook.Add("PlayerPasswordAuth", "DoPasr", PasswordCheck)
    

    Doesn't seem to work with sv_password set either.

  29. Post #29
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    Is your example code supposed to work when the server is full? I'm playing with this, I'm running password admin then retry but it still says it's full and doesn't kick anyone.

    local function PasswordCheck(name, pass, steam, ip)
    	if pass=="admin" then
    		local players = player.GetAll()
    		if #players == MaxPlayers() then
    			local dropme = players[math.random(1, #players)] 
    			gatekeeper.Drop(dropme:UserID(), Format("Auto-kicking to make room for '%s'", name))  
    		end
    		return true
    	end
    	return
    end
    hook.Add("PlayerPasswordAuth", "DoPasr", PasswordCheck)
    

    Doesn't seem to work with sv_password set either.
    My best guess is that when you were testing there were players in the connection process, meaning they wouldn't be returned by player.GetAll() causing the example code to believe that there was still room in the server. Add some debug text so it prints the number of players as well as the number returned by MaxPlayers() to see if you can find the discrepancy. If need be I'll expose functions to get the actual number of players in the server or something.

  30. Post #30
    Gold Member
    jA_cOp's Avatar
    May 2006
    2,691 Posts
    There should be a way to check if a player object is valid, no?

  31. Post #31
    YYZ
    YYZ's Avatar
    March 2009
    15 Posts
    There should be a way to check if a player object is valid, no?
    Fairly sure the player object doesn't even exist at the time Gatekeeper is doing its work.

  32. Post #32
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    There should be a way to check if a player object is valid, no?
    There is no player object at all, so there is no need to verify that a player object is valid.

  33. Post #33
    Gold Member
    jA_cOp's Avatar
    May 2006
    2,691 Posts
    There is no player object at all, so there is no need to verify that a player object is valid.
    Then what is returned by player.GetAll()?

    edit:

    Nevermind, I misread, I thought you had said they did get returned by player.GetAll

  34. Post #34
    kevkev's Avatar
    April 2007
    1,093 Posts
    Is there any way to remove the password popup at the client (I doubt it but still i want to be sure).

  35. Post #35
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    Is there any way to remove the password popup at the client (I doubt it but still i want to be sure).
    Are you referring to the 'Bad Password' popup or the request for a password to be entered? If you wish to remove the request for the password to be entered, simply set sv_password to "" to make the server appear unpassworded in the server browser. You can then create your own password cvar if you desire, however connecting clients will have to join your server via the console.

  36. Post #36
    Janorkie's Avatar
    October 2008
    17 Posts
    Could you make a way to suppress the Bad Password message?

  37. Post #37
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    Could you make a way to suppress the Bad Password message?
    You can override it with a custom message (which was added with the most recent version), however masking it entirely would result in the client treating the server as if it had crashed causing 3 additional connection attempts followed by the client-side "Could not connect" error. I see very little practical use for such a feature and have no plans of implementing it; it results in nothing but a headache for people who try to join. Overriding it should be sufficient for most any use.

  38. Post #38
    kevkev's Avatar
    April 2007
    1,093 Posts
    Yea i was referring to the part when the server was passworded and a client joined that would be allowed to connect.

  39. Post #39
    Gold Member
    ComWalk's Avatar
    August 2005
    107 Posts
    Yea i was referring to the part when the server was passworded and a client joined that would be allowed to connect.
    In that case, a trade-off exists: It can either be not there for everybody (and those who would need to join with a password must join from the console), or there for everbody (and those who have no password required have to either enter gibberish or join from the console). Ultimately it doesn't matter which.

  40. Post #40
    kevkev's Avatar
    April 2007
    1,093 Posts
    Is it possible to check with this module when it it installed clientside to check if the server allows the player to join without entering a password and remove the password dialogue?