Fossil SCM

Merge the experimental password changes into the trunk.

drh 2010-01-12 13:55 trunk merge
Commit 596f3c10feba7ebe31b0aa46038214a748e2dbe8
+4 -1
--- src/cgi.c
+++ src/cgi.c
@@ -398,10 +398,13 @@
398398
aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
399399
if( aParamQP==0 ) exit(1);
400400
}
401401
aParamQP[nUsedQP].zName = zName;
402402
aParamQP[nUsedQP].zValue = zValue;
403
+ if( g.fHttpTrace ){
404
+ fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue);
405
+ }
403406
aParamQP[nUsedQP].seq = seqQP++;
404407
nUsedQP++;
405408
sortQP = 1;
406409
}
407410
@@ -1253,11 +1256,11 @@
12531256
}else{
12541257
close(0);
12551258
dup(connection);
12561259
close(1);
12571260
dup(connection);
1258
- if( !g.fHttpTrace ){
1261
+ if( !g.fHttpTrace && !g.fSqlTrace ){
12591262
close(2);
12601263
dup(connection);
12611264
}
12621265
close(connection);
12631266
return 0;
12641267
--- src/cgi.c
+++ src/cgi.c
@@ -398,10 +398,13 @@
398 aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
399 if( aParamQP==0 ) exit(1);
400 }
401 aParamQP[nUsedQP].zName = zName;
402 aParamQP[nUsedQP].zValue = zValue;
 
 
 
403 aParamQP[nUsedQP].seq = seqQP++;
404 nUsedQP++;
405 sortQP = 1;
406 }
407
@@ -1253,11 +1256,11 @@
1253 }else{
1254 close(0);
1255 dup(connection);
1256 close(1);
1257 dup(connection);
1258 if( !g.fHttpTrace ){
1259 close(2);
1260 dup(connection);
1261 }
1262 close(connection);
1263 return 0;
1264
--- src/cgi.c
+++ src/cgi.c
@@ -398,10 +398,13 @@
398 aParamQP = realloc( aParamQP, nAllocQP*sizeof(aParamQP[0]) );
399 if( aParamQP==0 ) exit(1);
400 }
401 aParamQP[nUsedQP].zName = zName;
402 aParamQP[nUsedQP].zValue = zValue;
403 if( g.fHttpTrace ){
404 fprintf(stderr, "# cgi: %s = [%s]\n", zName, zValue);
405 }
406 aParamQP[nUsedQP].seq = seqQP++;
407 nUsedQP++;
408 sortQP = 1;
409 }
410
@@ -1253,11 +1256,11 @@
1256 }else{
1257 close(0);
1258 dup(connection);
1259 close(1);
1260 dup(connection);
1261 if( !g.fHttpTrace && !g.fSqlTrace ){
1262 close(2);
1263 dup(connection);
1264 }
1265 close(connection);
1266 return 0;
1267
+10 -14
--- src/db.c
+++ src/db.c
@@ -1179,24 +1179,20 @@
11791179
/*
11801180
** This function registers auxiliary functions when the SQLite
11811181
** database connection is first established.
11821182
*/
11831183
LOCAL void db_connection_init(void){
1184
- static int once = 1;
1185
- if( once ){
1186
- sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1187
- sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
1188
- sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1189
- sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1190
- sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
1191
- sqlite3_create_function(
1192
- g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
1193
- );
1194
- if( g.fSqlTrace ){
1195
- sqlite3_trace(g.db, db_sql_trace, 0);
1196
- }
1197
- once = 0;
1184
+ sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1185
+ sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
1186
+ sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1187
+ sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1188
+ sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
1189
+ sqlite3_create_function(
1190
+ g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
1191
+ );
1192
+ if( g.fSqlTrace ){
1193
+ sqlite3_trace(g.db, db_sql_trace, 0);
11981194
}
11991195
}
12001196
12011197
/*
12021198
** Return true if the string zVal represents "true" (or "false").
12031199
--- src/db.c
+++ src/db.c
@@ -1179,24 +1179,20 @@
1179 /*
1180 ** This function registers auxiliary functions when the SQLite
1181 ** database connection is first established.
1182 */
1183 LOCAL void db_connection_init(void){
1184 static int once = 1;
1185 if( once ){
1186 sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1187 sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
1188 sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1189 sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1190 sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
1191 sqlite3_create_function(
1192 g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
1193 );
1194 if( g.fSqlTrace ){
1195 sqlite3_trace(g.db, db_sql_trace, 0);
1196 }
1197 once = 0;
1198 }
1199 }
1200
1201 /*
1202 ** Return true if the string zVal represents "true" (or "false").
1203
--- src/db.c
+++ src/db.c
@@ -1179,24 +1179,20 @@
1179 /*
1180 ** This function registers auxiliary functions when the SQLite
1181 ** database connection is first established.
1182 */
1183 LOCAL void db_connection_init(void){
1184 sqlite3_exec(g.db, "PRAGMA foreign_keys=OFF;", 0, 0, 0);
1185 sqlite3_create_function(g.db, "user", 0, SQLITE_ANY, 0, db_sql_user, 0, 0);
1186 sqlite3_create_function(g.db, "cgi", 1, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1187 sqlite3_create_function(g.db, "cgi", 2, SQLITE_ANY, 0, db_sql_cgi, 0, 0);
1188 sqlite3_create_function(g.db, "print", -1, SQLITE_UTF8, 0,db_sql_print,0,0);
1189 sqlite3_create_function(
1190 g.db, "file_is_selected", 1, SQLITE_UTF8, 0, file_is_selected,0,0
1191 );
1192 if( g.fSqlTrace ){
1193 sqlite3_trace(g.db, db_sql_trace, 0);
 
 
 
 
1194 }
1195 }
1196
1197 /*
1198 ** Return true if the string zVal represents "true" (or "false").
1199
+31 -11
--- src/http.c
+++ src/http.c
@@ -38,24 +38,25 @@
3838
**
3939
** Write the constructed login card into pLogin. pLogin is initialized
4040
** by this routine.
4141
*/
4242
static void http_build_login_card(Blob *pPayload, Blob *pLogin){
43
- Blob nonce; /* The nonce */
44
- Blob pw; /* The user password */
45
- Blob sig; /* The signature field */
43
+ Blob nonce; /* The nonce */
44
+ const char *zLogin; /* The user login name */
45
+ const char *zPw; /* The user password */
46
+ Blob pw; /* The nonce with user password appended */
47
+ Blob sig; /* The signature field */
4648
4749
blob_zero(&nonce);
4850
blob_zero(&pw);
4951
sha1sum_blob(pPayload, &nonce);
5052
blob_copy(&pw, &nonce);
5153
blob_zero(pLogin);
5254
if( g.urlUser==0 ){
5355
user_select();
54
- db_blob(&pw, "SELECT pw FROM user WHERE uid=%d", g.userUid);
55
- sha1sum_blob(&pw, &sig);
56
- blob_appendf(pLogin, "login %F %b %b\n", g.zLogin, &nonce, &sig);
56
+ zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", g.userUid);
57
+ zLogin = g.zLogin;
5758
}else{
5859
if( g.urlPasswd==0 ){
5960
if( strcmp(g.urlUser,"anonymous")!=0 ){
6061
char *zPrompt = mprintf("password for %s: ", g.urlUser);
6162
Blob x;
@@ -64,17 +65,36 @@
6465
g.urlPasswd = blob_str(&x);
6566
}else{
6667
g.urlPasswd = "";
6768
}
6869
}
69
- blob_append(&pw, g.urlPasswd, -1);
70
- sha1sum_blob(&pw, &sig);
71
- blob_appendf(pLogin, "login %F %b %b\n", g.urlUser, &nonce, &sig);
72
- }
73
- blob_reset(&nonce);
70
+ zPw = g.urlPasswd;
71
+ zLogin = g.urlUser;
72
+ }
73
+
74
+ /* The login card wants the SHA1 hash of the password, so convert the
75
+ ** password to its SHA1 hash it it isn't already a SHA1 hash.
76
+ **
77
+ ** Except, if the password begins with "*" then use the characters
78
+ ** after the "*" as a cleartext password. Put an "*" at the beginning
79
+ ** of the password to trick a newer client to use the cleartext password
80
+ ** protocol required by legacy servers.
81
+ */
82
+ if( zPw && zPw[0] ){
83
+ if( zPw[0]=='*' ){
84
+ zPw++;
85
+ }else{
86
+ zPw = sha1_shared_secret(zPw, zLogin);
87
+ }
88
+ }
89
+
90
+ blob_append(&pw, zPw, -1);
91
+ sha1sum_blob(&pw, &sig);
92
+ blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig);
7493
blob_reset(&pw);
7594
blob_reset(&sig);
95
+ blob_reset(&nonce);
7696
}
7797
7898
/*
7999
** Construct an appropriate HTTP request header. Write the header
80100
** into pHdr. This routine initializes the pHdr blob. pPayload is
81101
--- src/http.c
+++ src/http.c
@@ -38,24 +38,25 @@
38 **
39 ** Write the constructed login card into pLogin. pLogin is initialized
40 ** by this routine.
41 */
42 static void http_build_login_card(Blob *pPayload, Blob *pLogin){
43 Blob nonce; /* The nonce */
44 Blob pw; /* The user password */
45 Blob sig; /* The signature field */
 
 
46
47 blob_zero(&nonce);
48 blob_zero(&pw);
49 sha1sum_blob(pPayload, &nonce);
50 blob_copy(&pw, &nonce);
51 blob_zero(pLogin);
52 if( g.urlUser==0 ){
53 user_select();
54 db_blob(&pw, "SELECT pw FROM user WHERE uid=%d", g.userUid);
55 sha1sum_blob(&pw, &sig);
56 blob_appendf(pLogin, "login %F %b %b\n", g.zLogin, &nonce, &sig);
57 }else{
58 if( g.urlPasswd==0 ){
59 if( strcmp(g.urlUser,"anonymous")!=0 ){
60 char *zPrompt = mprintf("password for %s: ", g.urlUser);
61 Blob x;
@@ -64,17 +65,36 @@
64 g.urlPasswd = blob_str(&x);
65 }else{
66 g.urlPasswd = "";
67 }
68 }
69 blob_append(&pw, g.urlPasswd, -1);
70 sha1sum_blob(&pw, &sig);
71 blob_appendf(pLogin, "login %F %b %b\n", g.urlUser, &nonce, &sig);
72 }
73 blob_reset(&nonce);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74 blob_reset(&pw);
75 blob_reset(&sig);
 
76 }
77
78 /*
79 ** Construct an appropriate HTTP request header. Write the header
80 ** into pHdr. This routine initializes the pHdr blob. pPayload is
81
--- src/http.c
+++ src/http.c
@@ -38,24 +38,25 @@
38 **
39 ** Write the constructed login card into pLogin. pLogin is initialized
40 ** by this routine.
41 */
42 static void http_build_login_card(Blob *pPayload, Blob *pLogin){
43 Blob nonce; /* The nonce */
44 const char *zLogin; /* The user login name */
45 const char *zPw; /* The user password */
46 Blob pw; /* The nonce with user password appended */
47 Blob sig; /* The signature field */
48
49 blob_zero(&nonce);
50 blob_zero(&pw);
51 sha1sum_blob(pPayload, &nonce);
52 blob_copy(&pw, &nonce);
53 blob_zero(pLogin);
54 if( g.urlUser==0 ){
55 user_select();
56 zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", g.userUid);
57 zLogin = g.zLogin;
 
58 }else{
59 if( g.urlPasswd==0 ){
60 if( strcmp(g.urlUser,"anonymous")!=0 ){
61 char *zPrompt = mprintf("password for %s: ", g.urlUser);
62 Blob x;
@@ -64,17 +65,36 @@
65 g.urlPasswd = blob_str(&x);
66 }else{
67 g.urlPasswd = "";
68 }
69 }
70 zPw = g.urlPasswd;
71 zLogin = g.urlUser;
72 }
73
74 /* The login card wants the SHA1 hash of the password, so convert the
75 ** password to its SHA1 hash it it isn't already a SHA1 hash.
76 **
77 ** Except, if the password begins with "*" then use the characters
78 ** after the "*" as a cleartext password. Put an "*" at the beginning
79 ** of the password to trick a newer client to use the cleartext password
80 ** protocol required by legacy servers.
81 */
82 if( zPw && zPw[0] ){
83 if( zPw[0]=='*' ){
84 zPw++;
85 }else{
86 zPw = sha1_shared_secret(zPw, zLogin);
87 }
88 }
89
90 blob_append(&pw, zPw, -1);
91 sha1sum_blob(&pw, &sig);
92 blob_appendf(pLogin, "login %F %b %b\n", zLogin, &nonce, &sig);
93 blob_reset(&pw);
94 blob_reset(&sig);
95 blob_reset(&nonce);
96 }
97
98 /*
99 ** Construct an appropriate HTTP request header. Write the header
100 ** into pHdr. This routine initializes the pHdr blob. pPayload is
101
+12 -4
--- src/login.c
+++ src/login.c
@@ -140,10 +140,11 @@
140140
const char *zNew1, *zNew2;
141141
const char *zAnonPw = 0;
142142
int anonFlag;
143143
char *zErrMsg = "";
144144
int uid; /* User id loged in user */
145
+ char *zSha1Pw;
145146
146147
login_check_credentials();
147148
zUsername = P("u");
148149
zPasswd = P("p");
149150
anonFlag = P("anon")!=0;
@@ -151,12 +152,14 @@
151152
const char *zCookieName = login_cookie_name();
152153
cgi_set_cookie(zCookieName, "", 0, -86400);
153154
redirect_to_g();
154155
}
155156
if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
157
+ zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
156158
if( db_int(1, "SELECT 0 FROM user"
157
- " WHERE uid=%d AND pw=%Q", g.userUid, zPasswd) ){
159
+ " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
160
+ g.userUid, zPasswd, zSha1Pw) ){
158161
sleep(1);
159162
zErrMsg =
160163
@ <p><font color="red">
161164
@ You entered an incorrect old password while attempting to change
162165
@ your password. Your password is unchanged.
@@ -168,12 +171,13 @@
168171
@ The two copies of your new passwords do not match.
169172
@ Your password is unchanged.
170173
@ </font></p>
171174
;
172175
}else{
176
+ char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
173177
db_multi_exec(
174
- "UPDATE user SET pw=%Q WHERE uid=%d", zNew1, g.userUid
178
+ "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
175179
);
176180
redirect_to_g();
177181
return;
178182
}
179183
}
@@ -196,16 +200,17 @@
196200
free(zNow);
197201
cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
198202
redirect_to_g();
199203
}
200204
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
205
+ zSha1Pw = sha1_shared_secret(zPasswd, zUsername);
201206
uid = db_int(0,
202207
"SELECT uid FROM user"
203208
" WHERE login=%Q"
204209
" AND login NOT IN ('anonymous','nobody','developer','reader')"
205
- " AND pw=%Q",
206
- zUsername, zPasswd
210
+ " AND (pw=%Q OR pw=%Q)",
211
+ zUsername, zPasswd, zSha1Pw
207212
);
208213
if( uid<=0 ){
209214
sleep(1);
210215
zErrMsg =
211216
@ <p><font color="red">
@@ -420,10 +425,13 @@
420425
db_finalize(&s);
421426
if( zCap==0 ){
422427
zCap = "";
423428
}
424429
}
430
+ if( g.fHttpTrace && g.zLogin ){
431
+ fprintf(stderr, "# login: [%s] with capabilities [%s]\n", g.zLogin, zCap);
432
+ }
425433
426434
/* Set the global variables recording the userid and login. The
427435
** "nobody" user is a special case in that g.zLogin==0.
428436
*/
429437
g.userUid = uid;
430438
--- src/login.c
+++ src/login.c
@@ -140,10 +140,11 @@
140 const char *zNew1, *zNew2;
141 const char *zAnonPw = 0;
142 int anonFlag;
143 char *zErrMsg = "";
144 int uid; /* User id loged in user */
 
