Fossil SCM

Begin adding code to implement "login groups" which will (we hope) ultimately lead to a single sign-on capability.

drh 2011-04-08 17:08 UTC trunk
Commit eec32b99dd74d226824fff2aa585d1ac06024cdf
+1 -1
--- src/http.c
+++ src/http.c
@@ -75,11 +75,11 @@
7575
*/
7676
if( zPw && zPw[0] ){
7777
if( zPw[0]=='*' ){
7878
zPw++;
7979
}else{
80
- zPw = sha1_shared_secret(zPw, zLogin);
80
+ zPw = sha1_shared_secret(zPw, zLogin, 0);
8181
}
8282
}
8383
8484
blob_append(&pw, zPw, -1);
8585
sha1sum_blob(&pw, &sig);
8686
--- src/http.c
+++ src/http.c
@@ -75,11 +75,11 @@
75 */
76 if( zPw && zPw[0] ){
77 if( zPw[0]=='*' ){
78 zPw++;
79 }else{
80 zPw = sha1_shared_secret(zPw, zLogin);
81 }
82 }
83
84 blob_append(&pw, zPw, -1);
85 sha1sum_blob(&pw, &sig);
86
--- src/http.c
+++ src/http.c
@@ -75,11 +75,11 @@
75 */
76 if( zPw && zPw[0] ){
77 if( zPw[0]=='*' ){
78 zPw++;
79 }else{
80 zPw = sha1_shared_secret(zPw, zLogin, 0);
81 }
82 }
83
84 blob_append(&pw, zPw, -1);
85 sha1sum_blob(&pw, &sig);
86
+259 -6
--- src/login.c
+++ src/login.c
@@ -178,11 +178,11 @@
178178
const char *zCookieName = login_cookie_name();
179179
cgi_set_cookie(zCookieName, "", 0, -86400);
180180
redirect_to_g();
181181
}
182182
if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
183
- zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
183
+ zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
184184
if( db_int(1, "SELECT 0 FROM user"
185185
" WHERE uid=%d AND (pw=%Q OR pw=%Q)",
186186
g.userUid, zPasswd, zSha1Pw) ){
187187
sleep(1);
188188
zErrMsg =
@@ -197,16 +197,31 @@
197197
@ The two copies of your new passwords do not match.
198198
@ Your password is unchanged.
199199
@ </span></p>
200200
;
201201
}else{
202
- char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
202
+ char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
203
+ char *zChngPw;
204
+ char *zErr;
203205
db_multi_exec(
204206
"UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
205207
);
206
- redirect_to_g();
207
- return;
208
+ fossil_free(zNewPw);
209
+ zChngPw = mprintf(
210
+ "UPDATE user"
211
+ " SET pw=shared_secret(%Q,%Q,"
212
+ " (SELECT value FROM config WHERE name='project-code'))"
213
+ " WHERE login=%Q",
214
+ zNew1, g.zLogin, g.zLogin
215
+ );
216
+ if( login_group_sql(zChngPw, "<p>", "</p>\n", &zErr) ){
217
+ zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
218
+ fossil_free(zErr);
219
+ }else{
220
+ redirect_to_g();
221
+ return;
222
+ }
208223
}
209224
}
210225
zIpAddr = PD("REMOTE_ADDR","nil");
211226
uid = isValidAnonymousLogin(zUsername, zPasswd);
212227
if( uid>0 ){
@@ -226,11 +241,11 @@
226241
cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
227242
record_login_attempt("anonymous", zIpAddr, 1);
228243
redirect_to_g();
229244
}
230245
if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
231
- zSha1Pw = sha1_shared_secret(zPasswd, zUsername);
246
+ zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
232247
uid = db_int(0,
233248
"SELECT uid FROM user"
234249
" WHERE login=%Q"
235250
" AND login NOT IN ('anonymous','nobody','developer','reader')"
236251
" AND (pw=%Q OR pw=%Q)",
@@ -795,11 +810,11 @@
795810
* this %s(zUsername), or at least I don't know how to force it to.*/
796811
@ <p><span class="loginError">
797812
@ %s(zUsername) already exists.
798813
@ </span></p>
799814
}else{
800
- char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
815
+ char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
801816
int uid;
802817
char *zCookie;
803818
const char *zCookieName;
804819
const char *zExpire;
805820
int expires;
@@ -873,5 +888,243 @@
873888
@ </form>
874889
style_footer();
875890
876891
free(zCaptcha);
877892
}
893
+
894
+/*
895
+** Return an abbreviated project code.
896
+**
897
+** Memory is obtained from malloc.
898
+*/
899
+static char *abbreviated_project_code(const char *zFullCode){
900
+ return mprintf("%.16s", zFullCode);
901
+}
902
+
903
+/*
904
+** Run SQL on the repository database for every repository in our
905
+** login group. The SQL is run in a separate database connection.
906
+**
907
+** Any members of the login group whose repository database file
908
+** cannot be found is silently removed from the group.
909
+**
910
+** Error messages accumulate and are returned in *pzErrorMsg. The
911
+** memory used to hold these messages should be freed using
912
+** fossil_free() if one desired to avoid a memory leak. The
913
+** zPrefix and zSuffix strings surround each error message.
914
+**
915
+** Return the number of errors.
916
+*/
917
+int login_group_sql(
918
+ const char *zSql, /* The SQL to run */
919
+ const char *zPrefix, /* Prefix to each error message */
920
+ const char *zSuffix, /* Suffix to each error message */
921
+ char **pzErrorMsg /* Write error message here, if not NULL */
922
+){
923
+ sqlite3 *pPeer; /* Connection to another database */
924
+ int nErr = 0; /* Number of errors seen so far */
925
+ int rc; /* Result code from subroutine calls */
926
+ char *zErr; /* SQLite error text */
927
+ char *zSelfCode; /* Project code for ourself */
928
+ Blob err; /* Accumulate errors here */
929
+ Stmt q; /* Query of all peer-* entries in CONFIG */
930
+
931
+ if( zPrefix==0 ) zPrefix = "";
932
+ if( zSuffix==0 ) zSuffix = "";
933
+ if( pzErrorMsg ) *pzErrorMsg = 0;
934
+ zSelfCode = abbreviated_project_code(db_get("project-code", "x"));
935
+ blob_zero(&err);
936
+ db_prepare(&q,
937
+ "SELECT name, value FROM config"
938
+ " WHERE name GLOB 'peer-repo-*'"
939
+ " AND name <> 'peer-repo-%q'"
940
+ " ORDER BY +value",
941
+ zSelfCode
942
+ );
943
+ while( db_step(&q)==SQLITE_ROW ){
944
+ const char *zRepoName = db_column_text(&q, 1);
945
+ if( file_size(zRepoName)<0 ){
946
+ /* Silently remove non-existant repositories from the login group. */
947
+ const char *zLabel = db_column_text(&q, 0);
948
+ db_multi_exec(
949
+ "DELETE FROM config WHERE name GLOB 'peer-*-%q'",
950
+ &zLabel[10]
951
+ );
952
+ continue;
953
+ }
954
+ rc = sqlite3_open_v2(zRepoName, &pPeer, SQLITE_OPEN_READWRITE, 0);
955
+ if( rc!=SQLITE_OK ){
956
+ blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName,
957
+ sqlite3_errmsg(pPeer), zSuffix);
958
+ nErr++;
959
+ sqlite3_close(pPeer);
960
+ continue;
961
+ }
962
+ sqlite3_create_function(pPeer, "shared_secret", 3, SQLITE_UTF8,
963
+ 0, sha1_shared_secret_sql_function, 0, 0);
964
+ zErr = 0;
965
+ rc = sqlite3_exec(pPeer, zSql, 0, 0, &zErr);
966
+ if( zErr ){
967
+ blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName, zErr, zSuffix);
968
+ sqlite3_free(zErr);
969
+ nErr++;
970
+ }else if( rc!=SQLITE_OK ){
971
+ blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName,
972
+ sqlite3_errmsg(pPeer), zSuffix);
973
+ nErr++;
974
+ }
975
+ sqlite3_close(pPeer);
976
+ }
977
+ db_finalize(&q);
978
+ if( pzErrorMsg && blob_size(&err)>0 ){
979
+ *pzErrorMsg = fossil_strdup(blob_str(&err));
980
+ }
981
+ blob_reset(&err);
982
+ fossil_free(zSelfCode);
983
+ return nErr;
984
+}
985
+
986
+/*
987
+** Attempt to join a login-group.
988
+**
989
+** If problems arise, leave an error message in *pzErrMsg.
990
+*/
991
+void login_group_join(
992
+ const char *zRepo, /* Repository file in the login group */
993
+ const char *zLogin, /* Login name for the other repo */
994
+ const char *zPassword, /* Password to prove we are authorized to join */
995
+ const char *zNewName, /* Name of new login group if making a new one */
996
+ char **pzErrMsg /* Leave an error message here */
997
+){
998
+ Blob fullName; /* Blob for finding full pathnames */
999
+ sqlite3 *pOther; /* The other repository */
1000
+ int rc; /* Return code from sqlite3 functions */
1001
+ char *zOtherProjCode; /* Project code for pOther */
1002
+ char *zPwHash; /* Password hash on pOther */
1003
+ char *zSelfRepo; /* Name of our repository */
1004
+ char *zSelfLabel; /* Project-name for our repository */
1005
+ char *zSelfProjCode; /* Our project-code */
1006
+ char *zSql; /* SQL to run on all peers */
1007
+ const char *zSelf; /* The ATTACH name of our repository */
1008
+
1009
+ *pzErrMsg = 0; /* Default to no errors */
1010
+ zSelf = db_name("repository");
1011
+
1012
+ /* Get the full pathname of the other repository */
1013
+ file_canonical_name(zRepo, &fullName);
1014
+ zRepo = mprintf(blob_str(&fullName));
1015
+ blob_reset(&fullName);
1016
+
1017
+ /* Get the full pathname for our repository. Also the project code
1018
+ ** and project name for ourself. */
1019
+ file_canonical_name(g.zRepositoryName, &fullName);
1020
+ zSelfRepo = mprintf(blob_str(&fullName));
1021
+ blob_reset(&fullName);
1022
+ zSelfProjCode = db_get("project-code", "unknown");
1023
+ zSelfLabel = db_get("project-name", 0);
1024
+ if( zSelfLabel==0 ){
1025
+ zSelfLabel = zSelfProjCode;
1026
+ }
1027
+
1028
+ /* Make sure we are not trying to join ourselves */
1029
+ if( strcmp(zRepo, zSelfRepo)==0 ){
1030
+ *pzErrMsg = mprintf("The \"other\" repository is the same as this one.");
1031
+ return;
1032
+ }
1033
+
1034
+ /* Make sure the other repository is a valid Fossil database */
1035
+ if( file_size(zRepo)<0 ){
1036
+ *pzErrMsg = mprintf("repository file \"%s\" does not exist", zRepo);
1037
+ return;
1038
+ }
1039
+ rc = sqlite3_open(zRepo, &pOther);
1040
+ if( rc!=SQLITE_OK ){
1041
+ *pzErrMsg = mprintf(sqlite3_errmsg(pOther));
1042
+ }else{
1043
+ rc = sqlite3_exec(pOther, "SELECT count(*) FROM user", 0, 0, pzErrMsg);
1044
+ }
1045
+ sqlite3_close(pOther);
1046
+ if( rc ) return;
1047
+
1048
+ /* Attach the other respository. Make sure the username/password is
1049
+ ** valid and has Setup permission.
1050
+ */
1051
+ db_multi_exec("ATTACH %Q AS other", zRepo);
1052
+ zOtherProjCode = db_text("x", "SELECT value FROM other.config"
1053
+ " WHERE name='project-code'");
1054
+ zPwHash = sha1_shared_secret(zPassword, zLogin, zOtherProjCode);
1055
+ if( !db_exists(
1056
+ "SELECT 1 FROM other.user"
1057
+ " WHERE login=%Q AND cap GLOB '*s*'"
1058
+ " AND (pw=%Q OR pw=%Q)",
1059
+ zLogin, zPassword, zPwHash)
1060
+ ){
1061
+ db_multi_exec("DETACH other");
1062
+ *pzErrMsg = "The supplied username/password does not correspond to a"
1063
+ " user Setup permission on the other repository.";
1064
+ return;
1065
+ }
1066
+
1067
+ /* Create all the necessary CONFIG table entries on both the
1068
+ ** other repository and on our own repository.
1069
+ */
1070
+ zSelfProjCode = abbreviated_project_code(zSelfProjCode);
1071
+ db_begin_transaction();
1072
+ db_multi_exec(
1073
+ "DELETE FROM %s.config WHERE name GLOB 'peer-*';"
1074
+ "INSERT INTO %s.config(name,value) VALUES('peer-repo-%s',%Q);"
1075
+ "INSERT INTO %s.config(name,value) "
1076
+ " SELECT 'peer-name-%q', value FROM other.config"
1077
+ " WHERE name='project-name';",
1078
+ zSelf,
1079
+ zSelf, zOtherProjCode, zRepo,
1080
+ zSelf, zOtherProjCode
1081
+ );
1082
+ db_multi_exec(
1083
+ "INSERT OR IGNORE INTO other.config(name,value)"
1084
+ " VALUES('login-group-name',%Q);",
1085
+ zNewName
1086
+ );
1087
+ db_multi_exec(
1088
+ "REPLACE INTO %s.config(name,value)"
1089
+ " SELECT name, value FROM other.config"
1090
+ " WHERE name GLOB 'peer-*' OR name='login-group-name'",
1091
+ zSelf
1092
+ );
1093
+ db_end_transaction(0);
1094
+ db_multi_exec("DETACH other");
1095
+
1096
+ /* Propagate the changes to all other members of the login-group */
1097
+ zSql = mprintf(
1098
+ "BEGIN;"
1099
+ "REPLACE INTO config(name, value) VALUES('peer-name-%q', %Q);"
1100
+ "REPLACE INTO config(name, value) VALUES('peer-repo-%q', %Q);"
1101
+ "COMMIT;",
1102
+ zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo
1103
+ );
1104
+ login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
1105
+ fossil_free(zSql);
1106
+}
1107
+
1108
+/*
1109
+** Leave the login group that we are currently part of.
1110
+*/
1111
+void login_group_leave(char **pzErrMsg){
1112
+ char *zProjCode;
1113
+ char *zSql;
1114
+
1115
+ *pzErrMsg = 0;
1116
+ zProjCode = abbreviated_project_code(db_get("project-code","x"));
1117
+ zSql = mprintf(
1118
+ "DELETE FROM config WHERE name GLOB 'peer-*-%q';"
1119
+ "DELETE FROM config"
1120
+ " WHERE name='login-group-name'"
1121
+ " AND (SELECT count(*) FROM config WHERE name GLOB 'peer-*')==0;",
1122
+ zProjCode
1123
+ );
1124
+ fossil_free(zProjCode);
1125
+ login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
1126
+ fossil_free(zSql);
1127
+ db_multi_exec(
1128
+ "DELETE FROM config WHERE name GLOB 'peer-*' OR name='login-group-name';"
1129
+ );
1130
+}
8781131
--- src/login.c
+++ src/login.c
@@ -178,11 +178,11 @@
178 const char *zCookieName = login_cookie_name();
179 cgi_set_cookie(zCookieName, "", 0, -86400);
180 redirect_to_g();
181 }
182 if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
183 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin);
184 if( db_int(1, "SELECT 0 FROM user"
185 " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
186 g.userUid, zPasswd, zSha1Pw) ){
187 sleep(1);
188 zErrMsg =
@@ -197,16 +197,31 @@
197 @ The two copies of your new passwords do not match.
198 @ Your password is unchanged.
199 @ </span></p>
200 ;
201 }else{
202 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin);
 
 
203 db_multi_exec(
204 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
205 );
206 redirect_to_g();
207 return;
 
 
 
 
 
 
 
 
 
 
 
 
 
