Fossil SCM
Harden the synchronization process against sending or receiving settings flagged as sensitive. Mark the th1-setup setting as sensitive because it can contain not only th1, but also arbitrary TCL code if fossil is configured using --with-tcl. This addresses [forum:6179500deadf6ec7 | forum post 6179500dead].
Commit
2ff87d4e0f7864117cf312ea4f42b8b3da7dd01a743c34c24a955bbecc4a494d
Parent
d7d106227fad4ae…
2 files changed
+28
-9
+1
-1
+28
-9
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -118,24 +118,15 @@ | ||
| 118 | 118 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 119 | 119 | { "default-csp", CONFIGSET_SKIN }, |
| 120 | 120 | { "sitemap-extra", CONFIGSET_SKIN }, |
| 121 | 121 | { "safe-html", CONFIGSET_SKIN }, |
| 122 | 122 | |
| 123 | -#ifdef FOSSIL_ENABLE_TH1_DOCS | |
| 124 | - { "th1-docs", CONFIGSET_TH1 }, | |
| 125 | -#endif | |
| 126 | 123 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 127 | 124 | { "th1-hooks", CONFIGSET_TH1 }, |
| 128 | 125 | #endif |
| 129 | - { "th1-setup", CONFIGSET_TH1 }, | |
| 130 | 126 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 131 | 127 | |
| 132 | -#ifdef FOSSIL_ENABLE_TCL | |
| 133 | - { "tcl", CONFIGSET_TH1 }, | |
| 134 | - { "tcl-setup", CONFIGSET_TH1 }, | |
| 135 | -#endif | |
| 136 | - | |
| 137 | 128 | { "project-name", CONFIGSET_PROJ }, |
| 138 | 129 | { "short-project-name", CONFIGSET_PROJ }, |
| 139 | 130 | { "project-description", CONFIGSET_PROJ }, |
| 140 | 131 | { "index-page", CONFIGSET_PROJ }, |
| 141 | 132 | { "manifest", CONFIGSET_PROJ }, |
| @@ -239,17 +230,35 @@ | ||
| 239 | 230 | ** |
| 240 | 231 | ** "Safe" in the previous paragraph means the permission is granted to |
| 241 | 232 | ** export the property. In other words, the requesting side has presented |
| 242 | 233 | ** login credentials and has sufficient capabilities to access the requested |
| 243 | 234 | ** information. |
| 235 | +** | |
| 236 | +** Settings which are specifically flagged as sensitive will (as of | |
| 237 | +** 2024-10-15) cause this function to return 0, regardless of user | |
| 238 | +** permissions. As an example, if the th1-setup setting were not | |
| 239 | +** sensitive then a malicious repo admin could set that to include | |
| 240 | +** arbitrary TCL code and affect users who configure fossil with the | |
| 241 | +** --with-tcl flag. | |
| 244 | 242 | */ |
| 245 | 243 | int configure_is_exportable(const char *zName){ |
| 246 | 244 | int i; |
| 247 | 245 | int n = strlen(zName); |
| 246 | + Setting *pSet; | |
| 248 | 247 | if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ |
| 248 | + char * zCpy; | |
| 249 | 249 | zName++; |
| 250 | 250 | n -= 2; |
| 251 | + zCpy = fossil_strndup(zName, (ssize_t)n); | |
| 252 | + pSet = db_find_setting(zCpy, 0); | |
| 253 | + fossil_free(zCpy); | |
| 254 | + }else{ | |
| 255 | + pSet = db_find_setting(zName, 0); | |
| 256 | + } | |
| 257 | + if( pSet && pSet->sensitive ){ | |
| 258 | + /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ | |
| 259 | + return 0; | |
| 251 | 260 | } |
| 252 | 261 | for(i=0; i<count(aConfig); i++){ |
| 253 | 262 | if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ |
| 254 | 263 | int m = aConfig[i].groupMask; |
| 255 | 264 | if( !g.perm.Admin ){ |
| @@ -414,10 +423,15 @@ | ||
| 414 | 423 | if( nToken>=count(azToken)-1 ) break; |
| 415 | 424 | } |
| 416 | 425 | if( nToken<2 ) return; |
| 417 | 426 | if( aType[ii].zName[0]=='/' ){ |
| 418 | 427 | thisMask = configure_is_exportable(azToken[1]); |
| 428 | + if( 0==thisMask ){ | |
| 429 | + fossil_warning("Skipping non-exportable setting: %s = %s", | |
| 430 | + azToken[1], nToken>3 ? azToken[3] : "?"); | |
| 431 | + /* Will be skipped below */ | |
| 432 | + } | |
| 419 | 433 | }else{ |
| 420 | 434 | thisMask = configure_is_exportable(aType[ii].zName); |
| 421 | 435 | } |
| 422 | 436 | if( (thisMask & groupMask)==0 ) return; |
| 423 | 437 | if( (thisMask & checkMask)!=0 ){ |
| @@ -681,10 +695,15 @@ | ||
| 681 | 695 | } |
| 682 | 696 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| 683 | 697 | " WHERE name=:name AND mtime>=%lld", iStart); |
| 684 | 698 | for(ii=0; ii<count(aConfig); ii++){ |
| 685 | 699 | if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ |
| 700 | + const Setting * pSet = db_find_setting(aConfig[ii].zName, 0); | |
| 701 | + if( pSet && pSet->sensitive ){ | |
| 702 | + /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ | |
| 703 | + continue; | |
| 704 | + } | |
| 686 | 705 | db_bind_text(&q, ":name", aConfig[ii].zName); |
| 687 | 706 | while( db_step(&q)==SQLITE_ROW ){ |
| 688 | 707 | blob_appendf(&rec,"%s %s value %s", |
| 689 | 708 | db_column_text(&q, 0), |
| 690 | 709 | db_column_text(&q, 1), |
| 691 | 710 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -118,24 +118,15 @@ | |
| 118 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 119 | { "default-csp", CONFIGSET_SKIN }, |
| 120 | { "sitemap-extra", CONFIGSET_SKIN }, |
| 121 | { "safe-html", CONFIGSET_SKIN }, |
| 122 | |
| 123 | #ifdef FOSSIL_ENABLE_TH1_DOCS |
| 124 | { "th1-docs", CONFIGSET_TH1 }, |
| 125 | #endif |
| 126 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 127 | { "th1-hooks", CONFIGSET_TH1 }, |
| 128 | #endif |
| 129 | { "th1-setup", CONFIGSET_TH1 }, |
| 130 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 131 | |
| 132 | #ifdef FOSSIL_ENABLE_TCL |
| 133 | { "tcl", CONFIGSET_TH1 }, |
| 134 | { "tcl-setup", CONFIGSET_TH1 }, |
| 135 | #endif |
| 136 | |
| 137 | { "project-name", CONFIGSET_PROJ }, |
| 138 | { "short-project-name", CONFIGSET_PROJ }, |
| 139 | { "project-description", CONFIGSET_PROJ }, |
| 140 | { "index-page", CONFIGSET_PROJ }, |
| 141 | { "manifest", CONFIGSET_PROJ }, |
| @@ -239,17 +230,35 @@ | |
| 239 | ** |
| 240 | ** "Safe" in the previous paragraph means the permission is granted to |
| 241 | ** export the property. In other words, the requesting side has presented |
| 242 | ** login credentials and has sufficient capabilities to access the requested |
| 243 | ** information. |
| 244 | */ |
| 245 | int configure_is_exportable(const char *zName){ |
| 246 | int i; |
| 247 | int n = strlen(zName); |
| 248 | if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ |
| 249 | zName++; |
| 250 | n -= 2; |
| 251 | } |
| 252 | for(i=0; i<count(aConfig); i++){ |
| 253 | if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ |
| 254 | int m = aConfig[i].groupMask; |
| 255 | if( !g.perm.Admin ){ |
| @@ -414,10 +423,15 @@ | |
| 414 | if( nToken>=count(azToken)-1 ) break; |
| 415 | } |
| 416 | if( nToken<2 ) return; |
| 417 | if( aType[ii].zName[0]=='/' ){ |
| 418 | thisMask = configure_is_exportable(azToken[1]); |
| 419 | }else{ |
| 420 | thisMask = configure_is_exportable(aType[ii].zName); |
| 421 | } |
| 422 | if( (thisMask & groupMask)==0 ) return; |
| 423 | if( (thisMask & checkMask)!=0 ){ |
| @@ -681,10 +695,15 @@ | |
| 681 | } |
| 682 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| 683 | " WHERE name=:name AND mtime>=%lld", iStart); |
| 684 | for(ii=0; ii<count(aConfig); ii++){ |
| 685 | if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ |
| 686 | db_bind_text(&q, ":name", aConfig[ii].zName); |
| 687 | while( db_step(&q)==SQLITE_ROW ){ |
| 688 | blob_appendf(&rec,"%s %s value %s", |
| 689 | db_column_text(&q, 0), |
| 690 | db_column_text(&q, 1), |
| 691 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -118,24 +118,15 @@ | |
| 118 | { "adunit-omit-if-user", CONFIGSET_SKIN }, |
| 119 | { "default-csp", CONFIGSET_SKIN }, |
| 120 | { "sitemap-extra", CONFIGSET_SKIN }, |
| 121 | { "safe-html", CONFIGSET_SKIN }, |
| 122 | |
| 123 | #ifdef FOSSIL_ENABLE_TH1_HOOKS |
| 124 | { "th1-hooks", CONFIGSET_TH1 }, |
| 125 | #endif |
| 126 | { "th1-uri-regexp", CONFIGSET_TH1 }, |
| 127 | |
| 128 | { "project-name", CONFIGSET_PROJ }, |
| 129 | { "short-project-name", CONFIGSET_PROJ }, |
| 130 | { "project-description", CONFIGSET_PROJ }, |
| 131 | { "index-page", CONFIGSET_PROJ }, |
| 132 | { "manifest", CONFIGSET_PROJ }, |
| @@ -239,17 +230,35 @@ | |
| 230 | ** |
| 231 | ** "Safe" in the previous paragraph means the permission is granted to |
| 232 | ** export the property. In other words, the requesting side has presented |
| 233 | ** login credentials and has sufficient capabilities to access the requested |
| 234 | ** information. |
| 235 | ** |
| 236 | ** Settings which are specifically flagged as sensitive will (as of |
| 237 | ** 2024-10-15) cause this function to return 0, regardless of user |
| 238 | ** permissions. As an example, if the th1-setup setting were not |
| 239 | ** sensitive then a malicious repo admin could set that to include |
| 240 | ** arbitrary TCL code and affect users who configure fossil with the |
| 241 | ** --with-tcl flag. |
| 242 | */ |
| 243 | int configure_is_exportable(const char *zName){ |
| 244 | int i; |
| 245 | int n = strlen(zName); |
| 246 | Setting *pSet; |
| 247 | if( n>2 && zName[0]=='\'' && zName[n-1]=='\'' ){ |
| 248 | char * zCpy; |
| 249 | zName++; |
| 250 | n -= 2; |
| 251 | zCpy = fossil_strndup(zName, (ssize_t)n); |
| 252 | pSet = db_find_setting(zCpy, 0); |
| 253 | fossil_free(zCpy); |
| 254 | }else{ |
| 255 | pSet = db_find_setting(zName, 0); |
| 256 | } |
| 257 | if( pSet && pSet->sensitive ){ |
| 258 | /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ |
| 259 | return 0; |
| 260 | } |
| 261 | for(i=0; i<count(aConfig); i++){ |
| 262 | if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ |
| 263 | int m = aConfig[i].groupMask; |
| 264 | if( !g.perm.Admin ){ |
| @@ -414,10 +423,15 @@ | |
| 423 | if( nToken>=count(azToken)-1 ) break; |
| 424 | } |
| 425 | if( nToken<2 ) return; |
| 426 | if( aType[ii].zName[0]=='/' ){ |
| 427 | thisMask = configure_is_exportable(azToken[1]); |
| 428 | if( 0==thisMask ){ |
| 429 | fossil_warning("Skipping non-exportable setting: %s = %s", |
| 430 | azToken[1], nToken>3 ? azToken[3] : "?"); |
| 431 | /* Will be skipped below */ |
| 432 | } |
| 433 | }else{ |
| 434 | thisMask = configure_is_exportable(aType[ii].zName); |
| 435 | } |
| 436 | if( (thisMask & groupMask)==0 ) return; |
| 437 | if( (thisMask & checkMask)!=0 ){ |
| @@ -681,10 +695,15 @@ | |
| 695 | } |
| 696 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| 697 | " WHERE name=:name AND mtime>=%lld", iStart); |
| 698 | for(ii=0; ii<count(aConfig); ii++){ |
| 699 | if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ |
| 700 | const Setting * pSet = db_find_setting(aConfig[ii].zName, 0); |
| 701 | if( pSet && pSet->sensitive ){ |
| 702 | /* https://fossil-scm.org/forum/forumpost/6179500deadf6ec7 */ |
| 703 | continue; |
| 704 | } |
| 705 | db_bind_text(&q, ":name", aConfig[ii].zName); |
| 706 | while( db_step(&q)==SQLITE_ROW ){ |
| 707 | blob_appendf(&rec,"%s %s value %s", |
| 708 | db_column_text(&q, 0), |
| 709 | db_column_text(&q, 1), |
| 710 |
M
src/db.c
+1
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -4967,11 +4967,11 @@ | ||
| 4967 | 4967 | ** If enabled, special TH1 commands will be called before and |
| 4968 | 4968 | ** after any Fossil command or web page. |
| 4969 | 4969 | */ |
| 4970 | 4970 | #endif |
| 4971 | 4971 | /* |
| 4972 | -** SETTING: th1-setup width=40 block-text | |
| 4972 | +** SETTING: th1-setup width=40 block-text sensitive | |
| 4973 | 4973 | ** This is the setup script to be evaluated after creating |
| 4974 | 4974 | ** and initializing the TH1 interpreter. By default, this |
| 4975 | 4975 | ** is empty and no extra setup is performed. |
| 4976 | 4976 | */ |
| 4977 | 4977 | /* |
| 4978 | 4978 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -4967,11 +4967,11 @@ | |
| 4967 | ** If enabled, special TH1 commands will be called before and |
| 4968 | ** after any Fossil command or web page. |
| 4969 | */ |
| 4970 | #endif |
| 4971 | /* |
| 4972 | ** SETTING: th1-setup width=40 block-text |
| 4973 | ** This is the setup script to be evaluated after creating |
| 4974 | ** and initializing the TH1 interpreter. By default, this |
| 4975 | ** is empty and no extra setup is performed. |
| 4976 | */ |
| 4977 | /* |
| 4978 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -4967,11 +4967,11 @@ | |
| 4967 | ** If enabled, special TH1 commands will be called before and |
| 4968 | ** after any Fossil command or web page. |
| 4969 | */ |
| 4970 | #endif |
| 4971 | /* |
| 4972 | ** SETTING: th1-setup width=40 block-text sensitive |
| 4973 | ** This is the setup script to be evaluated after creating |
| 4974 | ** and initializing the TH1 interpreter. By default, this |
| 4975 | ** is empty and no extra setup is performed. |
| 4976 | */ |
| 4977 | /* |
| 4978 |