145
146 login_check_credentials();
147 zUsername = P("u");
148 zPasswd = P("p");
149 anonFlag = P("anon")!=0;
@@ -151,12 +152,14 @@
151 const char *zCookieName = login_cookie_name();
152 cgi_set_cookie(zCookieName, "", 0, -86400);
153 redirect_to_g();
154 }
155 if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
 
156 if( db_int(1, "SELECT 0 FROM user"
157 " WHERE uid=%d AND pw=%Q", g.userUid, zPasswd) ){
 
158 sleep(1);
159 zErrMsg =
160 @ <p><font color="red">
161 @ You entered an incorrect old password while attempting to change
162 @ your password. Your password is unchanged.
@@ -168,12 +171,13 @@
168 @ The two copies of your new passwords do not match.
169 @ Your password is unchanged.
170 @ </font></p>
171 ;
172 }else{
 
173 db_multi_exec(
174 "UPDATE user SET pw=%Q WHERE uid=%d", zNew1, g.userUid
175 );
176 redirect_to_g();
177 return;
178 }
179 }
@@ -196,16 +200,17 @@
196 free(zNow);
197 cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
198 redirect_to_g();
199 }
200 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
 
201 uid = db_int(0,
202 "SELECT uid FROM user"
203 " WHERE login=%Q"
204 " AND login NOT IN ('anonymous','nobody','developer','reader')"
205 " AND pw=%Q",
206 zUsername, zPasswd
207 );
208 if( uid<=0 ){
209 sleep(1);
210 zErrMsg =
211 @ <p><font color="red">
@@ -420,10 +425,13 @@
420 db_finalize(&s);
421 if( zCap==0 ){
422 zCap = "";
423 }
424 }
 
 
 