208 }
209 }
210 zIpAddr = PD("REMOTE_ADDR","nil");
211 uid = isValidAnonymousLogin(zUsername, zPasswd);
212 if( uid>0 ){
@@ -226,11 +241,11 @@
226 cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
227 record_login_attempt("anonymous", zIpAddr, 1);
228 redirect_to_g();
229 }
230 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
231 zSha1Pw = sha1_shared_secret(zPasswd, zUsername);
232 uid = db_int(0,
233 "SELECT uid FROM user"
234 " WHERE login=%Q"
235 " AND login NOT IN ('anonymous','nobody','developer','reader')"
236 " AND (pw=%Q OR pw=%Q)",
@@ -795,11 +810,11 @@
795 * this %s(zUsername), or at least I don't know how to force it to.*/
796 @ <p><span class="loginError">
797 @ %s(zUsername) already exists.
798 @ </span></p>
799 }else{
800 char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
801 int uid;
802 char *zCookie;
803 const char *zCookieName;
804 const char *zExpire;
805 int expires;
@@ -873,5 +888,243 @@
873 @ </form>
874 style_footer();
875
876 free(zCaptcha);
877 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
878
--- src/login.c
+++ src/login.c
@@ -178,11 +178,11 @@
178 const char *zCookieName = login_cookie_name();
179 cgi_set_cookie(zCookieName, "", 0, -86400);
180 redirect_to_g();
181 }
182 if( g.okPassword && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
183 zSha1Pw = sha1_shared_secret(zPasswd, g.zLogin, 0);
184 if( db_int(1, "SELECT 0 FROM user"
185 " WHERE uid=%d AND (pw=%Q OR pw=%Q)",
186 g.userUid, zPasswd, zSha1Pw) ){
187 sleep(1);
188 zErrMsg =
@@ -197,16 +197,31 @@
197 @ The two copies of your new passwords do not match.
198 @ Your password is unchanged.
199 @ </span></p>
200 ;
201 }else{
202 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
203 char *zChngPw;
204 char *zErr;
205 db_multi_exec(
206 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
207 );
208 fossil_free(zNewPw);
209 zChngPw = mprintf(
210 "UPDATE user"
211 " SET pw=shared_secret(%Q,%Q,"
212 " (SELECT value FROM config WHERE name='project-code'))"
213 " WHERE login=%Q",
214 zNew1, g.zLogin, g.zLogin
215 );
216 if( login_group_sql(zChngPw, "<p>", "</p>\n", &zErr) ){
217 zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
218 fossil_free(zErr);
219 }else{
220 redirect_to_g();
221 return;
222 }
223 }
224 }
225 zIpAddr = PD("REMOTE_ADDR","nil");
226 uid = isValidAnonymousLogin(zUsername, zPasswd);
227 if( uid>0 ){
@@ -226,11 +241,11 @@
241 cgi_set_cookie(zCookieName, zCookie, 0, 6*3600);
242 record_login_attempt("anonymous", zIpAddr, 1);
243 redirect_to_g();
244 }
245 if( zUsername!=0 && zPasswd!=0 && zPasswd[0]!=0 ){
246 zSha1Pw = sha1_shared_secret(zPasswd, zUsername, 0);
247 uid = db_int(0,
248 "SELECT uid FROM user"
249 " WHERE login=%Q"
250 " AND login NOT IN ('anonymous','nobody','developer','reader')"
251 " AND (pw=%Q OR pw=%Q)",
@@ -795,11 +810,11 @@
810 * this %s(zUsername), or at least I don't know how to force it to.*/
811 @ <p><span class="loginError">
812 @ %s(zUsername) already exists.
813 @ </span></p>
814 }else{
815 char *zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
816 int uid;
817 char *zCookie;
818 const char *zCookieName;
819 const char *zExpire;
820 int expires;
@@ -873,5 +888,243 @@
888 @ </form>
889 style_footer();
890
891 free(zCaptcha);
892 }
893
894 /*
895 ** Return an abbreviated project code.
896 **
897 ** Memory is obtained from malloc.
898 */
899 static char *abbreviated_project_code(const char *zFullCode){
900 return mprintf("%.16s", zFullCode);
901 }
902
903 /*
904 ** Run SQL on the repository database for every repository in our
905 ** login group. The SQL is run in a separate database connection.
906 **
907 ** Any members of the login group whose repository database file
908 ** cannot be found is silently removed from the group.
909 **
910 ** Error messages accumulate and are returned in *pzErrorMsg. The
911 ** memory used to hold these messages should be freed using
912 ** fossil_free() if one desired to avoid a memory leak. The
913 ** zPrefix and zSuffix strings surround each error message.
914 **
915 ** Return the number of errors.
916 */
917 int login_group_sql(
918 const char *zSql, /* The SQL to run */
919 const char *zPrefix, /* Prefix to each error message */
920 const char *zSuffix, /* Suffix to each error message */
921 char **pzErrorMsg /* Write error message here, if not NULL */
922 ){
923 sqlite3 *pPeer; /* Connection to another database */
924 int nErr = 0; /* Number of errors seen so far */
925 int rc; /* Result code from subroutine calls */
926 char *zErr; /* SQLite error text */
927 char *zSelfCode; /* Project code for ourself */
928 Blob err; /* Accumulate errors here */
929 Stmt q; /* Query of all peer-* entries in CONFIG */
930
931 if( zPrefix==0 ) zPrefix = "";
932 if( zSuffix==0 ) zSuffix = "";
933 if( pzErrorMsg ) *pzErrorMsg = 0;
934 zSelfCode = abbreviated_project_code(db_get("project-code", "x"));
935 blob_zero(&err);
936 db_prepare(&q,
937 "SELECT name, value FROM config"
938 " WHERE name GLOB 'peer-repo-*'"
939 " AND name <> 'peer-repo-%q'"
940 " ORDER BY +value",
941 zSelfCode
942 );
943 while( db_step(&q)==SQLITE_ROW ){
944 const char *zRepoName = db_column_text(&q, 1);
945 if( file_size(zRepoName)<0 ){
946 /* Silently remove non-existant repositories from the login group. */
947 const char *zLabel = db_column_text(&q, 0);
948 db_multi_exec(
949 "DELETE FROM config WHERE name GLOB 'peer-*-%q'",
950 &zLabel[10]
951 );
952 continue;
953 }
954 rc = sqlite3_open_v2(zRepoName, &pPeer, SQLITE_OPEN_READWRITE, 0);
955 if( rc!=SQLITE_OK ){
956 blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName,
957 sqlite3_errmsg(pPeer), zSuffix);
958 nErr++;
959 sqlite3_close(pPeer);
960 continue;
961 }
962 sqlite3_create_function(pPeer, "shared_secret", 3, SQLITE_UTF8,
963 0, sha1_shared_secret_sql_function, 0, 0);
964 zErr = 0;
965 rc = sqlite3_exec(pPeer, zSql, 0, 0, &zErr);
966 if( zErr ){
967 blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName, zErr, zSuffix);
968 sqlite3_free(zErr);
969 nErr++;
970 }else if( rc!=SQLITE_OK ){
971 blob_appendf(&err, "%s%s: %s%s", zPrefix, zRepoName,
972 sqlite3_errmsg(pPeer), zSuffix);
973 nErr++;
974 }
975 sqlite3_close(pPeer);
976 }
977 db_finalize(&q);
978 if( pzErrorMsg && blob_size(&err)>0 ){
979 *pzErrorMsg = fossil_strdup(blob_str(&err));
980 }
981 blob_reset(&err);
982 fossil_free(zSelfCode);
983 return nErr;
984 }
985
986 /*
987 ** Attempt to join a login-group.
988 **
989 ** If problems arise, leave an error message in *pzErrMsg.
990 */
991 void login_group_join(
992 const char *zRepo, /* Repository file in the login group */
993 const char *zLogin, /* Login name for the other repo */
994 const char *zPassword, /* Password to prove we are authorized to join */
995 const char *zNewName, /* Name of new login group if making a new one */
996 char **pzErrMsg /* Leave an error message here */
997 ){
998 Blob fullName; /* Blob for finding full pathnames */
999 sqlite3 *pOther; /* The other repository */
1000 int rc; /* Return code from sqlite3 functions */
1001 char *zOtherProjCode; /* Project code for pOther */
1002 char *zPwHash; /* Password hash on pOther */
1003 char *zSelfRepo; /* Name of our repository */
1004 char *zSelfLabel; /* Project-name for our repository */
1005 char *zSelfProjCode; /* Our project-code */
1006 char *zSql; /* SQL to run on all peers */
1007 const char *zSelf; /* The ATTACH name of our repository */
1008
1009 *pzErrMsg = 0; /* Default to no errors */
1010 zSelf = db_name("repository");
1011
1012 /* Get the full pathname of the other repository */
1013 file_canonical_name(zRepo, &fullName);
1014 zRepo = mprintf(blob_str(&fullName));
1015 blob_reset(&fullName);
1016
1017 /* Get the full pathname for our repository. Also the project code
1018 ** and project name for ourself. */
1019 file_canonical_name(g.zRepositoryName, &fullName);
1020 zSelfRepo = mprintf(blob_str(&fullName));
1021 blob_reset(&fullName);
1022 zSelfProjCode = db_get("project-code", "unknown");
1023 zSelfLabel = db_get("project-name", 0);
1024 if( zSelfLabel==0 ){
1025 zSelfLabel = zSelfProjCode;
1026 }
1027
1028 /* Make sure we are not trying to join ourselves */
1029 if( strcmp(zRepo, zSelfRepo)==0 ){
1030 *pzErrMsg = mprintf("The \"other\" repository is the same as this one.");
1031 return;
1032 }
1033
1034 /* Make sure the other repository is a valid Fossil database */
1035 if( file_size(zRepo)<0 ){
1036 *pzErrMsg = mprintf("repository file \"%s\" does not exist", zRepo);
1037 return;
1038 }
1039 rc = sqlite3_open(zRepo, &pOther);
1040 if( rc!=SQLITE_OK ){
1041 *pzErrMsg = mprintf(sqlite3_errmsg(pOther));
1042 }else{
1043 rc = sqlite3_exec(pOther, "SELECT count(*) FROM user", 0, 0, pzErrMsg);
1044 }
1045 sqlite3_close(pOther);
1046 if( rc ) return;
1047
1048 /* Attach the other respository. Make sure the username/password is
1049 ** valid and has Setup permission.
1050 */
1051 db_multi_exec("ATTACH %Q AS other", zRepo);
1052 zOtherProjCode = db_text("x", "SELECT value FROM other.config"
1053 " WHERE name='project-code'");
1054 zPwHash = sha1_shared_secret(zPassword, zLogin, zOtherProjCode);
1055 if( !db_exists(
1056 "SELECT 1 FROM other.user"
1057 " WHERE login=%Q AND cap GLOB '*s*'"
1058 " AND (pw=%Q OR pw=%Q)",
1059 zLogin, zPassword, zPwHash)
1060 ){
1061 db_multi_exec("DETACH other");
1062 *pzErrMsg = "The supplied username/password does not correspond to a"
1063 " user Setup permission on the other repository.";
1064 return;
1065 }
1066
1067 /* Create all the necessary CONFIG table entries on both the
1068 ** other repository and on our own repository.
1069 */
1070 zSelfProjCode = abbreviated_project_code(zSelfProjCode);
1071 db_begin_transaction();
1072 db_multi_exec(
1073 "DELETE FROM %s.config WHERE name GLOB 'peer-*';"
1074 "INSERT INTO %s.config(name,value) VALUES('peer-repo-%s',%Q);"
1075 "INSERT INTO %s.config(name,value) "
1076 " SELECT 'peer-name-%q', value FROM other.config"
1077 " WHERE name='project-name';",
1078 zSelf,
1079 zSelf, zOtherProjCode, zRepo,
1080 zSelf, zOtherProjCode
1081 );
1082 db_multi_exec(
1083 "INSERT OR IGNORE INTO other.config(name,value)"
1084 " VALUES('login-group-name',%Q);",
1085 zNewName
1086 );
1087 db_multi_exec(
1088 "REPLACE INTO %s.config(name,value)"
1089 " SELECT name, value FROM other.config"
1090 " WHERE name GLOB 'peer-*' OR name='login-group-name'",
1091 zSelf
1092 );
1093 db_end_transaction(0);
1094 db_multi_exec("DETACH other");
1095
1096 /* Propagate the changes to all other members of the login-group */
1097 zSql = mprintf(
1098 "BEGIN;"
1099 "REPLACE INTO config(name, value) VALUES('peer-name-%q', %Q);"
1100 "REPLACE INTO config(name, value) VALUES('peer-repo-%q', %Q);"
1101 "COMMIT;",
1102 zSelfProjCode, zSelfLabel, zSelfProjCode, zSelfRepo
1103 );
1104 login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
1105 fossil_free(zSql);
1106 }
1107
1108 /*
1109 ** Leave the login group that we are currently part of.
1110 */
1111 void login_group_leave(char **pzErrMsg){
1112 char *zProjCode;
1113 char *zSql;
1114
1115 *pzErrMsg = 0;
1116 zProjCode = abbreviated_project_code(db_get("project-code","x"));
1117 zSql = mprintf(
1118 "DELETE FROM config WHERE name GLOB 'peer-*-%q';"
1119 "DELETE FROM config"
1120 " WHERE name='login-group-name'"
1121 " AND (SELECT count(*) FROM config WHERE name GLOB 'peer-*')==0;",
1122 zProjCode
1123 );
1124 fossil_free(zProjCode);
1125 login_group_sql(zSql, "<li> ", "</li>", pzErrMsg);
1126 fossil_free(zSql);
1127 db_multi_exec(
1128 "DELETE FROM config WHERE name GLOB 'peer-*' OR name='login-group-name';"
1129 );
1130 }
1131
+103 -4
--- src/setup.c
+++ src/setup.c
@@ -44,11 +44,11 @@
4444
if( zLink && zLink[0] ){
4545
@ <a href="%s(zLink)">%h(zTitle)</a>
4646
}else{
4747
@ %h(zTitle)
4848
}
49
- @ </td><td valign="top">%h(zDesc)</td></tr>
49
+ @ </td><td width="5"></td><td valign="top">%h(zDesc)</td></tr>
5050
}
5151
5252
/*
5353
** WEBPAGE: /setup
5454
*/
@@ -57,11 +57,11 @@
5757
if( !g.okSetup ){
5858
login_needed();
5959
}
6060
6161
style_header("Server Administration");
62
- @ <table border="0" cellspacing="20">
62
+ @ <table border="0" cellspacing="7">
6363
setup_menu_entry("Users", "setup_ulist",
6464
"Grant privileges to individual users.");
6565
setup_menu_entry("Access", "setup_access",
6666
"Control access settings.");
6767
setup_menu_entry("Configuration", "setup_config",
@@ -68,10 +68,13 @@
6868
"Configure the WWW components of the repository");
6969
setup_menu_entry("Settings", "setup_settings",
7070
"Web interface to the \"fossil settings\" command");
7171
setup_menu_entry("Timeline", "setup_timeline",
7272
"Timeline display preferences");
73
+ setup_menu_entry("Login-Group", "setup_login_group",
74
+ "Manage single sign-on between this repository and others"
75
+ " on the same server");
7376
setup_menu_entry("Tickets", "tktsetup",
7477
"Configure the trouble-ticketing system for this repository");
7578
setup_menu_entry("Skins", "setup_skin",
7679
"Select from a menu of prepackaged \"skins\" for the web interface");
7780
setup_menu_entry("CSS", "setup_editcss",
@@ -331,11 +334,11 @@
331334
332335
zCap[i] = 0;
333336
zPw = P("pw");
334337
zLogin = P("login");
335338
if( isValidPwString(zPw) ){
336
- zPw = sha1_shared_secret(zPw, zLogin);
339
+ zPw = sha1_shared_secret(zPw, zLogin, 0);
337340
}else{
338341
zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
339342
}
340343
if( uid>0 &&
341344
db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
@@ -823,11 +826,12 @@
823826
@ can register under any user name. This option is useful for public projects
824827
@ where you do not want everyone in any ticket discussion to be named
825828
@ "Anonymous".</p>
826829
827830
@ <hr />
828
- entry_attribute("Default privileges", 10, "default-perms", "defaultperms", "u");
831
+ entry_attribute("Default privileges", 10, "default-perms",
832
+ "defaultperms", "u");
829833
@ <p>Permissions given to users that register themselves using the HTTP UI
830834
@ or are registered by the administrator using the command line interface.
831835
@ </p>
832836
833837
@ <hr />
@@ -843,10 +847,105 @@
843847
@ <p><input type="submit" name="submit" value="Apply Changes" /></p>
844848
@ </div></form>
845849
db_end_transaction(0);
846850
style_footer();
847851
}
852
+
853
+/*
854
+** WEBPAGE: setup_login_group
855
+*/
856
+void setup_login_group(void){
857
+ const char *zGroup;
858
+ char *zErrMsg = 0;
859
+ Blob fullName;
860
+ char *zSelfRepo;
861
+ const char *zRepo = PD("repo", "");
862
+ const char *zLogin = PD("login", "");
863
+ const char *zPw = PD("pw", "");
864
+ const char *zNewName = PD("newname", "New Login Group");
865
+
866
+ login_check_credentials();
867
+ if( !g.okSetup ){
868
+ login_needed();
869
+ }
870
+ file_canonical_name(g.zRepositoryName, &fullName);
871
+ zSelfRepo = mprintf(blob_str(&fullName));
872
+ blob_reset(&fullName);
873
+ if( P("join")!=0 ){
874
+ login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg);
875
+ }else if( P("leave") ){
876
+ login_group_leave(&zErrMsg);
877
+ }
878
+ style_header("Login Group Configuration");
879
+ if( zErrMsg ){
880
+ @ <p class="generalError">%s(zErrMsg)</p>
881
+ }
882
+ zGroup = db_get("login-group-name", 0);
883
+ if( zGroup==0 ){
884
+ @ <p>This repository (in the file named "%h(zSelfRepo)")
885
+ @ is not currently part of any login-group.
886
+ @ To join a login group, fill out the form below.</p>
887
+ @
888
+ @ <form action="%s(g.zTop)/setup_login_group" method="post"><div>
889
+ login_insert_csrf_secret();
890
+ @ <blockquote><table broder="0">
891
+ @
892
+ @ <tr><td align="right"><b>Repository filename in group to join:</b></td>
893
+ @ <td width="5"></td><td>
894
+ @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr>
895
+ @
896
+ @ <td align="right"><b>Login on the above repo:</b></td>
897
+ @ <td width="5"></td><td>
898
+ @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr>
899
+ @
900
+ @ <td align="right"><b>Password:</b></td>
901
+ @ <td width="5"></td><td>
902
+ @ <input type="password" size="20" name="pw"></td></tr>
903
+ @
904
+ @ <tr><td align="right"><b>Name of login-group:</b></td>
905
+ @ <td width="5"></td><td>
906
+ @ <input type="text" size="30" value="%h(zNewName)" name="newname">
907
+ @ (only used if creating a new login-group).</td></tr>
908
+ @
909
+ @ <tr><td colspan="3" align="center">
910
+ @ <input type="submit" value="Join" name="join"></td></tr>
911
+ @ </table>
912
+ }else{
913
+ Stmt q;
914
+ int n = 0;
915
+ @ <p>This repository (in the file "%h(zSelfRepo)")
916
+ @ is currently part of the "<b>%h(zGroup)</b>" login group.
917
+ @ Other repositories in that group are:</p>
918
+ @ <table border="0" cellspacing="4">
919
+ @ <tr><td colspan="2"><th align="left">Project Name<td>
920
+ @ <th align="left">Repository File</tr>
921
+ db_prepare(&q,
922
+ "SELECT value,"
923
+ " (SELECT value FROM config"
924
+ " WHERE name=('peer-name-' || substr(x.name,11)))"
925
+ " FROM config AS x"
926
+ " WHERE name GLOB 'peer-repo-*'"
927
+ " ORDER BY value"
928
+ );
929
+ while( db_step(&q)==SQLITE_ROW ){
930
+ const char *zRepo = db_column_text(&q, 0);
931
+ const char *zTitle = db_column_text(&q, 1);
932
+ n++;
933
+ @ <tr><td align="right">%d(n).</td><td width="4">
934
+ @ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr>
935
+ }
936
+ db_finalize(&q);
937
+ @ </table>
938
+ @
939
+ @ <p><form action="%s(g.zTop)/setup_login_group" method="post"><div>
940
+ login_insert_csrf_secret();
941
+ @ To leave this login group press
942
+ @ <input type="submit" value="Leave Login Group" name="leave">
943
+ @ </form></p>
944
+ }
945
+ style_footer();
946
+}
848947
849948
/*
850949
** WEBPAGE: setup_timeline
851950
*/
852951
void setup_timeline(void){
853952
--- src/setup.c
+++ src/setup.c
@@ -44,11 +44,11 @@
44 if( zLink && zLink[0] ){
45 @ <a href="%s(zLink)">%h(zTitle)</a>
46 }else{
47 @ %h(zTitle)
48 }
49 @ </td><td valign="top">%h(zDesc)</td></tr>
50 }
51
52 /*
53 ** WEBPAGE: /setup
54 */
@@ -57,11 +57,11 @@
57 if( !g.okSetup ){
58 login_needed();
59 }
60
61 style_header("Server Administration");
62 @ <table border="0" cellspacing="20">
63 setup_menu_entry("Users", "setup_ulist",
64 "Grant privileges to individual users.");
65 setup_menu_entry("Access", "setup_access",
66 "Control access settings.");
67 setup_menu_entry("Configuration", "setup_config",
@@ -68,10 +68,13 @@
68 "Configure the WWW components of the repository");
69 setup_menu_entry("Settings", "setup_settings",
70 "Web interface to the \"fossil settings\" command");
71 setup_menu_entry("Timeline", "setup_timeline",
72 "Timeline display preferences");
 
 
 
