Fossil handles user authentication using passwords. Passwords are unique to each repository. Passwords are not part of the persistent state of a project. Passwords are not versioned and are not transmitted from one repository to another during a sync. Passwords are local configuration information that can (and usually does) vary from one repository to the next within the same project.
Passwords are stored in the PW field of the USER table. In older versions of Fossil (prior to 2010-01-11) the password is stored as cleartext. In newer versions of Fossil, the password can be either cleartext or an SHA1 hash (written as a 40-character lower-case hexadecimal number). If the USER.PW field contains a 40-character string, that string is assumed to be a SHA1 hash. If the size of USER.PW is anything other than 40 characters, then it is understood as a plain-text password.
The SHA1 hash in the USER.PW field is a hash of a string composed of the project-code, the user login, and the user cleartext password. Suppose user "alice" with password "asdfg" had an account on the Fossil self-hosting repository. Then the value of USER.PW for alice would be the SHA1 hash of
That hash value is "f1b699cc9af3eeb98e5de244ca7802ae38e77bae". Note that by including the project-code and the login as part of the hash, a different USER.PW value results even if two or more users on the repository select the same "asdfg" password or if user alice reuses the same password on multiple projects.
Whenever a password is changed using the web interface or using the "user" command-line method, the new password is stored using the SHA1 encoding. Thus, cleartext passwords will gradually migrate to become SHA1 passwords. All remaining cleartext passwords can be converted to SHA1 passwords using the following command:
fossil test-hash-passwords REPOSITORY-NAME
Remember that converting from cleartext to SHA1 passwords is an irreversible operation.
The only way to insert a new cleartext password into the USER table is to do so manually using SQL commands. For example:
UPDATE user SET pw='asdfg' WHERE login='alice';
Note that an password that is an empty string or NULL will disable all login for that user. Thus, to lock a user out of the system, one has only to set their password to an empty string, using either the web interface or direct SQL manipulation of the USER table. Note also that the password field is essentially ignored for the special users named "anonymous", "developer", "reader", and "nobody". It is not possible to authenticate as users "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords.
Web Interface Authentication
When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. The server then hashes the password and compares it against the value stored in USER.PW. If they match, the server sets a cookie on the client to record the login. This cookie contains a large amount of high-quality randomness and is thus intractable to guess. The value of the cookie and the IP address of the client is stored in the USER.COOKIE and USER.IPADDR fields of the USER table on the server. The USER.CEXPIRE field holds an expiration date for the cookie, encoded as a julian day number. On all subsequent HTTP requests, the cookie value is matched against the USER table to enable access to the repository.
A login cookie will only work if the IP address matches. This feature is designed to make it more difficult for an attacker to sniff the cookie and take over the connection. A cookie-sniffing attack will only work if the attacker is able to send and receive from the same IP address as the original login. However, we found that doing an exact IP match caused problems for some users who are behind proxy firewalls where the proxy might use a different IP address for each query. To work around this problem, newer versions of fossil only check the first 16 bits of the 32-bit IP address. This makes a cookie sniffing attack easier since now the attacker only has to send and receive from any IP address in a range of IPs that are similar to the initial login. But that is seen as an acceptable compromise in exchange for ease of use. If higher security is really needed, then HTTPS can be used instead of HTTP.
Note that in order to log into a Fossil server, it is necessary to write information into the repository database. Hence, login is not possible on a Fossil repository with a read-only database file.
Sync Protocol Authentication
A different authentication mechanism is used when one repository wants to sync (or push or pull or clone) another repository. When two repositories are syncing, the one that initiates the transaction is the client and the repository that responds is the server. The client works by sending HTTP requests to the server with a method of "xfer" and a content-type of "application/x-fossil". The content is Zlib-compressed text consisting of "cards" of instructions. The first card of this content is a "login" card responsible for authentication. The login card contains the login name of the user and a "signature" where the signature is the SHA1 hash of a nonce and the value of USER.PW. The nonce is the SHA1 hash of the remainder of the request content after the newline (ASCII 14) character that terminates the login card.
Using this approach, the USER.PW value is treated as a shared secret between the client and server. The USER.PW value is never sent over the wire, but the protocol establishes that both client and server know the value of USER.PW. Furthermore, the use of a SHA1 hash over the entire message prevents an attacker from sniffing a valid login from a legitimate users and then replaying the message modified content.
If the USER.PW on the server holds a cleartext password, then the server will also accept a login-card signature that is constructed using either the cleartext password or the SHA1 hash of the password. This means that when USER.PW holds a cleartext password, the login card will work for both older and newer clients. If the USER.PW on the server only holds the SHA1 hash of the password, then only newer clients will be able to authenticate to the server.
The client normally gets the login and password from the "remote URL".
For older clients, the password is used for the shared secret as stated in the URL and with no encoding. For newer clients, the shared secret is derived from the password by transformed the password using the SHA1 hash encoding described above. However, if the first character of the password is "*" (ASCII 0x2a) then the "*" is skipped and the rest of the password is used directly as the share secret without the SHA1 encoding.
This *-before-the-password trick can be used by newer clients to sync against a legacy server that does not understand the new SHA1 password encoding.