425
426 /* Set the global variables recording the userid and login. The
427 ** "nobody" user is a special case in that g.zLogin==0.
428 */
429 g.userUid = uid;
430
--- src/login.c
+++ src/login.c
@@ -140,10 +140,11 @@
140 const char *zNew1, *zNew2;
141 const char *zAnonPw = 0;
142 int anonFlag;
143 char *zErrMsg = "";
144 int uid; /* User id loged in user */
145 char *zSha1Pw;
146
147 login_check_credentials();
148 zUsername = P("u");
149 zPasswd = P("p");
150 anonFlag = P("anon")!=0;
@@ -151,12 +152,14 @@
152 const char *zCookieName = login_cookie_name();
153 cgi_set_cookie(zCookieName, "", 0, -86400);
154 redirect_to_g();
155 }
156 if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
157 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
158 if( db_int(1, "SELECT 0 FROM user"
159 " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
160 g.userUid, zPasswd, zSha1Pw) ){
161 sleep(1);
162 zErrMsg =
163 @ <p><font color="red">
164 @ You entered an incorrect old password while attempting to change
165 @ your password. Your password is unchanged.
@@ -168,12 +171,13 @@
171 @ The two copies of your new passwords do not match.
172 @ Your password is unchanged.
173 @ </font></p>
174 ;
175 }else{
176 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
177 db_multi_exec(
178 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
179 );
180 redirect_to_g();
181 return;
182 }
183 }
@@ -196,16 +200,17 @@
200 free(zNow);
201 cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
202 redirect_to_g();
203 }
204 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
205 zSha1Pw = sha1_shared_secret(zPasswd, zUsername);
206 uid = db_int(0,
207 "SELECT uid FROM user"
208 " WHERE login=%Q"
209 " AND login NOT IN ('anonymous','nobody','developer','reader')"
210 " AND (pw=%Q OR pw=%Q)",
211 zUsername, zPasswd, zSha1Pw
212 );
213 if( uid<=0 ){
214 sleep(1);
215 zErrMsg =
216 @ <p><font color="red">
@@ -420,10 +425,13 @@
425 db_finalize(&s);
426 if( zCap==0 ){
427 zCap = "";
428 }
429 }
430 if( g.fHttpTrace && g.zLogin ){
431 fprintf(stderr, "# login: [%s] with capabilities [%s]\n", g.zLogin, zCap);
432 }
433
434 /* Set the global variables recording the userid and login. The
435 ** "nobody" user is a special case in that g.zLogin==0.
436 */
437 g.userUid = uid;
438
+1 -1
--- src/main.c
+++ src/main.c
@@ -826,11 +826,11 @@
826826
if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
827827
fossil_fatal("unable to listen on TCP socket %d", iPort);
828828
}
829829
g.httpIn = stdin;
830830
g.httpOut = stdout;
831
- if( g.fHttpTrace ){
831
+ if( g.fHttpTrace || g.fSqlTrace ){
832832
fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
833833
}
834834
g.cgiPanic = 1;
835835
if( g.argc==2 ){
836836
db_must_be_within_tree();
837837
--- src/main.c
+++ src/main.c
@@ -826,11 +826,11 @@
826 if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
827 fossil_fatal("unable to listen on TCP socket %d", iPort);
828 }
829 g.httpIn = stdin;
830 g.httpOut = stdout;
831 if( g.fHttpTrace ){
832 fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
833 }
834 g.cgiPanic = 1;
835 if( g.argc==2 ){
836 db_must_be_within_tree();
837
--- src/main.c
+++ src/main.c
@@ -826,11 +826,11 @@
826 if( cgi_http_server(iPort, mxPort, zBrowserCmd) ){
827 fossil_fatal("unable to listen on TCP socket %d", iPort);
828 }
829 g.httpIn = stdin;
830 g.httpOut = stdout;
831 if( g.fHttpTrace || g.fSqlTrace ){
832 fprintf(stderr, "====== SERVER pid %d =======\n", getpid());
833 }
834 g.cgiPanic = 1;
835 if( g.argc==2 ){
836 db_must_be_within_tree();
837
--- src/schema.c
+++ src/schema.c
@@ -95,10 +95,17 @@
9595
@ nonce TEXT UNIQUE, -- Nonce used for login
9696
@ ipaddr TEXT -- Remote IP address. NULL for direct.
9797
@ );
9898
@
9999
@ -- Information about users
100
+@ --
101
+@ -- The user.pw field can be either cleartext of the password, or
102
+@ -- a SHA1 hash of the password. If the user.pw field is exactly 40
103
+@ -- characters long we assume it is a SHA1 hash. Otherwise, it is
104
+@ -- cleartext. The sha1_shared_secret() routine computes the password
105
+@ -- hash based on the project-code, the user login, and the cleartext
106
+@ -- password.
100107
@ --
101108
@ CREATE TABLE user(
102109
@ uid INTEGER PRIMARY KEY, -- User ID
103110
@ login TEXT, -- login name of the user
104111
@ pw TEXT, -- password
105112
--- src/schema.c
+++ src/schema.c
@@ -95,10 +95,17 @@
95 @ nonce TEXT UNIQUE, -- Nonce used for login
96 @ ipaddr TEXT -- Remote IP address. NULL for direct.
97 @ );
98 @
99 @ -- Information about users
 
 
 
 
 
 
 
100 @ --
101 @ CREATE TABLE user(
102 @ uid INTEGER PRIMARY KEY, -- User ID
103 @ login TEXT, -- login name of the user
104 @ pw TEXT, -- password
105
--- src/schema.c
+++ src/schema.c
@@ -95,10 +95,17 @@
95 @ nonce TEXT UNIQUE, -- Nonce used for login
96 @ ipaddr TEXT -- Remote IP address. NULL for direct.
97 @ );
98 @
99 @ -- Information about users
100 @ --
101 @ -- The user.pw field can be either cleartext of the password, or
102 @ -- a SHA1 hash of the password. If the user.pw field is exactly 40
103 @ -- characters long we assume it is a SHA1 hash. Otherwise, it is
104 @ -- cleartext. The sha1_shared_secret() routine computes the password
105 @ -- hash based on the project-code, the user login, and the cleartext
106 @ -- password.
107 @ --
108 @ CREATE TABLE user(
109 @ uid INTEGER PRIMARY KEY, -- User ID
110 @ login TEXT, -- login name of the user
111 @ pw TEXT, -- password
112
+6 -6
--- src/setup.c
+++ src/setup.c
@@ -318,14 +318,16 @@
318318
if( aw ){ zCap[i++] = 'w'; }
319319
if( az ){ zCap[i++] = 'z'; }
320320
321321
zCap[i] = 0;
322322
zPw = P("pw");
323
- if( !isValidPwString(zPw) ){
323
+ zLogin = P("login");
324
+ if( isValidPwString(zPw) ){
325
+ zPw = sha1_shared_secret(zPw, zLogin);
326
+ }else{
324327
zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
325328
}
326
- zLogin = P("login");
327329
if( uid>0 &&
328330
db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
329331
){
330332
style_header("User Creation Error");
331333
@ <font color="red">Login "%h(zLogin)" is already used by a different
@@ -473,14 +475,12 @@
473475
@ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
474476
@ </td>
475477
@ </tr>
476478
@ <tr>
477479
@ <td align="right">Password:</td>
478
- if( strcmp(zLogin, "anonymous")==0 ){
479
- @ <td><input type="text" name="pw" value="%h(zPw)"></td>
480
- }else if( zPw[0] ){
481
- /* Obscure the password for all other users */
480
+ if( zPw[0] ){
481
+ /* Obscure the password for all users */
482482
@ <td><input type="password" name="pw" value="**********"></td>
483483
}else{
484484
/* Show an empty password as an empty input field */
485485
@ <td><input type="password" name="pw" value=""></td>
486486
}
487487
--- src/setup.c
+++ src/setup.c
@@ -318,14 +318,16 @@
318 if( aw ){ zCap[i++] = 'w'; }
319 if( az ){ zCap[i++] = 'z'; }
320
321 zCap[i] = 0;
322 zPw = P("pw");
323 if( !isValidPwString(zPw) ){
 
 
 
324 zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
325 }
326 zLogin = P("login");
327 if( uid>0 &&
328 db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
329 ){
330 style_header("User Creation Error");
331 @ <font color="red">Login "%h(zLogin)" is already used by a different
@@ -473,14 +475,12 @@
473 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
474 @ </td>
475 @ </tr>
476 @ <tr>
477 @ <td align="right">Password:</td>
478 if( strcmp(zLogin, "anonymous")==0 ){
479 @ <td><input type="text" name="pw" value="%h(zPw)"></td>
480 }else if( zPw[0] ){
481 /* Obscure the password for all other users */
482 @ <td><input type="password" name="pw" value="**********"></td>
483 }else{
484 /* Show an empty password as an empty input field */
485 @ <td><input type="password" name="pw" value=""></td>
486 }
487
--- src/setup.c
+++ src/setup.c
@@ -318,14 +318,16 @@
318 if( aw ){ zCap[i++] = 'w'; }
319 if( az ){ zCap[i++] = 'z'; }
320
321 zCap[i] = 0;
322 zPw = P("pw");
323 zLogin = P("login");
324 if( isValidPwString(zPw) ){
325 zPw = sha1_shared_secret(zPw, zLogin);
326 }else{
327 zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
328 }
 
329 if( uid>0 &&
330 db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
331 ){
332 style_header("User Creation Error");
333 @ <font color="red">Login "%h(zLogin)" is already used by a different
@@ -473,14 +475,12 @@
475 @ <input type="checkbox" name="az"%s(oaz)/>%s(B('z'))Download Zip
476 @ </td>
477 @ </tr>
478 @ <tr>
479 @ <td align="right">Password:</td>
480 if( zPw[0] ){
481 /* Obscure the password for all users */
 
 
482 @ <td><input type="password" name="pw" value="**********"></td>
483 }else{
484 /* Show an empty password as an empty input field */
485 @ <td><input type="password" name="pw" value=""></td>
486 }
487
+59
--- src/sha1.c
+++ src/sha1.c
@@ -534,10 +534,69 @@
534534
SHA1Result(&ctx, zResult);
535535
DigestToBase16(zResult, blob_buffer(pCksum));
536536
return 0;
537537
}
538538
539
+/*
540
+** Compute the SHA1 checksum of a zero-terminated string. The
541
+** result is held in memory obtained from mprintf().
542
+*/
543
+char *sha1sum(const char *zIn){
544
+ SHA1Context ctx;
545
+ unsigned char zResult[20];
546
+ char zDigest[41];
547
+
548
+ SHA1Reset(&ctx);
549
+ SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn));
550
+ SHA1Result(&ctx, zResult);
551
+ DigestToBase16(zResult, zDigest);
552
+ return mprintf("%s", zDigest);
553
+}
554
+
555
+/*
556
+** Convert a cleartext password for a specific user into a SHA1 hash.
557
+**
558
+** The algorithm here is:
559
+**
560
+** SHA1( project-code + "/" + login + "/" + password )
561
+**
562
+** In words: The users login name and password are appended to the
563
+** project ID code and the SHA1 hash of the result is computed.
564
+**
565
+** The result of this function is the shared secret used by a client
566
+** to authenticate to a server for the sync protocol. It is also the
567
+** value stored in the USER.PW field of the database. By mixing in the
568
+** login name and the project id with the hash, different shared secrets
569
+** are obtained even if two users select the same password, or if a
570
+** single user selects the same password for multiple projects.
571
+*/
572
+char *sha1_shared_secret(const char *zPw, const char *zLogin){
573
+ static char *zProjectId = 0;
574
+ SHA1Context ctx;
575
+ unsigned char zResult[20];
576
+ char zDigest[41];
577
+
578
+ SHA1Reset(&ctx);
579
+ if( zProjectId==0 ){
580
+ zProjectId = db_get("project-code", 0);
581
+
582
+ /* On the first xfer request of a clone, the project-code is not yet
583
+ ** known. Use the cleartext password, since that is all we have.
584
+ */
585
+ if( zProjectId==0 ){
586
+ return mprintf("%s", zPw);
587
+ }
588
+ }
589
+ SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
590
+ SHA1Input(&ctx, (unsigned char*)"/", 1);
591
+ SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin));
592
+ SHA1Input(&ctx, (unsigned char*)"/", 1);
593
+ SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw));
594
+ SHA1Result(&ctx, zResult);
595
+ DigestToBase16(zResult, zDigest);
596
+ return mprintf("%s", zDigest);
597
+}
539598
540599
/*
541600
** COMMAND: sha1sum
542601
** %fossil sha1sum FILE...
543602
**
544603
--- src/sha1.c
+++ src/sha1.c
@@ -534,10 +534,69 @@
534 SHA1Result(&ctx, zResult);
535 DigestToBase16(zResult, blob_buffer(pCksum));
536 return 0;
537 }
538
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
539
540 /*
541 ** COMMAND: sha1sum
542 ** %fossil sha1sum FILE...
543 **
544
--- src/sha1.c
+++ src/sha1.c
@@ -534,10 +534,69 @@
534 SHA1Result(&ctx, zResult);
535 DigestToBase16(zResult, blob_buffer(pCksum));
536 return 0;
537 }
538
539 /*
540 ** Compute the SHA1 checksum of a zero-terminated string. The
541 ** result is held in memory obtained from mprintf().
542 */
543 char *sha1sum(const char *zIn){
544 SHA1Context ctx;
545 unsigned char zResult[20];
546 char zDigest[41];
547
548 SHA1Reset(&ctx);
549 SHA1Input(&ctx, (unsigned const char*)zIn, strlen(zIn));
550 SHA1Result(&ctx, zResult);
551 DigestToBase16(zResult, zDigest);
552 return mprintf("%s", zDigest);
553 }
554
555 /*
556 ** Convert a cleartext password for a specific user into a SHA1 hash.
557 **
558 ** The algorithm here is:
559 **
560 ** SHA1( project-code + "/" + login + "/" + password )
561 **
562 ** In words: The users login name and password are appended to the
563 ** project ID code and the SHA1 hash of the result is computed.
564 **
565 ** The result of this function is the shared secret used by a client
566 ** to authenticate to a server for the sync protocol. It is also the
567 ** value stored in the USER.PW field of the database. By mixing in the
568 ** login name and the project id with the hash, different shared secrets
569 ** are obtained even if two users select the same password, or if a
570 ** single user selects the same password for multiple projects.
571 */
572 char *sha1_shared_secret(const char *zPw, const char *zLogin){
573 static char *zProjectId = 0;
574 SHA1Context ctx;
575 unsigned char zResult[20];
576 char zDigest[41];
577
578 SHA1Reset(&ctx);
579 if( zProjectId==0 ){
580 zProjectId = db_get("project-code", 0);
581
582 /* On the first xfer request of a clone, the project-code is not yet
583 ** known. Use the cleartext password, since that is all we have.
584 */
585 if( zProjectId==0 ){
586 return mprintf("%s", zPw);
587 }
588 }
589 SHA1Input(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
590 SHA1Input(&ctx, (unsigned char*)"/", 1);
591 SHA1Input(&ctx, (unsigned char*)zLogin, strlen(zLogin));
592 SHA1Input(&ctx, (unsigned char*)"/", 1);
593 SHA1Input(&ctx, (unsigned const char*)zPw, strlen(zPw));
594 SHA1Result(&ctx, zResult);
595 DigestToBase16(zResult, zDigest);
596 return mprintf("%s", zDigest);
597 }
598
599 /*
600 ** COMMAND: sha1sum
601 ** %fossil sha1sum FILE...
602 **
603
+46 -3
--- src/user.c
+++ src/user.c
@@ -184,10 +184,11 @@
184184
usage("capabilities|default|list|new|password ...");
185185
}
186186
n = strlen(g.argv[2]);
187187
if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
188188
Blob passwd, login, contact;
189
+ char *zPw;
189190
190191
if( g.argc>=4 ){
191192
blob_init(&login, g.argv[3], -1);
192193
}else{
193194
prompt_user("login: ", &login);
@@ -203,15 +204,17 @@
203204
if( g.argc>=6 ){
204205
blob_init(&passwd, g.argv[5], -1);
205206
}else{
206207
prompt_for_password("password: ", &passwd, 1);
207208
}
209
+ zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
208210
db_multi_exec(
209211
"INSERT INTO user(login,pw,cap,info)"
210
- "VALUES(%B,%B,'v',%B)",
211
- &login, &passwd, &contact
212
+ "VALUES(%B,%Q,'v',%B)",
213
+ &login, zPw, &contact
212214
);
215
+ free(zPw);
213216
}else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
214217
user_select();
215218
if( g.argc==3 ){
216219
printf("%s\n", g.zLogin);
217220
}else{
@@ -247,11 +250,13 @@
247250
prompt_for_password(zPrompt, &pw, 1);
248251
}
249252
if( blob_size(&pw)==0 ){
250253
printf("password unchanged\n");
251254
}else{
252
- db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid);
255
+ char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3]);
256
+ db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
257
+ free(zSecret);
253258
}
254259
}else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
255260
int uid;
256261
if( g.argc!=4 && g.argc!=5 ){
257262
usage("user capabilities USERNAME ?PERMISSIONS?");
@@ -344,5 +349,43 @@
344349
);
345350
g.userUid = db_last_insert_rowid();
346351
g.zLogin = "anonymous";
347352
}
348353
}
354
+
355
+/*
356
+** Compute the shared secret for a user.
357
+*/
358
+static void user_sha1_shared_secret_func(
359
+ sqlite3_context *context,
360
+ int argc,
361
+ sqlite3_value **argv
362
+){
363
+ char *zPw;
364
+ char *zLogin;
365
+ assert( argc==2 );
366
+ zPw = (char*)sqlite3_value_text(argv[0]);
367
+ zLogin = (char*)sqlite3_value_text(argv[1]);
368
+ if( zPw && zLogin ){
369
+ sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin), -1, free);
370
+ }
371
+}
372
+
373
+/*
374
+** COMMAND: test-hash-passwords
375
+**
376
+** Usage: %fossil test-hash-passwords REPOSITORY
377
+**
378
+** Convert all local password storage to use a SHA1 hash of the password
379
+** rather than cleartext. Passwords that are already stored as the SHA1
380
+** has are unchanged.
381
+*/
382
+void user_hash_passwords_cmd(void){
383
+ if( g.argc!=3 ) usage("REPOSITORY");
384
+ db_open_repository(g.argv[2]);
385
+ sqlite3_create_function(g.db, "sha1_shared_secret", 2, SQLITE_UTF8, 0,
386
+ user_sha1_shared_secret_func, 0, 0);
387
+ db_multi_exec(
388
+ "UPDATE user SET pw=sha1_shared_secret(pw,login)"
389
+ " WHERE length(pw)>0 AND length(pw)!=40"
390
+ );
391
+}
349392
--- src/user.c
+++ src/user.c
@@ -184,10 +184,11 @@
184 usage("capabilities|default|list|new|password ...");
185 }
186 n = strlen(g.argv[2]);
187 if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
188 Blob passwd, login, contact;
 