73 setup_menu_entry("Tickets", "tktsetup",
74 "Configure the trouble-ticketing system for this repository");
75 setup_menu_entry("Skins", "setup_skin",
76 "Select from a menu of prepackaged \"skins\" for the web interface");
77 setup_menu_entry("CSS", "setup_editcss",
@@ -331,11 +334,11 @@
331
332 zCap[i] = 0;
333 zPw = P("pw");
334 zLogin = P("login");
335 if( isValidPwString(zPw) ){
336 zPw = sha1_shared_secret(zPw, zLogin);
337 }else{
338 zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
339 }
340 if( uid>0 &&
341 db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
@@ -823,11 +826,12 @@
823 @ can register under any user name. This option is useful for public projects
824 @ where you do not want everyone in any ticket discussion to be named
825 @ "Anonymous".</p>
826
827 @ <hr />
828 entry_attribute("Default privileges", 10, "default-perms", "defaultperms", "u");
 
829 @ <p>Permissions given to users that register themselves using the HTTP UI
830 @ or are registered by the administrator using the command line interface.
831 @ </p>
832
833 @ <hr />
@@ -843,10 +847,105 @@
843 @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
844 @ </div></form>
845 db_end_transaction(0);
846 style_footer();
847 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
848
849 /*
850 ** WEBPAGE: setup_timeline
851 */
852 void setup_timeline(void){
853
--- src/setup.c
+++ src/setup.c
@@ -44,11 +44,11 @@
44 if( zLink && zLink[0] ){
45 @ <a href="%s(zLink)">%h(zTitle)</a>
46 }else{
47 @ %h(zTitle)
48 }
49 @ </td><td width="5"></td><td valign="top">%h(zDesc)</td></tr>
50 }
51
52 /*
53 ** WEBPAGE: /setup
54 */
@@ -57,11 +57,11 @@
57 if( !g.okSetup ){
58 login_needed();
59 }
60
61 style_header("Server Administration");
62 @ <table border="0" cellspacing="7">
63 setup_menu_entry("Users", "setup_ulist",
64 "Grant privileges to individual users.");
65 setup_menu_entry("Access", "setup_access",
66 "Control access settings.");
67 setup_menu_entry("Configuration", "setup_config",
@@ -68,10 +68,13 @@
68 "Configure the WWW components of the repository");
69 setup_menu_entry("Settings", "setup_settings",
70 "Web interface to the \"fossil settings\" command");
71 setup_menu_entry("Timeline", "setup_timeline",
72 "Timeline display preferences");
73 setup_menu_entry("Login-Group", "setup_login_group",
74 "Manage single sign-on between this repository and others"
75 " on the same server");
76 setup_menu_entry("Tickets", "tktsetup",
77 "Configure the trouble-ticketing system for this repository");
78 setup_menu_entry("Skins", "setup_skin",
79 "Select from a menu of prepackaged \"skins\" for the web interface");
80 setup_menu_entry("CSS", "setup_editcss",
@@ -331,11 +334,11 @@
334
335 zCap[i] = 0;
336 zPw = P("pw");
337 zLogin = P("login");
338 if( isValidPwString(zPw) ){
339 zPw = sha1_shared_secret(zPw, zLogin, 0);
340 }else{
341 zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
342 }
343 if( uid>0 &&
344 db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
@@ -823,11 +826,12 @@
826 @ can register under any user name. This option is useful for public projects
827 @ where you do not want everyone in any ticket discussion to be named
828 @ "Anonymous".</p>
829
830 @ <hr />
831 entry_attribute("Default privileges", 10, "default-perms",
832 "defaultperms", "u");
833 @ <p>Permissions given to users that register themselves using the HTTP UI
834 @ or are registered by the administrator using the command line interface.
835 @ </p>
836
837 @ <hr />
@@ -843,10 +847,105 @@
847 @ <p><input type="submit" name="submit" value="Apply Changes" /></p>
848 @ </div></form>
849 db_end_transaction(0);
850 style_footer();
851 }
852
853 /*
854 ** WEBPAGE: setup_login_group
855 */
856 void setup_login_group(void){
857 const char *zGroup;
858 char *zErrMsg = 0;
859 Blob fullName;
860 char *zSelfRepo;
861 const char *zRepo = PD("repo", "");
862 const char *zLogin = PD("login", "");
863 const char *zPw = PD("pw", "");
864 const char *zNewName = PD("newname", "New Login Group");
865
866 login_check_credentials();
867 if( !g.okSetup ){
868 login_needed();
869 }
870 file_canonical_name(g.zRepositoryName, &fullName);
871 zSelfRepo = mprintf(blob_str(&fullName));
872 blob_reset(&fullName);
873 if( P("join")!=0 ){
874 login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg);
875 }else if( P("leave") ){
876 login_group_leave(&zErrMsg);
877 }
878 style_header("Login Group Configuration");
879 if( zErrMsg ){
880 @ <p class="generalError">%s(zErrMsg)</p>
881 }
882 zGroup = db_get("login-group-name", 0);
883 if( zGroup==0 ){
884 @ <p>This repository (in the file named "%h(zSelfRepo)")
885 @ is not currently part of any login-group.
886 @ To join a login group, fill out the form below.</p>
887 @
888 @ <form action="%s(g.zTop)/setup_login_group" method="post"><div>
889 login_insert_csrf_secret();
890 @ <blockquote><table broder="0">
891 @
892 @ <tr><td align="right"><b>Repository filename in group to join:</b></td>
893 @ <td width="5"></td><td>
894 @ <input type="text" size="50" value="%h(zRepo)" name="repo"></td></tr>
895 @
896 @ <td align="right"><b>Login on the above repo:</b></td>
897 @ <td width="5"></td><td>
898 @ <input type="text" size="20" value="%h(zLogin)" name="login"></td></tr>
899 @
900 @ <td align="right"><b>Password:</b></td>
901 @ <td width="5"></td><td>
902 @ <input type="password" size="20" name="pw"></td></tr>
903 @
904 @ <tr><td align="right"><b>Name of login-group:</b></td>
905 @ <td width="5"></td><td>
906 @ <input type="text" size="30" value="%h(zNewName)" name="newname">
907 @ (only used if creating a new login-group).</td></tr>
908 @
909 @ <tr><td colspan="3" align="center">
910 @ <input type="submit" value="Join" name="join"></td></tr>
911 @ </table>
912 }else{
913 Stmt q;
914 int n = 0;
915 @ <p>This repository (in the file "%h(zSelfRepo)")
916 @ is currently part of the "<b>%h(zGroup)</b>" login group.
917 @ Other repositories in that group are:</p>
918 @ <table border="0" cellspacing="4">
919 @ <tr><td colspan="2"><th align="left">Project Name<td>
920 @ <th align="left">Repository File</tr>
921 db_prepare(&q,
922 "SELECT value,"
923 " (SELECT value FROM config"
924 " WHERE name=('peer-name-' || substr(x.name,11)))"
925 " FROM config AS x"
926 " WHERE name GLOB 'peer-repo-*'"
927 " ORDER BY value"
928 );
929 while( db_step(&q)==SQLITE_ROW ){
930 const char *zRepo = db_column_text(&q, 0);
931 const char *zTitle = db_column_text(&q, 1);
932 n++;
933 @ <tr><td align="right">%d(n).</td><td width="4">
934 @ <td>%h(zTitle)<td width="10"><td>%h(zRepo)</tr>
935 }
936 db_finalize(&q);
937 @ </table>
938 @
939 @ <p><form action="%s(g.zTop)/setup_login_group" method="post"><div>
940 login_insert_csrf_secret();
941 @ To leave this login group press
942 @ <input type="submit" value="Leave Login Group" name="leave">
943 @ </form></p>
944 }
945 style_footer();
946 }
947
948 /*
949 ** WEBPAGE: setup_timeline
950 */
951 void setup_timeline(void){
952
+53 -12
--- src/sha1.c
+++ src/sha1.c
@@ -342,36 +342,77 @@
342342
** value stored in the USER.PW field of the database. By mixing in the
343343
** login name and the project id with the hash, different shared secrets
344344
** are obtained even if two users select the same password, or if a
345345
** single user selects the same password for multiple projects.
346346
*/
347
-char *sha1_shared_secret(const char *zPw, const char *zLogin){
347
+char *sha1_shared_secret(
348
+ const char *zPw, /* The password to encrypt */
349
+ const char *zLogin, /* Username */
350
+ const char *zProjCode /* Project-code. Use built-in project code if NULL */
351
+){
348352
static char *zProjectId = 0;
349353
SHA1Context ctx;
350354
unsigned char zResult[20];
351355
char zDigest[41];
352356
353357
SHA1Init(&ctx);
354
- if( zProjectId==0 ){
355
- zProjectId = db_get("project-code", 0);
356
-
357
- /* On the first xfer request of a clone, the project-code is not yet
358
- ** known. Use the cleartext password, since that is all we have.
359
- */
360
- if( zProjectId==0 ){
361
- return mprintf("%s", zPw);
362
- }
363
- }
364
- SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
358
+ if( zProjCode==0 ){
359
+ if( zProjectId==0 ){
360
+ zProjectId = db_get("project-code", 0);
361
+
362
+ /* On the first xfer request of a clone, the project-code is not yet
363
+ ** known. Use the cleartext password, since that is all we have.
364
+ */
365
+ if( zProjectId==0 ){
366
+ return mprintf("%s", zPw);
367
+ }
368
+ }
369
+ zProjCode = zProjectId;
370
+ }
371
+ SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode));
365372
SHA1Update(&ctx, (unsigned char*)"/", 1);
366373
SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
367374
SHA1Update(&ctx, (unsigned char*)"/", 1);
368375
SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
369376
SHA1Final(&ctx, zResult);
370377
DigestToBase16(zResult, zDigest);
371378
return mprintf("%s", zDigest);
372379
}
380
+
381
+/*
382
+** Implement the shared_secret() SQL function. shared_secret() takes two or
383
+** three arguments; the third argument is optional.
384
+**
385
+** (1) The cleartext password
386
+** (2) The login name
387
+** (3) The project code
388
+**
389
+** Returns sha1($password/$login/$projcode).
390
+*/
391
+void sha1_shared_secret_sql_function(
392
+ sqlite3_context *context,
393
+ int argc,
394
+ sqlite3_value **argv
395
+){
396
+ const char *zPw;
397
+ const char *zLogin;
398
+ const char *zProjid;
399
+
400
+ assert( argc==2 || argc==3 );
401
+ zPw = (const char*)sqlite3_value_text(argv[0]);
402
+ if( zPw==0 ) return;
403
+ zLogin = (const char*)sqlite3_value_text(argv[1]);
404
+ if( zLogin==0 ) return;
405
+ if( argc==3 ){
406
+ zProjid = (const char*)sqlite3_value_text(argv[2]);
407
+ if( zProjid && zProjid[0]==0 ) zProjid = 0;
408
+ }else{
409
+ zProjid = 0;
410
+ }
411
+ sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1,
412
+ fossil_free);
413
+}
373414
374415
/*
375416
** COMMAND: sha1sum
376417
** %fossil sha1sum FILE...
377418
**
378419
--- src/sha1.c
+++ src/sha1.c
@@ -342,36 +342,77 @@
342 ** value stored in the USER.PW field of the database. By mixing in the
343 ** login name and the project id with the hash, different shared secrets
344 ** are obtained even if two users select the same password, or if a
345 ** single user selects the same password for multiple projects.
346 */
347 char *sha1_shared_secret(const char *zPw, const char *zLogin){
 
 
 
 
348 static char *zProjectId = 0;
349 SHA1Context ctx;
350 unsigned char zResult[20];
351 char zDigest[41];
352
353 SHA1Init(&ctx);
354 if( zProjectId==0 ){
355 zProjectId = db_get("project-code", 0);
356
357 /* On the first xfer request of a clone, the project-code is not yet
358 ** known. Use the cleartext password, since that is all we have.
359 */
360 if( zProjectId==0 ){
361 return mprintf("%s", zPw);
362 }
363 }
364 SHA1Update(&ctx, (unsigned char*)zProjectId, strlen(zProjectId));
 
 
 
365 SHA1Update(&ctx, (unsigned char*)"/", 1);
366 SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
367 SHA1Update(&ctx, (unsigned char*)"/", 1);
368 SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
369 SHA1Final(&ctx, zResult);
370 DigestToBase16(zResult, zDigest);
371 return mprintf("%s", zDigest);
372 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
373
374 /*
375 ** COMMAND: sha1sum
376 ** %fossil sha1sum FILE...
377 **
378
--- src/sha1.c
+++ src/sha1.c
@@ -342,36 +342,77 @@
342 ** value stored in the USER.PW field of the database. By mixing in the
343 ** login name and the project id with the hash, different shared secrets
344 ** are obtained even if two users select the same password, or if a
345 ** single user selects the same password for multiple projects.
346 */
347 char *sha1_shared_secret(
348 const char *zPw, /* The password to encrypt */
349 const char *zLogin, /* Username */
350 const char *zProjCode /* Project-code. Use built-in project code if NULL */
351 ){
352 static char *zProjectId = 0;
353 SHA1Context ctx;
354 unsigned char zResult[20];
355 char zDigest[41];
356
357 SHA1Init(&ctx);
358 if( zProjCode==0 ){
359 if( zProjectId==0 ){
360 zProjectId = db_get("project-code", 0);
361
362 /* On the first xfer request of a clone, the project-code is not yet
363 ** known. Use the cleartext password, since that is all we have.
364 */
365 if( zProjectId==0 ){
366 return mprintf("%s", zPw);
367 }
368 }
369 zProjCode = zProjectId;
370 }
371 SHA1Update(&ctx, (unsigned char*)zProjCode, strlen(zProjCode));
372 SHA1Update(&ctx, (unsigned char*)"/", 1);
373 SHA1Update(&ctx, (unsigned char*)zLogin, strlen(zLogin));
374 SHA1Update(&ctx, (unsigned char*)"/", 1);
375 SHA1Update(&ctx, (unsigned const char*)zPw, strlen(zPw));
376 SHA1Final(&ctx, zResult);
377 DigestToBase16(zResult, zDigest);
378 return mprintf("%s", zDigest);
379 }
380
381 /*
382 ** Implement the shared_secret() SQL function. shared_secret() takes two or
383 ** three arguments; the third argument is optional.
384 **
385 ** (1) The cleartext password
386 ** (2) The login name
387 ** (3) The project code
388 **
389 ** Returns sha1($password/$login/$projcode).
390 */
391 void sha1_shared_secret_sql_function(
392 sqlite3_context *context,
393 int argc,
394 sqlite3_value **argv
395 ){
396 const char *zPw;
397 const char *zLogin;
398 const char *zProjid;
399
400 assert( argc==2 || argc==3 );
401 zPw = (const char*)sqlite3_value_text(argv[0]);
402 if( zPw==0 ) return;
403 zLogin = (const char*)sqlite3_value_text(argv[1]);
404 if( zLogin==0 ) return;
405 if( argc==3 ){
406 zProjid = (const char*)sqlite3_value_text(argv[2]);
407 if( zProjid && zProjid[0]==0 ) zProjid = 0;
408 }else{
409 zProjid = 0;
410 }
411 sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin, zProjid), -1,
412 fossil_free);
413 }
414
415 /*
416 ** COMMAND: sha1sum
417 ** %fossil sha1sum FILE...
418 **
419
+5 -22
--- src/user.c
+++ src/user.c
@@ -202,11 +202,11 @@
202202
if( g.argc>=6 ){
203203
blob_init(&passwd, g.argv[5], -1);
204204
}else{
205205
prompt_for_password("password: ", &passwd, 1);
206206
}
207
- zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
207
+ zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
208208
db_multi_exec(
209209
"INSERT INTO user(login,pw,cap,info)"
210210
"VALUES(%B,%Q,%B,%B)",
211211
&login, zPw, &caps, &contact
212212
);
@@ -248,11 +248,11 @@
248248
prompt_for_password(zPrompt, &pw, 1);
249249
}
250250
if( blob_size(&pw)==0 ){
251251
printf("password unchanged\n");
252252
}else{
253
- char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3]);
253
+ char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0);
254254
db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
255255
free(zSecret);
256256
}
257257
}else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
258258
int uid;
@@ -348,27 +348,10 @@
348348
g.userUid = db_last_insert_rowid();
349349
g.zLogin = "anonymous";
350350
}
351351
}
352352
353
-/*
354
-** Compute the shared secret for a user.
355
-*/
356
-static void user_sha1_shared_secret_func(
357
- sqlite3_context *context,
358
- int argc,
359
- sqlite3_value **argv
360
-){
361
- char *zPw;
362
- char *zLogin;
363
- assert( argc==2 );
364
- zPw = (char*)sqlite3_value_text(argv[0]);
365
- zLogin = (char*)sqlite3_value_text(argv[1]);
366
- if( zPw && zLogin ){
367
- sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin), -1, free);
368
- }
369
-}
370353
371354
/*
372355
** COMMAND: test-hash-passwords
373356
**
374357
** Usage: %fossil test-hash-passwords REPOSITORY
@@ -378,14 +361,14 @@
378361
** has are unchanged.
379362
*/
380363
void user_hash_passwords_cmd(void){
381364
if( g.argc!=3 ) usage("REPOSITORY");
382365
db_open_repository(g.argv[2]);
383
- sqlite3_create_function(g.db, "sha1_shared_secret", 2, SQLITE_UTF8, 0,
384
- user_sha1_shared_secret_func, 0, 0);
366
+ sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0,
367
+ sha1_shared_secret_sql_function, 0, 0);
385368
db_multi_exec(
386
- "UPDATE user SET pw=sha1_shared_secret(pw,login)"
369
+ "UPDATE user SET pw=shared_secret(pw,login)"
387370
" WHERE length(pw)>0 AND length(pw)!=40"
388371
);
389372
}
390373
391374
/*
392375
--- src/user.c
+++ src/user.c
@@ -202,11 +202,11 @@
202 if( g.argc>=6 ){
203 blob_init(&passwd, g.argv[5], -1);
204 }else{
205 prompt_for_password("password: ", &passwd, 1);
206 }
207 zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login));
208 db_multi_exec(
209 "INSERT INTO user(login,pw,cap,info)"
210 "VALUES(%B,%Q,%B,%B)",
211 &login, zPw, &caps, &contact
212 );
@@ -248,11 +248,11 @@
248 prompt_for_password(zPrompt, &pw, 1);
249 }
250 if( blob_size(&pw)==0 ){
251 printf("password unchanged\n");
252 }else{
253 char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3]);
254 db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
255 free(zSecret);
256 }
257 }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
258 int uid;
@@ -348,27 +348,10 @@
348 g.userUid = db_last_insert_rowid();
349 g.zLogin = "anonymous";
350 }
351 }
352
353 /*
354 ** Compute the shared secret for a user.
355 */
356 static void user_sha1_shared_secret_func(
357 sqlite3_context *context,
358 int argc,
359 sqlite3_value **argv
360 ){
361 char *zPw;
362 char *zLogin;
363 assert( argc==2 );
364 zPw = (char*)sqlite3_value_text(argv[0]);
365 zLogin = (char*)sqlite3_value_text(argv[1]);
366 if( zPw && zLogin ){
367 sqlite3_result_text(context, sha1_shared_secret(zPw, zLogin), -1, free);
368 }
369 }
370
371 /*
372 ** COMMAND: test-hash-passwords
373 **
374 ** Usage: %fossil test-hash-passwords REPOSITORY
@@ -378,14 +361,14 @@
378 ** has are unchanged.
379 */
380 void user_hash_passwords_cmd(void){
381 if( g.argc!=3 ) usage("REPOSITORY");
382 db_open_repository(g.argv[2]);
383 sqlite3_create_function(g.db, "sha1_shared_secret", 2, SQLITE_UTF8, 0,
384 user_sha1_shared_secret_func, 0, 0);
385 db_multi_exec(
386 "UPDATE user SET pw=sha1_shared_secret(pw,login)"
387 " WHERE length(pw)>0 AND length(pw)!=40"
388 );
389 }
390
391 /*
392
--- src/user.c
+++ src/user.c
@@ -202,11 +202,11 @@
202 if( g.argc>=6 ){
203 blob_init(&passwd, g.argv[5], -1);
204 }else{
205 prompt_for_password("password: ", &passwd, 1);
206 }
207 zPw = sha1_shared_secret(blob_str(&passwd), blob_str(&login), 0);
208 db_multi_exec(
209 "INSERT INTO user(login,pw,cap,info)"
210 "VALUES(%B,%Q,%B,%B)",
211 &login, zPw, &caps, &contact
212 );
@@ -248,11 +248,11 @@
248 prompt_for_password(zPrompt, &pw, 1);
249 }
250 if( blob_size(&pw)==0 ){
251 printf("password unchanged\n");
252 }else{
253 char *zSecret = sha1_shared_secret(blob_str(&pw), g.argv[3], 0);
254 db_multi_exec("UPDATE user SET pw=%Q WHERE uid=%d", zSecret, uid);
255 free(zSecret);
256 }
257 }else if( n>=2 && strncmp(g.argv[2],"capabilities",2)==0 ){
258 int uid;
@@ -348,27 +348,10 @@
348 g.userUid = db_last_insert_rowid();
349 g.zLogin = "anonymous";
350 }
351 }
352
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
354 /*
355 ** COMMAND: test-hash-passwords
356 **
357 ** Usage: %fossil test-hash-passwords REPOSITORY
@@ -378,14 +361,14 @@
361 ** has are unchanged.
362 */
363 void user_hash_passwords_cmd(void){
364 if( g.argc!=3 ) usage("REPOSITORY");
365 db_open_repository(g.argv[2]);
366 sqlite3_create_function(g.db, "shared_secret", 2, SQLITE_UTF8, 0,
367 sha1_shared_secret_sql_function, 0, 0);
368 db_multi_exec(
369 "UPDATE user SET pw=shared_secret(pw,login)"
370 " WHERE length(pw)>0 AND length(pw)!=40"
371 );
372 }
373
374 /*
375
+1 -1
--- src/xfer.c
+++ src/xfer.c
@@ -579,11 +579,11 @@
579579
/* If this server stores cleartext passwords and the password did not
580580
** match, then perhaps the client is sending SHA1 passwords. Try
581581
** again with the SHA1 password.
582582
*/
583583
const char *zPw = db_column_text(&q, 0);
584
- char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin));
584
+ char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
585585
blob_zero(&combined);
586586
blob_copy(&combined, pNonce);
587587
blob_append(&combined, zSecret, -1);
588588
free(zSecret);
589589
sha1sum_blob(&combined, &hash);
590590
--- src/xfer.c
+++ src/xfer.c
@@ -579,11 +579,11 @@
579 /* If this server stores cleartext passwords and the password did not
580 ** match, then perhaps the client is sending SHA1 passwords. Try
581 ** again with the SHA1 password.
582 */
583 const char *zPw = db_column_text(&q, 0);
584 char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin));
585 blob_zero(&combined);
586 blob_copy(&combined, pNonce);
587 blob_append(&combined, zSecret, -1);
588 free(zSecret);
589 sha1sum_blob(&combined, &hash);
590
--- src/xfer.c
+++ src/xfer.c
@@ -579,11 +579,11 @@
579 /* If this server stores cleartext passwords and the password did not
580 ** match, then perhaps the client is sending SHA1 passwords. Try
581 ** again with the SHA1 password.
582 */
583 const char *zPw = db_column_text(&q, 0);
584 char *zSecret = sha1_shared_secret(zPw, blob_str(pLogin), 0);
585 blob_zero(&combined);
586 blob_copy(&combined, pNonce);
587 blob_append(&combined, zSecret, -1);
588 free(zSecret);
589 sha1sum_blob(&combined, &hash);
590

Keyboard Shortcuts

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