Fossil SCM

Add the /resetpw web page. The name argument must contain a hash that proves knowledge of the old password and that limits the valid lifetime of the argument.

drh 2023-01-07 00:03 trunk
Commit ac86dfa085cd5eed9b3c8d94a50d5a6ccf083877c96d9cdf66e83329343dd666
2 files changed +1 +229 -1
+1
--- src/blob.c
+++ src/blob.c
@@ -124,10 +124,11 @@
124124
** Other replacements for ctype.h functions.
125125
*/
126126
int fossil_islower(char c){ return c>='a' && c<='z'; }
127127
int fossil_isupper(char c){ return c>='A' && c<='Z'; }
128128
int fossil_isdigit(char c){ return c>='0' && c<='9'; }
129
+int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); }
129130
int fossil_tolower(char c){
130131
return fossil_isupper(c) ? c - 'A' + 'a' : c;
131132
}
132133
int fossil_toupper(char c){
133134
return fossil_islower(c) ? c - 'a' + 'A' : c;
134135
--- src/blob.c
+++ src/blob.c
@@ -124,10 +124,11 @@
124 ** Other replacements for ctype.h functions.
125 */
126 int fossil_islower(char c){ return c>='a' && c<='z'; }
127 int fossil_isupper(char c){ return c>='A' && c<='Z'; }
128 int fossil_isdigit(char c){ return c>='0' && c<='9'; }
 
129 int fossil_tolower(char c){
130 return fossil_isupper(c) ? c - 'A' + 'a' : c;
131 }
132 int fossil_toupper(char c){
133 return fossil_islower(c) ? c - 'a' + 'A' : c;
134
--- src/blob.c
+++ src/blob.c
@@ -124,10 +124,11 @@
124 ** Other replacements for ctype.h functions.
125 */
126 int fossil_islower(char c){ return c>='a' && c<='z'; }
127 int fossil_isupper(char c){ return c>='A' && c<='Z'; }
128 int fossil_isdigit(char c){ return c>='0' && c<='9'; }
129 int fossil_isxdigit(char c){ return (c>='0' && c<='9') || (c>='a' && c<='f'); }
130 int fossil_tolower(char c){
131 return fossil_isupper(c) ? c - 'A' + 'a' : c;
132 }
133 int fossil_toupper(char c){
134 return fossil_islower(c) ? c - 'a' + 'A' : c;
135
+229 -1
--- src/login.c
+++ src/login.c
@@ -613,10 +613,14 @@
613613
char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
614614
char *zChngPw;
615615
char *zErr;
616616
int rc;
617617
618
+ /* vvvvvvv--- tag-20230106-1 ----vvvvvv
619
+ **
620
+ ** Replicate changes made below to tag-20230106-2
621
+ */
618622
db_unprotect(PROTECT_USER);
619623
db_multi_exec(
620624
"UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
621625
);
622626
zChngPw = mprintf(
@@ -627,10 +631,16 @@
627631
zNew1, g.zLogin, g.zLogin
628632
);
629633
fossil_free(zNewPw);
630634
rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr);
631635
db_protect_pop();
636
+ /*
637
+ ** ^^^^^^^^--- tag-20230106-1 ----^^^^^^^^^
638
+ **
639
+ ** Replicate changes above to tag-20230106-2
640
+ */
641
+
632642
if( rc ){
633643
zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
634644
fossil_free(zErr);
635645
}else{
636646
redirect_to_g();
@@ -828,10 +838,218 @@
828838
@ </form>
829839
}
830840
}
831841
style_finish_page();
832842
}
843
+
844
+/*
845
+** Construct an appropriate URL suffix for the /resetpw page. The
846
+** suffix will be of the form:
847
+**
848
+** UID-TIMESTAMP-HASH
849
+**
850
+** Where UID and TIMESTAMP are the parameters to this function, and HASH
851
+** is constructed from information that is unique to the user in question
852
+** and which is not publicly available. In particular, the HASH includes
853
+** the existing user password. Thus, in order to construct a URL that can
854
+** change a password, the attacker must know the current password, in which
855
+** case that do not need to construct the URL in order to take over the
856
+** account.
857
+**
858
+** Return a pointer to the resulting string in memory obtained
859
+** from fossil_malloc().
860
+*/
861
+char *login_resetpw_suffix(int uid, i64 timestamp){
862
+ char *zHash;
863
+ char *zInnerSql;
864
+ char *zResult;
865
+ extern int sqlite3_shathree_init(sqlite3*,char**,const sqlite3_api_routines*);
866
+ if( timestamp<=0 ){ timestamp = time(0); }
867
+ sqlite3_shathree_init(g.db, 0, 0);
868
+ if( db_table_exists("repository","subscriber") ){
869
+ zInnerSql = mprintf(
870
+ "SELECT %lld, login, pw, cookie, user.mtime, user.info, subscriberCode"
871
+ " FROM user LEFT JOIN subscriber ON suname=login"
872
+ " WHERE uid=%d", timestamp, uid);
873
+ }else{
874
+ zInnerSql = mprintf(
875
+ "SELECT %lld, login, pw, cookie, user.mtime, user.info"
876
+ " FROM user WHERE uid=%d", timestamp, uid);
877
+ }
878
+ zHash = db_text(0, "SELECT lower(hex(sha3_query(%Q)))", zInnerSql);
879
+ fossil_free(zInnerSql);
880
+ zResult = mprintf("%x-%llx-%s", uid, timestamp, zHash);
881
+ fossil_free(zHash);
882
+ return zResult;
883
+}
884
+
885
+/*
886
+** Check to see if the "name" query parameter is a valid resetpw suffix
887
+** for a user whose password we are allowed to reset. If it is, then return
888
+** the positive integer UID for that user. If the query parameter is not
889
+** valid, return 0.
890
+*/
891
+static int login_resetpw_suffix_is_valid(const char *zName){
892
+ int i, j;
893
+ int uid;
894
+ i64 timestamp;
895
+ i64 now;
896
+ char *zHash;
897
+ for(i=0; fossil_isxdigit(zName[i]); i++){}
898
+ if( i<1 || zName[i]!='-' ) goto not_valid_suffix;
899
+ for(j=i+1; fossil_isxdigit(zName[j]); j++){}
900
+ if( j<=i+1 || zName[j]!='-' ) goto not_valid_suffix;
901
+ uid = strtol(zName, 0, 16);
902
+ if( uid<=0 ) goto not_valid_suffix;
903
+ if( !db_exists("SELECT 1 FROM user WHERE uid=%d", uid) ){
904
+ goto not_valid_suffix;
905
+ }
906
+ timestamp = strtoll(&zName[i+1], 0, 16);
907
+ now = time(0);
908
+ if( timestamp+3600 <= now ) goto not_valid_suffix;
909
+ zHash = login_resetpw_suffix(uid,timestamp);
910
+ if( fossil_strcmp(zHash, zName)!=0 ){
911
+ fossil_free(zHash);
912
+ goto not_valid_suffix;
913
+ }
914
+ fossil_free(zHash);
915
+ return uid;
916
+
917
+not_valid_suffix:
918
+ sleep(2); /* Introduce a small delay on an invalid suffix as an
919
+ ** extra defense against search attacks */
920
+ return 0;
921
+}
922
+
923
+/*
924
+** COMMAND: test-resetpw-url
925
+** Usage: fossil test-resetpw-url UID
926
+**
927
+** Generate and verify a /resetpw URL for user UID.
928
+*/
929
+void test_resetpw_url(void){
930
+ char *zSuffix;
931
+ int uid;
932
+ db_find_and_open_repository(0, 0);
933
+ verify_all_options();
934
+ if( g.argc!=3 ){
935
+ usage("UID");
936
+ }
937
+ uid = atoi(g.argv[2]);
938
+ zSuffix = login_resetpw_suffix(uid, 0);
939
+ fossil_print("/resetpw/%s %d\n", zSuffix,
940
+ login_resetpw_suffix_is_valid(zSuffix));
941
+ fossil_free(zSuffix);
942
+}
943
+
944
+/*
945
+** WEBPAGE: resetpw
946
+**
947
+** The URL format must be like this:
948
+**
949
+** /resetpw/UID-TIMESTAMP-HASH
950
+**
951
+** Where UID is the uid of the user whose password is to be reset,
952
+** TIMESTAMP is the unix timestamp when the request was made, and
953
+** HASH is a hash based on UID, TIMESTAMP, and other information that
954
+** is unavailable to an attacher.
955
+**
956
+** With no other arguments, a form is present which allows the user to
957
+** enter a new password. When the SUBMIT button is pressed, a POST request
958
+** back to the same URL that will change the password.
959
+*/
960
+void login_resetpw(void){
961
+ const char *zName;
962
+ int uid;
963
+ char *zRPW;
964
+ const char *zNew1, *zNew2;
965
+
966
+ style_set_current_feature("resetpw");
967
+ style_header("Reset Password");
968
+ style_adunit_config(ADUNIT_OFF);
969
+ zName = PD("name","");
970
+ uid = login_resetpw_suffix_is_valid(zName);
971
+ if( uid==0 ){
972
+ @ <p><span class="loginError">
973
+ @ This password-reset URL is invalid, probably because it has expired.
974
+ @ Password-reset URLs have a short lifespan.
975
+ @ </span></p>
976
+ style_finish_page();
977
+ return;
978
+ }
979
+ login_set_uid(uid, 0);
980
+ if( g.perm.Setup || g.perm.Admin || !g.perm.Password || g.zLogin==0 ){
981
+ @ <p><span class="loginError">
982
+ @ Cannot change the password for user <b>%h(g.zLogin)</b>.
983
+ @ </span></p>
984
+ style_finish_page();
985
+ return;
986
+ }
987
+ if( (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
988
+ if( fossil_strcmp(zNew1,zNew2)!=0 ){
989
+ @ <p><span class="loginError">
990
+ @ The two copies of your new passwords do not match.
991
+ @ Try again.
992
+ @ </span></p>
993
+ }else{
994
+ char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
995
+ char *zChngPw;
996
+ char *zErr;
997
+ int rc;
998
+
999
+ /* vvvvvvv--- tag-20230106-2 ----vvvvvv
1000
+ **
1001
+ ** Replicate changes made below to tag-20230106-1
1002
+ */
1003
+ db_unprotect(PROTECT_USER);
1004
+ db_multi_exec(
1005
+ "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
1006
+ );
1007
+ zChngPw = mprintf(
1008
+ "UPDATE user"
1009
+ " SET pw=shared_secret(%Q,%Q,"
1010
+ " (SELECT value FROM config WHERE name='project-code'))"
1011
+ " WHERE login=%Q",
1012
+ zNew1, g.zLogin, g.zLogin
1013
+ );
1014
+ fossil_free(zNewPw);
1015
+ rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr);
1016
+ db_protect_pop();
1017
+ /*
1018
+ ** ^^^^^^^^--- tag-20230106-2 ----^^^^^^^^^
1019
+ **
1020
+ ** Replicate changes above to tag-20230106-1
1021
+ */
1022
+
1023
+ if( rc ){
1024
+ @ <p><span class='loginError'>
1025
+ @ %s(zErr);
1026
+ @ </span></p>
1027
+ fossil_free(zErr);
1028
+ }else{
1029
+ redirect_to_g();
1030
+ return;
1031
+ }
1032
+ }
1033
+ }
1034
+ zRPW = fossil_random_password(12);
1035
+ @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
1036
+ form_begin(0, "%R/resetpw");
1037
+ @ <input type='hidden' name='name' value='%h(zName)'>
1038
+ @ <table>
1039
+ @ <tr><td class="form_label" id="newpw">New Password:</td>
1040
+ @ <td><input aria-labelledby="newpw" type="password" name="n1" \
1041
+ @ size="30" /> Suggestion: %z(zRPW)</td></tr>
1042
+ @ <tr><td class="form_label" id="reppw">Repeat New Password:</td>
1043
+ @ <td><input aria-labledby="reppw" type="password" name="n2" \
1044
+ @ size="30" /></td></tr>
1045
+ @ <tr><td></td>
1046
+ @ <td><input type="submit" value="Change Password" /></td></tr>
1047
+ @ </table>
1048
+ @ </form>
1049
+ style_finish_page();
1050
+}
8331051
8341052
/*
8351053
** Attempt to find login credentials for user zLogin on a peer repository
8361054
** with project code zCode. Transfer those credentials to the local
8371055
** repository.
@@ -999,11 +1217,10 @@
9991217
void login_check_credentials(void){
10001218
int uid = 0; /* User id */
10011219
const char *zCookie; /* Text of the login cookie */
10021220
const char *zIpAddr; /* Raw IP address of the requestor */
10031221
const char *zCap = 0; /* Capability string */
1004
- const char *zPublicPages = 0; /* GLOB patterns of public pages */
10051222
const char *zLogin = 0; /* Login user for credentials */
10061223
10071224
/* Only run this check once. */
10081225
if( g.userUid!=0 ) return;
10091226
@@ -1139,10 +1356,21 @@
11391356
zCap = "";
11401357
}
11411358
sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none");
11421359
}
11431360
1361
+ login_set_uid(uid, zCap);
1362
+}
1363
+
1364
+/*
1365
+** Set the current logged in user to be uid. zCap is precomputed
1366
+** (override) capabilities. If zCap==0, then look up the capabilities
1367
+** in the USER table.
1368
+*/
1369
+void login_set_uid(int uid, const char *zCap){
1370
+ const char *zPublicPages = 0; /* GLOB patterns of public pages */
1371
+
11441372
/* At this point, we know that uid!=0. Find the privileges associated
11451373
** with user uid.
11461374
*/
11471375
assert( uid!=0 );
11481376
if( zCap==0 ){
11491377
--- src/login.c
+++ src/login.c
@@ -613,10 +613,14 @@
613 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
614 char *zChngPw;
615 char *zErr;
616 int rc;
617
 
 
 
 
618 db_unprotect(PROTECT_USER);
619 db_multi_exec(
620 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
621 );
622 zChngPw = mprintf(
@@ -627,10 +631,16 @@
627 zNew1, g.zLogin, g.zLogin
628 );
629 fossil_free(zNewPw);
630 rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr);
631 db_protect_pop();
 
 
 
 
 
 
632 if( rc ){
633 zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
634 fossil_free(zErr);
635 }else{
636 redirect_to_g();
@@ -828,10 +838,218 @@
828 @ </form>
829 }
830 }
831 style_finish_page();
832 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
833
834 /*
835 ** Attempt to find login credentials for user zLogin on a peer repository
836 ** with project code zCode. Transfer those credentials to the local
837 ** repository.
@@ -999,11 +1217,10 @@
999 void login_check_credentials(void){
1000 int uid = 0; /* User id */
1001 const char *zCookie; /* Text of the login cookie */
1002 const char *zIpAddr; /* Raw IP address of the requestor */
1003 const char *zCap = 0; /* Capability string */
1004 const char *zPublicPages = 0; /* GLOB patterns of public pages */
1005 const char *zLogin = 0; /* Login user for credentials */
1006
1007 /* Only run this check once. */
1008 if( g.userUid!=0 ) return;
1009
@@ -1139,10 +1356,21 @@
1139 zCap = "";
1140 }
1141 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none");
1142 }
1143
 
 
 
 
 
 
 
 
 
 
 