189
190 if( g.argc>=4 ){
191 blob_init(&login, g.argv[3], -1);
192 }else{
193 prompt_user("login: ", &login);
@@ -203,15 +204,17 @@
203 if( g.argc>=6 ){
204 blob_init(&passwd, g.argv[5], -1);
205 }else{
206 prompt_for_password("password: ", &passwd, 1);
207 }
 
208 db_multi_exec(
209 "INSERT INTO user(login,pw,cap,info)"
210 "VALUES(%B,%B,'v',%B)",
211 &login, &passwd, &contact
212 );
 
213 }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
214 user_select();
215 if( g.argc==3 ){
216 printf("%s\n", g.zLogin);
217 }else{
@@ -247,11 +250,13 @@
247 prompt_for_password(zPrompt, &pw, 1);
248 }
249 if( blob_size(&pw)==0 ){
250 printf("password unchanged\n");
251 }else{
252 db_multi_exec("UPDATE user SET pw=%B WHERE uid=%d", &pw, uid);
 
 
253 }
254 }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
255 int uid;
256 if( g.argc!=4 && g.argc!=5 ){
257 usage("user capabilities USERNAME ?PERMISSIONS?");
@@ -344,5 +349,43 @@
344 );
345 g.userUid = db_last_insert_rowid();
346 g.zLogin = "anonymous";
347 }
348 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
--- src/user.c
+++ src/user.c
@@ -184,10 +184,11 @@
184 usage("capabilities|default|list|new|password ...");
185 }
186 n = strlen(g.argv[2]);
187 if( n>=2 && strncmp(g.argv[2],"new",n)==0 ){
188 Blob passwd, login, contact;
189 char *zPw;
190
191 if( g.argc>=4 ){
192 blob_init(&login, g.argv[3], -1);
193 }else{
194 prompt_user("login: ", &login);
@@ -203,15 +204,17 @@
204 if( g.argc>=6 ){
205 blob_init(&passwd, g.argv[5], -1);
206 }else{
207 prompt_for_password("password: ", &passwd, 1);
208 }
209 zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
210 db_multi_exec(
211 "INSERT INTO user(login,pw,cap,info)"
212 "VALUES(%B,%Q,'v',%B)",
213 &login, zPw, &contact
214 );
215 free(zPw);
216 }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
217 user_select();
218 if( g.argc==3 ){
219 printf("%s\n", g.zLogin);
220 }else{
@@ -247,11 +250,13 @@
250 prompt_for_password(zPrompt, &pw, 1);
251 }
252 if( blob_size(&pw)==0 ){
253 printf("password unchanged\n");
254 }else{
255 char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3]);
256 db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
257 free(zSecret);
258 }
259 }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
260 int uid;
261 if( g.argc!=4 && g.argc!=5 ){
262 usage("user capabilities USERNAME ?PERMISSIONS?");
@@ -344,5 +349,43 @@
349 );
350 g.userUid = db_last_insert_rowid();
351 g.zLogin = "anonymous";
352 }
353 }
354
355 /*
356 ** Compute the shared secret for a user.
357 */
358 static void user_sha1_shared_secret_func(
359 sqlite3_context *context,
360 int argc,
361 sqlite3_value **argv
362 ){
363 char *zPw;
364 char *zLogin;
365 assert( argc==2 );
366 zPw = (char*)sqlite3_value_text(argv[0]);
367 zLogin = (char*)sqlite3_value_text(argv[1]);
368 if( zPw && zLogin ){
369 sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin), -1, free);
370 }
371 }
372
373 /*
374 ** COMMAND: test-hash-passwords
375 **
376 ** Usage: %fossil test-hash-passwords REPOSITORY
377 **
378 ** Convert all local password storage to use a SHA1 hash of the password
379 ** rather than cleartext. Passwords that are already stored as the SHA1
380 ** has are unchanged.
381 */
382 void user_hash_passwords_cmd(void){
383 if( g.argc!=3 ) usage("REPOSITORY");
384 db_open_repository(g.argv[2]);
385 sqlite3_create_function(g.db, "sha1_shared_secret", 2, SQLITE_UTF8, 0,
386 user_sha1_shared_secret_func, 0, 0);
387 db_multi_exec(
388 "UPDATE user SET pw=sha1_shared_secret(pw,login)"
389 " WHERE length(pw)>0 AND length(pw)!=40"
390 );
391 }
392
+53 -36
--- src/xfer.c
+++ src/xfer.c
@@ -357,11 +357,10 @@
357357
blob_reset(&h2);
358358
blob_reset(&tail);
359359
return rc==0;
360360
}
361361
362
-
363362
/*
364363
** Check the signature on an application/x-fossil payload received by
365364
** the HTTP server. The signature is a line of the following form:
366365
**
367366
** login LOGIN NONCE SIGNATURE
@@ -394,28 +393,49 @@
394393
" AND login NOT IN ('anonymous','nobody','developer','reader')"
395394
" AND length(pw)>0",
396395
zLogin
397396
);
398397
if( db_step(&q)==SQLITE_ROW ){
398
+ int szPw;
399399
Blob pw, combined, hash;
400400
blob_zero(&pw);
401401
db_ephemeral_blob(&q, 0, &pw);
402
+ szPw = blob_size(&pw);
402403
blob_zero(&combined);
403404
blob_copy(&combined, pNonce);
404
- blob_append(&combined, blob_buffer(&pw), blob_size(&pw));
405
- /* CGIDEBUG(("presig=[%s]\n", blob_str(&combined))); */
405
+ blob_append(&combined, blob_buffer(&pw), szPw);
406406
sha1sum_blob(&combined, &hash);
407
+ assert( blob_size(&hash)==40 );
407408
rc = blob_compare(&hash, pSig);
408409
blob_reset(&hash);
409410
blob_reset(&combined);
411
+ if( rc!=0 && szPw!=40 ){
412
+ /* If this server stores cleartext passwords and the password did not
413
+ ** match, then perhaps the client is sending SHA1 passwords. Try
414
+ ** again with the SHA1 password.
415
+ */
416
+ const char *zPw = db_column_text(&q, 0);
417
+ char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin));
418
+ blob_zero(&combined);
419
+ blob_copy(&combined, pNonce);
420
+ blob_append(&combined, zSecret, -1);
421
+ free(zSecret);
422
+ sha1sum_blob(&combined, &hash);
423
+ rc = blob_compare(&hash, pSig);
424
+ blob_reset(&hash);
425
+ blob_reset(&combined);
426
+ }
410427
if( rc==0 ){
411428
const char *zCap;
412429
zCap = db_column_text(&q, 1);
413430
login_set_capabilities(zCap);
414431
g.userUid = db_column_int(&q, 2);
415432
g.zLogin = mprintf("%b", pLogin);
416433
g.zNonce = mprintf("%b", pNonce);
434
+ if( g.fHttpTrace ){
435
+ fprintf(stderr, "# login [%s] with capabilities [%s]\n", g.zLogin,zCap);
436
+ }
417437
}
418438
}
419439
db_finalize(&q);
420440
421441
if( rc==0 ){
@@ -650,41 +670,18 @@
650670
651671
/* pull SERVERCODE PROJECTCODE
652672
** push SERVERCODE PROJECTCODE
653673
**
654674
** The client wants either send or receive. The server should
655
- ** verify that the project code matches and that the server code
656
- ** does not match.
675
+ ** verify that the project code matches.
657676
*/
658677
if( xfer.nToken==3
659678
&& (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push"))
660679
&& blob_is_uuid(&xfer.aToken[1])
661680
&& blob_is_uuid(&xfer.aToken[2])
662681
){
663682
const char *zPCode;
664
-
665
-#if 0
666
- /* This block checks to see if a server is trying to sync with itself.
667
- ** This used to be disallowed, but I cannot think of any significant
668
- ** harm, so I have disabled the check.
669
- **
670
- ** With this check disabled, it is sufficient to copy the repository
671
- ** database. No need to run clone.
672
- */
673
- const char *zSCode;
674
- zSCode = db_get("server-code", 0);
675
- if( zSCode==0 ){
676
- fossil_panic("missing server code");
677
- }
678
- if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
679
- cgi_reset_content();
680
- @ error server\sloop
681
- nErr++;
682
- break;
683
- }
684
-#endif
685
-
686683
zPCode = db_get("project-code", 0);
687684
if( zPCode==0 ){
688685
fossil_panic("missing project code");
689686
}
690687
if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){
@@ -723,10 +720,11 @@
723720
*/
724721
if( blob_eq(&xfer.aToken[0], "clone") ){
725722
login_check_credentials();
726723
if( !g.okClone ){
727724
cgi_reset_content();
725
+ @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
728726
@ error not\sauthorized\sto\sclone
729727
nErr++;
730728
break;
731729
}
732730
isClone = 1;
@@ -960,11 +958,11 @@
960958
blobarray_zero(xfer.aToken, count(xfer.aToken));
961959
blob_zero(&send);
962960
blob_zero(&recv);
963961
blob_zero(&xfer.err);
964962
blob_zero(&xfer.line);
965
- origConfigRcvMask = configRcvMask;
963
+ origConfigRcvMask = 0;
966964
967965
/*
968966
** Always begin with a clone, pull, or push message
969967
*/
970968
if( cloneFlag ){
@@ -1005,12 +1003,15 @@
10051003
if( pushFlag ){
10061004
send_unsent(&xfer);
10071005
nCardSent += send_unclustered(&xfer);
10081006
}
10091007
1010
- /* Send configuration parameter requests */
1011
- if( configRcvMask ){
1008
+ /* Send configuration parameter requests. On a clone, delay sending
1009
+ ** this until the second cycle since the login card might fail on
1010
+ ** the first cycle.
1011
+ */
1012
+ if( configRcvMask && (cloneFlag==0 || nCycle>0) ){
10121013
const char *zName;
10131014
zName = configure_first_name(configRcvMask);
10141015
while( zName ){
10151016
blob_appendf(&send, "reqconfig %s\n", zName);
10161017
zName = configure_next_name(configRcvMask);
@@ -1017,10 +1018,11 @@
10171018
nCardSent++;
10181019
}
10191020
if( configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT) ){
10201021
configure_prepare_to_receive(0);
10211022
}
1023
+ origConfigRcvMask = configRcvMask;
10221024
configRcvMask = 0;
10231025
}
10241026
10251027
/* Send configuration parameters being pushed */
10261028
if( configSendMask ){
@@ -1032,11 +1034,14 @@
10321034
nCardSent++;
10331035
}
10341036
configSendMask = 0;
10351037
}
10361038
1037
- /* Append randomness to the end of the message */
1039
+ /* Append randomness to the end of the message. This makes all
1040
+ ** messages unique so that that the login-card nonce will always
1041
+ ** be unique.
1042
+ */
10381043
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
10391044
blob_appendf(&send, "# %s\n", zRandomness);
10401045
free(zRandomness);
10411046
10421047
/* Exchange messages with the server */
@@ -1213,17 +1218,26 @@
12131218
printf("\rServer says: %s\n", zMsg);
12141219
}else
12151220
12161221
/* error MESSAGE
12171222
**
1218
- ** Report an error and abandon the sync session
1223
+ ** Report an error and abandon the sync session.
1224
+ **
1225
+ ** Except, when cloning we will sometimes get an error on the
1226
+ ** first message exchange because the project-code is unknown
1227
+ ** and so the login card on the request was invalid. The project-code
1228
+ ** is returned in the reply before the error card, so second and
1229
+ ** subsequent messages should be OK. Nevertheless, we need to ignore
1230
+ ** the error card on the first message of a clone.
12191231
*/
12201232
if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1221
- char *zMsg = blob_terminate(&xfer.aToken[1]);
1222
- defossilize(zMsg);
1223
- blob_appendf(&xfer.err, "server says: %s", zMsg);
1224
- printf("Server Error: %s\n", zMsg);
1233
+ if( !cloneFlag || nCycle>0 ){
1234
+ char *zMsg = blob_terminate(&xfer.aToken[1]);
1235
+ defossilize(zMsg);
1236
+ blob_appendf(&xfer.err, "server says: %s", zMsg);
1237
+ printf("Server Error: %s\n", zMsg);
1238
+ }
12251239
}else
12261240
12271241
/* Unknown message */
12281242
{
12291243
if( blob_str(&xfer.aToken[0])[0]=='<' ){
@@ -1272,10 +1286,13 @@
12721286
** another round
12731287
*/
12741288
if( xfer.nFileSent+xfer.nDeltaSent>0 ){
12751289
go = 1;
12761290
}
1291
+
1292
+ /* If this is a clone, the go at least two rounds */
1293
+ if( cloneFlag && nCycle==1 ) go = 1;
12771294
};
12781295
transport_stats(&nSent, &nRcvd, 1);
12791296
printf("Total network traffic: %d bytes sent, %d bytes received\n",
12801297
nSent, nRcvd);
12811298
transport_close();
12821299
12831300
ADDED www/password.wiki
--- src/xfer.c
+++ src/xfer.c
@@ -357,11 +357,10 @@
357 blob_reset(&h2);
358 blob_reset(&tail);
359 return rc==0;
360 }
361
362
363 /*
364 ** Check the signature on an application/x-fossil payload received by
365 ** the HTTP server. The signature is a line of the following form:
366 **
367 ** login LOGIN NONCE SIGNATURE
@@ -394,28 +393,49 @@
394 " AND login NOT IN ('anonymous','nobody','developer','reader')"
395 " AND length(pw)>0",
396 zLogin
397 );
398 if( db_step(&q)==SQLITE_ROW ){
 
399 Blob pw, combined, hash;
400 blob_zero(&pw);
401 db_ephemeral_blob(&q, 0, &pw);
 
402 blob_zero(&combined);
403 blob_copy(&combined, pNonce);
404 blob_append(&combined, blob_buffer(&pw), blob_size(&pw));
405 /* CGIDEBUG(("presig=[%s]\n", blob_str(&combined))); */
406 sha1sum_blob(&combined, &hash);
 
407 rc = blob_compare(&hash, pSig);
408 blob_reset(&hash);
409 blob_reset(&combined);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
410 if( rc==0 ){
411 const char *zCap;
412 zCap = db_column_text(&q, 1);
413 login_set_capabilities(zCap);
414 g.userUid = db_column_int(&q, 2);
415 g.zLogin = mprintf("%b", pLogin);
416 g.zNonce = mprintf("%b", pNonce);
 
 
 
417 }
418 }
419 db_finalize(&q);
420
421 if( rc==0 ){
@@ -650,41 +670,18 @@
650
651 /* pull SERVERCODE PROJECTCODE
652 ** push SERVERCODE PROJECTCODE
653 **
654 ** The client wants either send or receive. The server should
655 ** verify that the project code matches and that the server code
656 ** does not match.
657 */
658 if( xfer.nToken==3
659 && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push"))
660 && blob_is_uuid(&xfer.aToken[1])
661 && blob_is_uuid(&xfer.aToken[2])
662 ){
663 const char *zPCode;
664
665 #if 0
666 /* This block checks to see if a server is trying to sync with itself.
667 ** This used to be disallowed, but I cannot think of any significant
668 ** harm, so I have disabled the check.
669 **
670 ** With this check disabled, it is sufficient to copy the repository
671 ** database. No need to run clone.
672 */
673 const char *zSCode;
674 zSCode = db_get("server-code", 0);
675 if( zSCode==0 ){
676 fossil_panic("missing server code");
677 }
678 if( blob_eq_str(&xfer.aToken[1], zSCode, -1) ){
679 cgi_reset_content();
680 @ error server\sloop
681 nErr++;
682 break;
683 }
684 #endif
685
686 zPCode = db_get("project-code", 0);
687 if( zPCode==0 ){
688 fossil_panic("missing project code");
689 }
690 if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){
@@ -723,10 +720,11 @@
723 */
724 if( blob_eq(&xfer.aToken[0], "clone") ){
725 login_check_credentials();
726 if( !g.okClone ){
727 cgi_reset_content();
 
728 @ error not\sauthorized\sto\sclone
729 nErr++;
730 break;
731 }
732 isClone = 1;
@@ -960,11 +958,11 @@
960 blobarray_zero(xfer.aToken, count(xfer.aToken));
961 blob_zero(&send);
962 blob_zero(&recv);
963 blob_zero(&xfer.err);
964 blob_zero(&xfer.line);
965 origConfigRcvMask = configRcvMask;
966
967 /*
968 ** Always begin with a clone, pull, or push message
969 */
970 if( cloneFlag ){
@@ -1005,12 +1003,15 @@
1005 if( pushFlag ){
1006 send_unsent(&xfer);
1007 nCardSent += send_unclustered(&xfer);
1008 }
1009
1010 /* Send configuration parameter requests */
1011 if( configRcvMask ){
 
 
 
1012 const char *zName;
1013 zName = configure_first_name(configRcvMask);
1014 while( zName ){
1015 blob_appendf(&send, "reqconfig %s\n", zName);
1016 zName = configure_next_name(configRcvMask);
@@ -1017,10 +1018,11 @@
1017 nCardSent++;
1018 }
1019 if( configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT) ){
1020 configure_prepare_to_receive(0);
1021 }
 
1022 configRcvMask = 0;
1023 }
1024
1025 /* Send configuration parameters being pushed */
1026 if( configSendMask ){
@@ -1032,11 +1034,14 @@
1032 nCardSent++;
1033 }
1034 configSendMask = 0;
1035 }
1036
1037 /* Append randomness to the end of the message */
 
 
 
1038 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1039 blob_appendf(&send, "# %s\n", zRandomness);
1040 free(zRandomness);
1041
1042 /* Exchange messages with the server */
@@ -1213,17 +1218,26 @@
1213 printf("\rServer says: %s\n", zMsg);
1214 }else
1215
1216 /* error MESSAGE
1217 **
1218 ** Report an error and abandon the sync session
 
 
 
 
 
 
 
1219 */
1220 if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1221 char *zMsg = blob_terminate(&xfer.aToken[1]);
1222 defossilize(zMsg);
1223 blob_appendf(&xfer.err, "server says: %s", zMsg);
1224 printf("Server Error: %s\n", zMsg);
 
 
1225 }else
1226
1227 /* Unknown message */
1228 {
1229 if( blob_str(&xfer.aToken[0])[0]=='<' ){
@@ -1272,10 +1286,13 @@
1272 ** another round
1273 */
1274 if( xfer.nFileSent+xfer.nDeltaSent>0 ){
1275 go = 1;
1276 }
 
 
 
1277 };
1278 transport_stats(&nSent, &nRcvd, 1);
1279 printf("Total network traffic: %d bytes sent, %d bytes received\n",
1280 nSent, nRcvd);
1281 transport_close();
1282
1283 DDED www/password.wiki
--- src/xfer.c
+++ src/xfer.c
@@ -357,11 +357,10 @@
357 blob_reset(&h2);
358 blob_reset(&tail);
359 return rc==0;
360 }
361
 
