- 1 BZAuthd - the global authentication daemon
- 1.1 I. Abstract
- 1.2 II. Long term goals
- 1.3 III. Detailed Description
- 1.4 IV. Timeline
- 1.5 V. Work Log
BZAuthd - the global authentication daemon
BZFlag currently employs a php list server that, besides sending the list of servers and managing permissions, handles global authentication for players by their callsign and password. I propose implementing BZAuthd, a standalone C++ authentication daemon that would address some of its issues, as well as add multiple other desirable features.
The list server does not perform well under high load and since it's run through an apache web server, it is not easy to profile either. The web server is considered a burden to maintain and the fact that it's not written in the same programming language as the majority of the code base is also a disadvantage.
The current system is tied in with the phpBB forums and does not offer a viable method of using the same authentication data for other services like MediaWiki. A good solution is to use LDAP as a backend for storing the auth data since both phpBB and MediaWiki have plugins that support LDAP authentication and Drupal has full support for it.
Another big issue is the lack of data redundancy, the MySQL tables are a single point of failure for the system. The daemon would allow the auth data will to be replicated by using OpenLDAP's built in replication. The daemon would fall back to reading data from the slaves and cache writes to the master until it comes back online. Should the daemon die aswell during this time, clients and servers could fall back to additional instances of it.
The callsign and password are sent in clear text form to the list server and this is a risk to the users' privacy since they may use those passwords elsewhere. The auth daemon would use a public key cryptography algorithm called RSA that would effectively solve this problem. The only way to register at the moment is at the forums. The daemon would allow users to register through a secure, RSA encrypted channel from inside the game.
Full backwards compatibility will be kept. Users of older versions of the client/server could continue using the existing list server except that its data layer would need to be changed to LDAP to share authentication data with the daemon. From what I was told this aspect could be implemented easily in the php list server rewrite that blast007 is working on.
II. Long term goals
Many of the list server functions are closely tied in with authentication. If the two are left as separate entities, they would need to communicate with eachother or duplicate some of the eachother's tasks. Also, the larger part of the load on the list server is probably not authentication. So there is little to no point in having the list server separate from the auth daemon and therefore the end goal is to have the daemon handle all functions that the list server does now.
Since that might be impossible given the time frame, I propose implementing the following:
- a solid foundation for the daemon that would handle the authentication related aspects fully
- complete integration with the client/server
- a menu for user registration in the client
- ability to choose between standard authentication and the daemon
Should I finish with those early, I will continue with the rest of the implementation:
- memory cached storage of the server list and a MySQL backend and replication
- permissions, group management, player tracking etc
- linking the authentication data to the server list
- additional chatter between the server/client/daemon
III. Detailed Description
III.1 Low level networking
The ServerLink/NetHandler classes are already written generic enough to be reused by the daemon to host TCP sessions. I propose moving these classes into their own library, as part of the net project and making specialized classes for non-generic code.
III.2 Authentication Process
Before the client connects to a server, it first connects to the daemon and initiates an RSA handshake. The daemon sends its public key to the client, which in turn encrypts the username and password and sends it. The daemon decrypts the message using its private key and replies with a random token generated for the client's new session and stores that token for future validation. Tokens could have a period after which they expire and may be invalidated after being verified by the server. The daemon could also periodically generate new public-private key pairs to maintain a decent level of security.
When connecting to the server, the client sends this token for it to verify. If the server requires global authentication, it connects to the daemon and passes the token along with the player's callsign and the daemon replies if the token is correct.
For in game registration, the client initiates an RSA session with the daemon in the same manner as described above and sends the encrypted registration data. After the daemon finishes the registration process it sends an error code to the client which displays a message accordingly. Errors that could occur are for example the callsign being already registered or the password being too short or if access to the LDAP server times out.
I propose using the primitives in the OpenSSL library for RSA. These are simple enough to use and provide everything we need.
Note that public key cryptography is necessary for registration because at that point there is no shared secret between the client and daemon yet. For authentication however, a simpler solution might also suffice like salted hashing.
If the client chooses not to use the new auth method, the token is retrieved as usual from the list server. When sending the token over, the client also tells the server to use the list server for validating the token.
III.3 LDAP backend
The daemon invokes functions from the OpenLDAP library. First it binds to the LDAP master server. When auth requests arrive it issues an asynchronous search operation. It queues the message identifier and periodically polls for results. When it gets a result it sends a reply to the client accordingly.
When register requests arrive first of all it checks if the callsign is already registered with a search operation. If that succeeds it carries on with an async modify operation. It queues this as well and polls for the result. If it fails for some reason it tries again a couple of times. If it times out it sends an error message, otherwise it signals a successful registration to the client.
If the search or modify operations return the LDAP_SERVER_DOWN error code for example, the daemon will fall back to one of the replicated slaves. It will cache the modify operations in memory until the master LDAP server comes back online and until then, search operations would first check the cache and then the slaves.
III.4 Client/server integration
Additional instances of ServerLink classes will be made in both the client and server when connecting to the daemon. For example a global authLink variable could exist for this purpose.
There could be a "Register" menu point in the main menu of the client that would bring up the registration menu. For starters this could ask for the user's desired callsign, password and an email address. Interfaces for changing username/password could also be considered.
Validation of the provided registration info is done by the daemon and a message is displayed once it responds, or a time out message if it doesn't. Depending on the error message, certain fields in the form could be highlighted.
III.6 Design goals
Since this is a project that will probably have plenty on the TODO list long after the summer deadline, a very important design goal will be for it to be easily maitainable. As such, I will try to code in a generic manner wherever possible, create clear/easy to use class interfaces and always think ahead for future expansion.
Settings like IPs of the LDAP servers, timeout delays, security options, minimum password length etc will be read from the command line and from a configuration file, in the same format as bzfs.conf for example.
All connections to the daemon as well as it's internal operations will be logged to a text file and to the console if desired.
June 16th: move the ServerLink/NetHandler code into its own lib
July 7th: create the foundation for the daemon that can accept connections from the client/server
July 14th: link OpenLDAP to the project, implement the LDAP backend for the daemon
July 21th: add the option to the client and server to connect to the daemon for authentication
July 28th: link OpenSSL to the projects, implement the RSA handshake and the auth chatter
August 4th: create registration menus in the client
August 11th: make use of RSA for sending the email address/password for the registration
August 18th: The implemented features should be tweaked/tested and most bugs fixed.
V. Work Log
June 30: So far I got some server framework basics laid down. I finished the RSA support code based on libgcrypt (instead of OpenSSL) and wrote C++ wrappers for it. LDAP has been tested but still needs more work. Some networking has been added but there's still plenty to do there as well.
July 8: Made lots of progress with the network code. Got the design laid out for sending, receiving packets, implemented some of the message handling on the daemon side too.
July 17: Fixed a few remaining problems with the netcode. Finished most of the registration. Did some tests and users are now properly added to the directory.
July 27: Moved bzauthd to it's own branch, cleaned up and reorganized the projects to allow code to be reused by the client later.
August 4: Restructured the netcode for allowing outbound connections and made use of it for basic client integration.
August 10: Finished writing a basic registration menu. Authentication while loading the server list and registration works now.
August 18: Finished writing the token validation for the server.