|
1
|
<title>Fossil Password Management</title> |
|
2
|
|
|
3
|
Fossil handles user authentication using passwords. |
|
4
|
Passwords are unique to each repository. Passwords are not part of the |
|
5
|
persistent state of a project. Passwords are not versioned and |
|
6
|
are not transmitted from one repository to another during a sync. |
|
7
|
Passwords are local configuration information that can (and usually does) |
|
8
|
vary from one repository to the next within the same project. |
|
9
|
|
|
10
|
Passwords are stored in the PW field of the USER table. |
|
11
|
In older versions of Fossil (prior to |
|
12
|
[/timeline?c=2010-01-10T20:56:30 | 2010-01-11]) the password |
|
13
|
is stored as cleartext. In newer versions of Fossil, the password |
|
14
|
can be either cleartext or an SHA1 hash (written as a 40-character |
|
15
|
lower-case hexadecimal number). If the USER.PW field contains |
|
16
|
a 40-character string, that string is assumed to be a SHA1 hash. |
|
17
|
If the size of USER.PW is anything other than 40 characters, then |
|
18
|
it is understood as a plain-text password. |
|
19
|
|
|
20
|
The SHA1 hash in the USER.PW field is a hash of a string composed of |
|
21
|
the project-code, the user login, and the user cleartext password. |
|
22
|
Suppose user "alice" with password "asdfg" had an account on the |
|
23
|
Fossil self-hosting repository. Then the value of USER.PW |
|
24
|
for alice would be the SHA1 hash of |
|
25
|
|
|
26
|
<pre> |
|
27
|
CE59BB9F186226D80E49D1FA2DB29F935CCA0333/alice/asdfg |
|
28
|
</pre> |
|
29
|
|
|
30
|
Note that by including the project-code and the login as part of the |
|
31
|
hash, a different USER.PW value results even if two or more users on |
|
32
|
the repository select the same "asdfg" password or if user alice |
|
33
|
reuses the same password on multiple projects. |
|
34
|
|
|
35
|
Whenever a password is changed using the web interface or using the |
|
36
|
"user" command-line method, the new password is stored using the SHA1 |
|
37
|
encoding. Thus, cleartext passwords will gradually migrate to become |
|
38
|
SHA1 passwords. All remaining cleartext passwords can be converted to |
|
39
|
SHA1 passwords using the following command: |
|
40
|
|
|
41
|
<pre> |
|
42
|
fossil test-hash-passwords <i>REPOSITORY-NAME</i> |
|
43
|
</pre> |
|
44
|
|
|
45
|
Remember that converting from cleartext to SHA1 passwords is an |
|
46
|
irreversible operation. |
|
47
|
|
|
48
|
The only way to insert a new cleartext password into the USER table |
|
49
|
is to do so manually using SQL commands. For example: |
|
50
|
|
|
51
|
<pre> |
|
52
|
UPDATE user SET pw='asdfg' WHERE login='alice'; |
|
53
|
</pre> |
|
54
|
|
|
55
|
Note that an password that is an empty string or NULL will disable |
|
56
|
all login for that user. Thus, to lock a user out of the system, |
|
57
|
one has only to set their password to an empty string, using either |
|
58
|
the web interface or direct SQL manipulation of the USER table. |
|
59
|
Note also that the password field is |
|
60
|
essentially ignored for the special users named "anonymous", "developer", |
|
61
|
"reader", and "nobody". It is not possible to authenticate as users |
|
62
|
"developer", "reader", or "nobody" and the authentication protocol |
|
63
|
for "anonymous" uses one-time captchas not persistent passwords. |
|
64
|
|
|
65
|
<h2>Web Interface Authentication</h2> |
|
66
|
|
|
67
|
When a user logs into Fossil using the web interface, the login name |
|
68
|
and password are sent in the clear to the server. For most modern fossil |
|
69
|
server setups with [/help/redirect-to-https|redirect-to-https] enabled, |
|
70
|
this will be protected by the |
|
71
|
SSL connection over HTTPS so it cannot be easily viewed. The server then |
|
72
|
hashes the password and compares it against the value stored in USER.PW. |
|
73
|
If they match, the server sets a cookie on the client to record the |
|
74
|
login. This cookie contains a large amount of high-quality randomness |
|
75
|
and is thus intractable to guess. The value of the cookie and the IP |
|
76
|
address of the client is stored in the USER.COOKIE and USER.IPADDR fields |
|
77
|
of the USER table on the server. |
|
78
|
|
|
79
|
The USER.CEXPIRE field holds an expiration date |
|
80
|
for the cookie, encoded as a Julian day number. On all subsequent |
|
81
|
HTTP requests, the cookie value is matched against the USER table to |
|
82
|
enable access to the repository. |
|
83
|
|
|
84
|
Note that in order to log into a Fossil server, it is necessary to |
|
85
|
write information into the repository database. Hence, login is not |
|
86
|
possible on a Fossil repository with a read-only database file. |
|
87
|
|
|
88
|
The user password is sent over the wire as cleartext on the initial |
|
89
|
login attempt. The plan moving forward is to compute the SHA1 hash of |
|
90
|
the password on the client using JavaScript and then send only the hash |
|
91
|
over the wire, but that plan has not yet been set in code. |
|
92
|
|
|
93
|
<h2>Sync Protocol Authentication</h2> |
|
94
|
|
|
95
|
A different authentication mechanism is used when one repository wants |
|
96
|
to sync (or push or pull or clone) another repository. When two |
|
97
|
repositories are syncing, the one that initiates the transaction is |
|
98
|
the client and the repository that responds is the server. The client |
|
99
|
works by sending HTTP requests to the server with a method of "xfer" |
|
100
|
and a content-type of "application/x-fossil". The content is Zlib-compressed |
|
101
|
text consisting of "cards" of instructions. The first card of this content |
|
102
|
is a "login" card responsible for authentication. The login card contains |
|
103
|
the login name of the user and a "signature" where the signature is the |
|
104
|
SHA1 hash of a nonce and the value of USER.PW. The nonce is the |
|
105
|
SHA1 hash of the remainder of the request content after the newline |
|
106
|
(ASCII 14) character that terminates the login card. |
|
107
|
|
|
108
|
Using this approach, the USER.PW value is treated as a shared secret |
|
109
|
between the client and server. The USER.PW value is never sent over |
|
110
|
the wire, but the protocol establishes that both client and server know |
|
111
|
the value of USER.PW. Furthermore, the use of a SHA1 hash over the entire |
|
112
|
message prevents an attacker from sniffing a valid login from a legitimate |
|
113
|
users and then replaying the message modified content. |
|
114
|
|
|
115
|
If the USER.PW on the server holds a cleartext password, then the |
|
116
|
server will also accept a login-card signature that is constructed |
|
117
|
using either the cleartext password or the SHA1 hash of the password. |
|
118
|
This means that when USER.PW holds a cleartext password, the login card |
|
119
|
will work for both older and newer clients. If the USER.PW on the server |
|
120
|
only holds the SHA1 hash of the password, then only newer clients will be |
|
121
|
able to authenticate to the server. |
|
122
|
|
|
123
|
The client normally gets the login and password from the "remote URL". |
|
124
|
|
|
125
|
<pre> |
|
126
|
http://<span style="color:blue">login:password</span>@servername.org/path |
|
127
|
</pre> |
|
128
|
|
|
129
|
For older clients, the password is used for the shared secret as stated |
|
130
|
in the URL and with no encoding. |
|
131
|
For newer clients, the shared secret is derived from the password |
|
132
|
by transforming the password using the SHA1 hash encoding |
|
133
|
described above. However, if the first character of the password is |
|
134
|
"*" (ASCII 0x2a) then the "*" is skipped and the rest of the password |
|
135
|
is used directly as the share secret without the SHA1 encoding. |
|
136
|
|
|
137
|
<pre> |
|
138
|
http://<span style="color:blue">login:*password</span>@servername.org/path |
|
139
|
</pre> |
|
140
|
|
|
141
|
This *-before-the-password trick can be used by newer clients to |
|
142
|
sync against a legacy server that does not understand the new SHA1 |
|
143
|
password encoding. |
|
144
|
|