BZAuthd is the name being given to the game authorization and account management daemon, being implemented as a replacement to the current global account management system used in 2.0.
- 1 Problem Statement
- 2 Definitions
- 3 Design
- 4 Use Cases
- 5 Design Questions
Currently, BZFlag implements authorization in a mixed-mode fashion. The majority of authorization processes are carried out by querying the forum username / password database. Essentially, a player starts the game client, enters in their username and password info, and clicks the 'Connect' menu item. The client then contacts my.bzflag.org and transmits the username password pair, wherein the MySQL database for the forum is queried and my.bzflag.org transmits to the server if the client is identified.
The problem can be summed up as follows: there should be one 'unified' gatekeeper for any and all clients in the bzflag world (e.g. Bzflag, bzfs, web services, stats, etc). That gatekeeper should be able to query a singular point for validation and permissions associated to a particular client. This allows not only for easy maintenance, but also for easier administration of user actions (adding to groups, canceling accounts, removal of abusers, etc.) This system aims to also support 'passwordless' authentication and authorization of any given client, and will eventually support a 'karma' system of credibility for server and bzflag clients. The system should also support temporary, or anonymous, users for a period of time. Of course, anonymous servers (i.e. Servers that do not require the registration or the identification process), will exist, and thus people will play there who do not wish to run identified or authorized. Their accounts will be severely limited, and hopefully, those clients who wish to have a long-term presence in the bzflag community, will register, thus reaping the rewards of those associated actions.
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.
anything that needs read-write, read-only, or authorization services from my.bzflag.org
Essentially, the gatekeeper that talks to all clients. Responsible for negotiating the sending and receiving of tokens between client and server applications for purposes of joining a game, receiving a list of servers (and their capabilities), and registering new clients. Talks to all clients of the bzflag universe. Responds to server requests for user identity information and permissions. Informs bzflag clients of their permissions. Reads and writes to LDAP server and potentially certificate generating authority. Should serve as interim caching mechanism for writes to LDAP server in case main goes offline for whatever reason. Once main is online, cache pops data out in timed intervals to sync whatever data needs to be synced until cache is empty. INTERFACES WITH: LDAP, KARMA, BZFS, BZFLAG, CERTIFICATE GENERATOR (perhaps).
The main storage for all clients (bzfs clients, bzflag clients, web services (phpbb, drupal, etc), anything else). Should be broken down further into a 'main' repository (i.e. The master), and replications (read-only in case of failure of primary). INTERFACES WITH: BZAUTHD, KARMA, REPLICANTS, CERTIFICATE GENERATOR (perhaps).
The primary driver behind certificate generation and revocation for clients. Certificate generation for a user should give registered and validated users (whether a service or an actual player) some manner of positive karma because of that process. INTERFACES WITH: BZAUTHD, LDAP (see above 'perhaps'.)
The database that ties in with the Directory of clients that primary LDAP provides. 'Grades' clients based on a number of factors (registered / validated, 3rd party ratings, length of time in community and activity, volunteer activities, etc) and assigns appropriate pre-defined levels of positive karma. Distributes this information across the bzflag spectrum. INTERFACES WITH: BZAUTHD, LDAP, REPLICANTS.
The actual game client. Responsible for registering of players, joining servers, retrieval of server list (with associated server capabilities and restrictions), and game play. INTERFACES WITH: BZAUTHD, BZFS.
The game server. Responsible for verifying the identity of users, informing users if they need to register to play there, and keeping track of karmic issues. INTERFACES WITH: BZAUTHD, BZFLAG.
Primary BZFlag 'jumping off point' that aggregates all known official BZFlag entities in one easy to access, user-friendly area. Integrates the following items listed below. INTERFACES WITH: BZAUTHD, LDAP.
Community site that can also register clients. Talks to bzauthd and perhaps ldap directly for user certificates, etc. INTERFACES WITH: BZPORTAL.
Community site that tracks player and other client statistics (bzfs, primarily). Talks to bzauthd. Collects data from bzfs clients. INTERFACES WITH: BZPORTAL.
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.
The primitives in the OpenSSL library will be used 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.
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.
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.
Replication and Redundancy
The clients and servers could have a list of auth daemons to try and connect to. If one fails they try the next and so on.
External sources like forum registration can alter the LDAP data and thus caching reads would be impossible or at least error prone unless those external sources notify the daemons of these changes or do these through them. Even so, each instance of the daemon could have its own read cache, that does not need to be replicated.
The write cache for the case when the LDAP server goes down would need to be replicated however. Each daemon would need to send the writes to every other daemon (that is still active) for the caches to remain consistent.
When the LDAP master server comes online only one of them should commit the changes. One could establish a hierarchy between daemons and the highest one still active could do the job.
The packet format for all communication between client/server/daemons will be as follows:
- 4 byte packet header containing: a 2 byte opcode followed by the 2 byte data length
- the rest of the data, between 0 and a theoretical maximum of 65k bytes, but capped at 4096 bytes, this can be increased later if larger packets are needed
The information in the data field will be structured into:
- integer types of 1,2,4 or 8 bytes in size, sent in binary, little-endian, with no delimiter
- boolean type of 1 byte (0 - false, 1 - true)
- C strings, with a 0 delimiter at the end
- binary strings, preceded by a 2 byte length descriptor
Opcode descriptions and data contents:
The opcodes will be denoted by constants, they will be prefixed with either CMSG, SMSG or DMSG to denote whether they originated from a server a client or a daemon, or just MSG if it may originate from either of these. For inter-node communication, the additional prefixes might be used.
MSG_HANDSHAKE - first packet sent by both parties when opening any communication - Contains: 1 byte type id (0 - client/1 - server/2 - daemon) 2 byte protocol version number, x bytes of additional information based on the type and protocol version (e.g client version, daemon rank, etc)
Protocol version 1:
CMSG_HANDSHAKE - the client specific version of the handshake - Contains: the data from MSG_HANDSHAKE followed by a 4 byte integer for the client version and a 2 byte integer for the type of initial communication requested (can be 0 - auth, 1 - register get form, 2 - register request) - Note: the client may request other communication types without closing the connection later on
DMSG_HANDSHAKE - the daemon specific version of the handshake - Contains: the data from MSG_HANDSHAKE followed by a 4 byte integer containing the daemon version and a 2 byte integer denoting the daemon rank
SMSG_HANDSHAKE - the server specific version of the handshake - Contains: the data from MSG_HANDSHAKE followed by a 4 byte integer for the server version
CMSG_AUTH_REQUEST - this may be sent to notify the daemon that an auth is requested, but is not needed if this was already specified in the handshake - Contains: nothing
DMSG_AUTH_FAIL - sent after the auth request or response, if the auth failed for some reason (e.g unknown user name, incorrect password etc) - Contains: a 4 byte error code, perhaps followed by a C string with more information
DMSG_AUTH_CHALLENGE - sent after an auth request if it was accepted - Contains: a binary string containing the public key "n" value and a 2 byte integer containing the public key "e" value
CMSG_AUTH_RESPONSE - sent if the auth request was accepted, after the public key was received - Contains: binary string for an encrypted message containing the username and the password delimited by a space
DMSG_AUTH_SUCCESS - sent if the auth succeeded - Contains: 4 byte integer session token to be sent to a server
CMSG_REGISTER_GET_FORM - sent when the user tries to access the register menu, but is not needed if this was already specified in the handshake - Contains: nothing
DMSG_REGISTER_FAIL - sent whenever if getting the form failed or if the request was denied or if the challenge failed etc - Contains: a 4 byte error code, perhaps followed by a C string with more information
DMSG_REGISTER_SEND_FORM - sent after a form request - Contains: C String that describes the fields that the user is asked to complete
CMSG_REGISTER_REQUEST - sent when the user completed the form, but is not needed if this was already specified in the handshake - Contains: nothing
DMSG_REGISTER_CHALLENGE - sent if the provided preliminary information is valid - Contains: a binary string containing the public key "n" value and a 2 byte integer containing the public key "e" value
CMSG_REGISTER_RESPONSE - sent if the register request was accepted and a key was received - Contains: a binary string for an encrypted message contain the username, the password and other information, each delimited by a space
DMSG_REGISTER_SUCCESS - sent if the registration was successful - Contains: nothing
SMSG_TOKEN_VALIDATE - sent when the server wishes to validate tokens that it got from a client - Contains: a 1 byte integer "n" for the number of tokens to be validated followed by "n" 4 byte integer tokens
DMSG_TOKEN_VALIDATE - sent when the daemon processed a validation request - Contains: a 1 byte integer "n" for the number of tokens to be validated followed by "n" 4 byte integer error codes which are 0 where the the token was successfully validated
- LDAP SERVER (master) dies:
- BZAUTHd falls over to querying the LDAP replicants for player information.
- New player / client information is cached by BZAUTHd for later insertion into master LDAP directory.
- KARMA SERVER (master) dies:
- BZAUTHd falls over to querying Karma replicant(s) for client karma.
- BZAUTHD dies:
- Fall over to other hosted, trusted BZAUTHd process.
- Fail gracefully – no cache data blown away, etc.
- CERTIFICATE GENERATOR dies:
- Temporarily suspend new account creation.
- Limit new account creation to temporary account types only.
- Cache new account data, and resubmit when service is available.
- Should or can the Karma server and LDAP server be one and the same?
- PROVIDES: easier maintenance, both autonomously and manually
- PROVIDES: easier ability for maintaining a consistent data state (no fuzzy syncing issues – it either is or isn't synced with replicants)
- PROBLEMATIC: multiple areas of entry for possible abuse (unless replicants are hosted on 'trusted' systems, as far as that can be determined.)
- Should or can certificate generation be done by one of the preexisting processes?
- BZAUTHD (not sure)
- LDAP (likely if possible)
- KARMA (unlikely – doesn't seem to make logical sense)