362 /*
363 ** Check the signature on an application/x-fossil payload received by
364 ** the HTTP server. The signature is a line of the following form:
365 **
366 ** login LOGIN NONCE SIGNATURE
@@ -394,28 +393,49 @@
393 " AND login NOT IN ('anonymous','nobody','developer','reader')"
394 " AND length(pw)>0",
395 zLogin
396 );
397 if( db_step(&q)==SQLITE_ROW ){
398 int szPw;
399 Blob pw, combined, hash;
400 blob_zero(&pw);
401 db_ephemeral_blob(&q, 0, &pw);
402 szPw = blob_size(&pw);
403 blob_zero(&combined);
404 blob_copy(&combined, pNonce);
405 blob_append(&combined, blob_buffer(&pw), szPw);
 
406 sha1sum_blob(&combined, &hash);
407 assert( blob_size(&hash)==40 );
408 rc = blob_compare(&hash, pSig);
409 blob_reset(&hash);
410 blob_reset(&combined);
411 if( rc!=0 && szPw!=40 ){
412 /* If this server stores cleartext passwords and the password did not
413 ** match, then perhaps the client is sending SHA1 passwords. Try
414 ** again with the SHA1 password.
415 */
416 const char *zPw = db_column_text(&q, 0);
417 char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin));
418 blob_zero(&combined);
419 blob_copy(&combined, pNonce);
420 blob_append(&combined, zSecret, -1);
421 free(zSecret);
422 sha1sum_blob(&combined, &hash);
423 rc = blob_compare(&hash, pSig);
424 blob_reset(&hash);
425 blob_reset(&combined);
426 }
427 if( rc==0 ){
428 const char *zCap;
429 zCap = db_column_text(&q, 1);
430 login_set_capabilities(zCap);
431 g.userUid = db_column_int(&q, 2);
432 g.zLogin = mprintf("%b", pLogin);
433 g.zNonce = mprintf("%b", pNonce);
434 if( g.fHttpTrace ){
435 fprintf(stderr, "# login [%s] with capabilities [%s]\n", g.zLogin,zCap);
436 }
437 }
438 }
439 db_finalize(&q);
440
441 if( rc==0 ){
@@ -650,41 +670,18 @@
670
671 /* pull SERVERCODE PROJECTCODE
672 ** push SERVERCODE PROJECTCODE
673 **
674 ** The client wants either send or receive. The server should
675 ** verify that the project code matches.
 
676 */
677 if( xfer.nToken==3
678 && (blob_eq(&xfer.aToken[0], "pull") || blob_eq(&xfer.aToken[0], "push"))
679 && blob_is_uuid(&xfer.aToken[1])
680 && blob_is_uuid(&xfer.aToken[2])
681 ){
682 const char *zPCode;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
683 zPCode = db_get("project-code", 0);
684 if( zPCode==0 ){
685 fossil_panic("missing project code");
686 }
687 if( !blob_eq_str(&xfer.aToken[2], zPCode, -1) ){
@@ -723,10 +720,11 @@
720 */
721 if( blob_eq(&xfer.aToken[0], "clone") ){
722 login_check_credentials();
723 if( !g.okClone ){
724 cgi_reset_content();
725 @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
726 @ error not\sauthorized\sto\sclone
727 nErr++;
728 break;
729 }
730 isClone = 1;
@@ -960,11 +958,11 @@
958 blobarray_zero(xfer.aToken, count(xfer.aToken));
959 blob_zero(&send);
960 blob_zero(&recv);
961 blob_zero(&xfer.err);
962 blob_zero(&xfer.line);
963 origConfigRcvMask = 0;
964
965 /*
966 ** Always begin with a clone, pull, or push message
967 */
968 if( cloneFlag ){
@@ -1005,12 +1003,15 @@
1003 if( pushFlag ){
1004 send_unsent(&xfer);
1005 nCardSent += send_unclustered(&xfer);
1006 }
1007
1008 /* Send configuration parameter requests. On a clone, delay sending
1009 ** this until the second cycle since the login card might fail on
1010 ** the first cycle.
1011 */
1012 if( configRcvMask && (cloneFlag==0 || nCycle>0) ){
1013 const char *zName;
1014 zName = configure_first_name(configRcvMask);
1015 while( zName ){
1016 blob_appendf(&send, "reqconfig %s\n", zName);
1017 zName = configure_next_name(configRcvMask);
@@ -1017,10 +1018,11 @@
1018 nCardSent++;
1019 }
1020 if( configRcvMask & (CONFIGSET_USER|CONFIGSET_TKT) ){
1021 configure_prepare_to_receive(0);
1022 }
1023 origConfigRcvMask = configRcvMask;
1024 configRcvMask = 0;
1025 }
1026
1027 /* Send configuration parameters being pushed */
1028 if( configSendMask ){
@@ -1032,11 +1034,14 @@
1034 nCardSent++;
1035 }
1036 configSendMask = 0;
1037 }
1038
1039 /* Append randomness to the end of the message. This makes all
1040 ** messages unique so that that the login-card nonce will always
1041 ** be unique.
1042 */
1043 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
1044 blob_appendf(&send, "# %s\n", zRandomness);
1045 free(zRandomness);
1046
1047 /* Exchange messages with the server */
@@ -1213,17 +1218,26 @@
1218 printf("\rServer says: %s\n", zMsg);
1219 }else
1220
1221 /* error MESSAGE
1222 **
1223 ** Report an error and abandon the sync session.
1224 **
1225 ** Except, when cloning we will sometimes get an error on the
1226 ** first message exchange because the project-code is unknown
1227 ** and so the login card on the request was invalid. The project-code
1228 ** is returned in the reply before the error card, so second and
1229 ** subsequent messages should be OK. Nevertheless, we need to ignore
1230 ** the error card on the first message of a clone.
1231 */
1232 if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
1233 if( !cloneFlag || nCycle>0 ){
1234 char *zMsg = blob_terminate(&xfer.aToken[1]);
1235 defossilize(zMsg);
1236 blob_appendf(&xfer.err, "server says: %s", zMsg);
1237 printf("Server Error: %s\n", zMsg);
1238 }
1239 }else
1240
1241 /* Unknown message */
1242 {
1243 if( blob_str(&xfer.aToken[0])[0]=='<' ){
@@ -1272,10 +1286,13 @@
1286 ** another round
1287 */
1288 if( xfer.nFileSent+xfer.nDeltaSent>0 ){
1289 go = 1;
1290 }
1291
1292 /* If this is a clone, the go at least two rounds */
1293 if( cloneFlag && nCycle==1 ) go = 1;
1294 };
1295 transport_stats(&nSent, &nRcvd, 1);
1296 printf("Total network traffic: %d bytes sent, %d bytes received\n",
1297 nSent, nRcvd);
1298 transport_close();
1299
1300 DDED www/password.wiki
--- a/www/password.wiki
+++ b/www/password.wiki
@@ -0,0 +1,66 @@
1
+<title>Fossil P<h1 align="center">Password Management</h1 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 v ersions of Fossil (prior+ | 2010-01-11]) the password
12
+is stored as cleartext. In newer versions of Fossil, the password
13
+can be either cleartext or an SHA1 hash (written as a 40-character
14
+lower-case hexadecimal number). If the USER.PW field contains
15
+a 40-character string, that string is assumed to be a SHA1 hash.
16
+If the size of USER.PW is anything other than 40 characters, then
17
+it is understood as a plain-text password.
18
+
19
+The SHA1 hash in the USER.PW field is a hash of a string composed of
20
+the project-code, the user login, and the user cleartext password.
21
+Suppose user "alice" with password "asdfg" had an account on the
22
+Fossil self-hosting repository. Then the vale of USER.PW
23
+for alice wblockquotld be the SHA1 fferent USER.PW bloctkquotmore users on
24
+the repository seThat hash value is "f1b699cc9af3eeb98e5de244ca7802ae38e77bae". Note
25
+ hash,
26
+ multiple projects.
27
+
28
+Whenever a pas the
29
+nterface or using the
30
+"user" comm reuses the
31
+ored using the SHA1
32
+encoding. Thus, cleartext passwords will gradually migrate to become
33
+SHA1 passwords. All remaning cleartext passwords can be convertmpossilowing command:
34
+
35
+<pre>
36
+fossil test-hash-passwords <i>REPOSITORY-NA to SHA1 passwords is an
37
+irblockquote>rreversible ojeration.
38
+
39
+The only wa</blockquotxt password into the USER table
40
+is to do so manually using SQL commands. user SEA login cookie will only work if the IP address matches. This feature
41
+is designed to make it more difficult for an attacker to sniff the cookie
42
+and take over the connection. A cookie-sniffing attack will only work
43
+if the attacker is able to send and receive from the same IP address as
44
+the original login. However, we found that doing an exact IP match
45
+caused problems for some users who are behind proxy firewalls where the proxy
46
+might use a different IP address for each query. To work around this
47
+problem, newer versions of fossil only check the first 16 bits of the
48
+32-bit IP address. This makes a cookie sniffing attack easier since now
49
+the attacker only has to send and receive from any IP address in a range
50
+of IPs that are similar to the initial login. But that is seen as an
51
+acceptable compromise in exchange for ease of use. If higher security
52
+is really needed, then HTTPS can be used instead of HTTP SET pw='asdfg' WHERE login='alice';
53
+</pre>
54
+
55
+Note that an password that is an empty string or NULL will disable
56
+ablockquote>Thus, to lock a user out of the s</blockquot 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 spository. When two
66
+res
--- a/www/password.wiki
+++ b/www/password.wiki
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/www/password.wiki
+++ b/www/password.wiki
@@ -0,0 +1,66 @@
1 <title>Fossil P<h1 align="center">Password Management</h1 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 v ersions of Fossil (prior+ | 2010-01-11]) the password
12 is stored as cleartext. In newer versions of Fossil, the password
13 can be either cleartext or an SHA1 hash (written as a 40-character
14 lower-case hexadecimal number). If the USER.PW field contains
15 a 40-character string, that string is assumed to be a SHA1 hash.
16 If the size of USER.PW is anything other than 40 characters, then
17 it is understood as a plain-text password.
18
19 The SHA1 hash in the USER.PW field is a hash of a string composed of
20 the project-code, the user login, and the user cleartext password.
21 Suppose user "alice" with password "asdfg" had an account on the
22 Fossil self-hosting repository. Then the vale of USER.PW
23 for alice wblockquotld be the SHA1 fferent USER.PW bloctkquotmore users on
24 the repository seThat hash value is "f1b699cc9af3eeb98e5de244ca7802ae38e77bae". Note
25 hash,
26 multiple projects.
27
28 Whenever a pas the
29 nterface or using the
30 "user" comm reuses the
31 ored using the SHA1
32 encoding. Thus, cleartext passwords will gradually migrate to become
33 SHA1 passwords. All remaning cleartext passwords can be convertmpossilowing command:
34
35 <pre>
36 fossil test-hash-passwords <i>REPOSITORY-NA to SHA1 passwords is an
37 irblockquote>rreversible ojeration.
38
39 The only wa</blockquotxt password into the USER table
40 is to do so manually using SQL commands. user SEA login cookie will only work if the IP address matches. This feature
41 is designed to make it more difficult for an attacker to sniff the cookie
42 and take over the connection. A cookie-sniffing attack will only work
43 if the attacker is able to send and receive from the same IP address as
44 the original login. However, we found that doing an exact IP match
45 caused problems for some users who are behind proxy firewalls where the proxy
46 might use a different IP address for each query. To work around this
47 problem, newer versions of fossil only check the first 16 bits of the
48 32-bit IP address. This makes a cookie sniffing attack easier since now
49 the attacker only has to send and receive from any IP address in a range
50 of IPs that are similar to the initial login. But that is seen as an
51 acceptable compromise in exchange for ease of use. If higher security
52 is really needed, then HTTPS can be used instead of HTTP SET pw='asdfg' WHERE login='alice';
53 </pre>
54
55 Note that an password that is an empty string or NULL will disable
56 ablockquote>Thus, to lock a user out of the s</blockquot 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 spository. When two
66 res

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button