1144 /* At this point, we know that uid!=0. Find the privileges associated
1145 ** with user uid.
1146 */
1147 assert( uid!=0 );
1148 if( zCap==0 ){
1149
--- src/login.c
+++ src/login.c
@@ -613,10 +613,14 @@
613 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
614 char *zChngPw;
615 char *zErr;
616 int rc;
617
618 /* vvvvvvv--- tag-20230106-1 ----vvvvvv
619 **
620 ** Replicate changes made below to tag-20230106-2
621 */
622 db_unprotect(PROTECT_USER);
623 db_multi_exec(
624 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
625 );
626 zChngPw = mprintf(
@@ -627,10 +631,16 @@
631 zNew1, g.zLogin, g.zLogin
632 );
633 fossil_free(zNewPw);
634 rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr);
635 db_protect_pop();
636 /*
637 ** ^^^^^^^^--- tag-20230106-1 ----^^^^^^^^^
638 **
639 ** Replicate changes above to tag-20230106-2
640 */
641
642 if( rc ){
643 zErrMsg = mprintf("<span class=\"loginError\">%s</span>", zErr);
644 fossil_free(zErr);
645 }else{
646 redirect_to_g();
@@ -828,10 +838,218 @@
838 @ </form>
839 }
840 }
841 style_finish_page();
842 }
843
844 /*
845 ** Construct an appropriate URL suffix for the /resetpw page. The
846 ** suffix will be of the form:
847 **
848 ** UID-TIMESTAMP-HASH
849 **
850 ** Where UID and TIMESTAMP are the parameters to this function, and HASH
851 ** is constructed from information that is unique to the user in question
852 ** and which is not publicly available. In particular, the HASH includes
853 ** the existing user password. Thus, in order to construct a URL that can
854 ** change a password, the attacker must know the current password, in which
855 ** case that do not need to construct the URL in order to take over the
856 ** account.
857 **
858 ** Return a pointer to the resulting string in memory obtained
859 ** from fossil_malloc().
860 */
861 char *login_resetpw_suffix(int uid, i64 timestamp){
862 char *zHash;
863 char *zInnerSql;
864 char *zResult;
865 extern int sqlite3_shathree_init(sqlite3*,char**,const sqlite3_api_routines*);
866 if( timestamp<=0 ){ timestamp = time(0); }
867 sqlite3_shathree_init(g.db, 0, 0);
868 if( db_table_exists("repository","subscriber") ){
869 zInnerSql = mprintf(
870 "SELECT %lld, login, pw, cookie, user.mtime, user.info, subscriberCode"
871 " FROM user LEFT JOIN subscriber ON suname=login"
872 " WHERE uid=%d", timestamp, uid);
873 }else{
874 zInnerSql = mprintf(
875 "SELECT %lld, login, pw, cookie, user.mtime, user.info"
876 " FROM user WHERE uid=%d", timestamp, uid);
877 }
878 zHash = db_text(0, "SELECT lower(hex(sha3_query(%Q)))", zInnerSql);
879 fossil_free(zInnerSql);
880 zResult = mprintf("%x-%llx-%s", uid, timestamp, zHash);
881 fossil_free(zHash);
882 return zResult;
883 }
884
885 /*
886 ** Check to see if the "name" query parameter is a valid resetpw suffix
887 ** for a user whose password we are allowed to reset. If it is, then return
888 ** the positive integer UID for that user. If the query parameter is not
889 ** valid, return 0.
890 */
891 static int login_resetpw_suffix_is_valid(const char *zName){
892 int i, j;
893 int uid;
894 i64 timestamp;
895 i64 now;
896 char *zHash;
897 for(i=0; fossil_isxdigit(zName[i]); i++){}
898 if( i<1 || zName[i]!='-' ) goto not_valid_suffix;
899 for(j=i+1; fossil_isxdigit(zName[j]); j++){}
900 if( j<=i+1 || zName[j]!='-' ) goto not_valid_suffix;
901 uid = strtol(zName, 0, 16);
902 if( uid<=0 ) goto not_valid_suffix;
903 if( !db_exists("SELECT 1 FROM user WHERE uid=%d", uid) ){
904 goto not_valid_suffix;
905 }
906 timestamp = strtoll(&zName[i+1], 0, 16);
907 now = time(0);
908 if( timestamp+3600 <= now ) goto not_valid_suffix;
909 zHash = login_resetpw_suffix(uid,timestamp);
910 if( fossil_strcmp(zHash, zName)!=0 ){
911 fossil_free(zHash);
912 goto not_valid_suffix;
913 }
914 fossil_free(zHash);
915 return uid;
916
917 not_valid_suffix:
918 sleep(2); /* Introduce a small delay on an invalid suffix as an
919 ** extra defense against search attacks */
920 return 0;
921 }
922
923 /*
924 ** COMMAND: test-resetpw-url
925 ** Usage: fossil test-resetpw-url UID
926 **
927 ** Generate and verify a /resetpw URL for user UID.
928 */
929 void test_resetpw_url(void){
930 char *zSuffix;
931 int uid;
932 db_find_and_open_repository(0, 0);
933 verify_all_options();
934 if( g.argc!=3 ){
935 usage("UID");
936 }
937 uid = atoi(g.argv[2]);
938 zSuffix = login_resetpw_suffix(uid, 0);
939 fossil_print("/resetpw/%s %d\n", zSuffix,
940 login_resetpw_suffix_is_valid(zSuffix));
941 fossil_free(zSuffix);
942 }
943
944 /*
945 ** WEBPAGE: resetpw
946 **
947 ** The URL format must be like this:
948 **
949 ** /resetpw/UID-TIMESTAMP-HASH
950 **
951 ** Where UID is the uid of the user whose password is to be reset,
952 ** TIMESTAMP is the unix timestamp when the request was made, and
953 ** HASH is a hash based on UID, TIMESTAMP, and other information that
954 ** is unavailable to an attacher.
955 **
956 ** With no other arguments, a form is present which allows the user to
957 ** enter a new password. When the SUBMIT button is pressed, a POST request
958 ** back to the same URL that will change the password.
959 */
960 void login_resetpw(void){
961 const char *zName;
962 int uid;
963 char *zRPW;
964 const char *zNew1, *zNew2;
965
966 style_set_current_feature("resetpw");
967 style_header("Reset Password");
968 style_adunit_config(ADUNIT_OFF);
969 zName = PD("name","");
970 uid = login_resetpw_suffix_is_valid(zName);
971 if( uid==0 ){
972 @ <p><span class="loginError">
973 @ This password-reset URL is invalid, probably because it has expired.
974 @ Password-reset URLs have a short lifespan.
975 @ </span></p>
976 style_finish_page();
977 return;
978 }
979 login_set_uid(uid, 0);
980 if( g.perm.Setup || g.perm.Admin || !g.perm.Password || g.zLogin==0 ){
981 @ <p><span class="loginError">
982 @ Cannot change the password for user <b>%h(g.zLogin)</b>.
983 @ </span></p>
984 style_finish_page();
985 return;
986 }
987 if( (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
988 if( fossil_strcmp(zNew1,zNew2)!=0 ){
989 @ <p><span class="loginError">
990 @ The two copies of your new passwords do not match.
991 @ Try again.
992 @ </span></p>
993 }else{
994 char *zNewPw = sha1_shared_secret(zNew1, g.zLogin, 0);
995 char *zChngPw;
996 char *zErr;
997 int rc;
998
999 /* vvvvvvv--- tag-20230106-2 ----vvvvvv
1000 **
1001 ** Replicate changes made below to tag-20230106-1
1002 */
1003 db_unprotect(PROTECT_USER);
1004 db_multi_exec(
1005 "UPDATE user SET pw=%Q WHERE uid=%d", zNewPw, g.userUid
1006 );
1007 zChngPw = mprintf(
1008 "UPDATE user"
1009 " SET pw=shared_secret(%Q,%Q,"
1010 " (SELECT value FROM config WHERE name='project-code'))"
1011 " WHERE login=%Q",
1012 zNew1, g.zLogin, g.zLogin
1013 );
1014 fossil_free(zNewPw);
1015 rc = login_group_sql(zChngPw, "<p>", "</p>\n", &zErr);
1016 db_protect_pop();
1017 /*
1018 ** ^^^^^^^^--- tag-20230106-2 ----^^^^^^^^^
1019 **
1020 ** Replicate changes above to tag-20230106-1
1021 */
1022
1023 if( rc ){
1024 @ <p><span class='loginError'>
1025 @ %s(zErr);
1026 @ </span></p>
1027 fossil_free(zErr);
1028 }else{
1029 redirect_to_g();
1030 return;
1031 }
1032 }
1033 }
1034 zRPW = fossil_random_password(12);
1035 @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
1036 form_begin(0, "%R/resetpw");
1037 @ <input type='hidden' name='name' value='%h(zName)'>
1038 @ <table>
1039 @ <tr><td class="form_label" id="newpw">New Password:</td>
1040 @ <td><input aria-labelledby="newpw" type="password" name="n1" \
1041 @ size="30" /> Suggestion: %z(zRPW)</td></tr>
1042 @ <tr><td class="form_label" id="reppw">Repeat New Password:</td>
1043 @ <td><input aria-labledby="reppw" type="password" name="n2" \
1044 @ size="30" /></td></tr>
1045 @ <tr><td></td>
1046 @ <td><input type="submit" value="Change Password" /></td></tr>
1047 @ </table>
1048 @ </form>
1049 style_finish_page();
1050 }
1051
1052 /*
1053 ** Attempt to find login credentials for user zLogin on a peer repository
1054 ** with project code zCode. Transfer those credentials to the local
1055 ** repository.
@@ -999,11 +1217,10 @@
1217 void login_check_credentials(void){
1218 int uid = 0; /* User id */
1219 const char *zCookie; /* Text of the login cookie */
1220 const char *zIpAddr; /* Raw IP address of the requestor */
1221 const char *zCap = 0; /* Capability string */
 
1222 const char *zLogin = 0; /* Login user for credentials */
1223
1224 /* Only run this check once. */
1225 if( g.userUid!=0 ) return;
1226
@@ -1139,10 +1356,21 @@
1356 zCap = "";
1357 }
1358 sqlite3_snprintf(sizeof(g.zCsrfToken), g.zCsrfToken, "none");
1359 }
1360
1361 login_set_uid(uid, zCap);
1362 }
1363
1364 /*
1365 ** Set the current logged in user to be uid. zCap is precomputed
1366 ** (override) capabilities. If zCap==0, then look up the capabilities
1367 ** in the USER table.
1368 */
1369 void login_set_uid(int uid, const char *zCap){
1370 const char *zPublicPages = 0; /* GLOB patterns of public pages */
1371
1372 /* At this point, we know that uid!=0. Find the privileges associated
1373 ** with user uid.
1374 */
1375 assert( uid!=0 );
1376 if( zCap==0 ){
1377

Keyboard Shortcuts

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