Fossil SCM
Enhance the ssh:// URL to be cautious about the fossil= query parameter. Only commands "fossil" and "echo" (with an optional path) are accepted.
Commit
cb43937d8c1232d3f9f0cb18bc20b41ce8d47f7c09e703d1f32df608df83fa03
Parent
d5b015946d85ba4…
1 file changed
+19
+19
| --- src/http_transport.c | ||
| +++ src/http_transport.c | ||
| @@ -73,10 +73,25 @@ | ||
| 73 | 73 | if( resetFlag ){ |
| 74 | 74 | transport.nSent = 0; |
| 75 | 75 | transport.nRcvd = 0; |
| 76 | 76 | } |
| 77 | 77 | } |
| 78 | + | |
| 79 | +/* | |
| 80 | +** Check zFossil to see if it is a reasonable "fossil" command to | |
| 81 | +** run on the server. Do not allow an attacker to substitute something | |
| 82 | +** like "/bin/rm". | |
| 83 | +*/ | |
| 84 | +static int is_safe_fossil_command(const char *zFossil){ | |
| 85 | + static const char *azSafe[] = { "*/fossil", "*/echo" }; | |
| 86 | + int i; | |
| 87 | + for(i=0; i<sizeof(azSafe)/sizeof(azSafe[0]); i++){ | |
| 88 | + if( sqlite3_strglob(azSafe[i], zFossil)==0 ) return 1; | |
| 89 | + if( strcmp(azSafe[i]+2, zFossil)==0 ) return 1; | |
| 90 | + } | |
| 91 | + return 0; | |
| 92 | +} | |
| 78 | 93 | |
| 79 | 94 | /* |
| 80 | 95 | ** Default SSH command |
| 81 | 96 | */ |
| 82 | 97 | #ifdef _WIN32 |
| @@ -110,10 +125,14 @@ | ||
| 110 | 125 | zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); |
| 111 | 126 | blob_append_escaped_arg(&zCmd, zHost); |
| 112 | 127 | fossil_free(zHost); |
| 113 | 128 | }else{ |
| 114 | 129 | blob_append_escaped_arg(&zCmd, pUrlData->name); |
| 130 | + } | |
| 131 | + if( !is_safe_fossil_command(pUrlData->fossil) ){ | |
| 132 | + fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on " | |
| 133 | + "the server.", pUrlData->fossil); | |
| 115 | 134 | } |
| 116 | 135 | blob_append_escaped_arg(&zCmd, pUrlData->fossil); |
| 117 | 136 | blob_append(&zCmd, " test-http", 10); |
| 118 | 137 | if( pUrlData->path && pUrlData->path[0] ){ |
| 119 | 138 | blob_append_escaped_arg(&zCmd, pUrlData->path); |
| 120 | 139 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -73,10 +73,25 @@ | |
| 73 | if( resetFlag ){ |
| 74 | transport.nSent = 0; |
| 75 | transport.nRcvd = 0; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | /* |
| 80 | ** Default SSH command |
| 81 | */ |
| 82 | #ifdef _WIN32 |
| @@ -110,10 +125,14 @@ | |
| 110 | zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); |
| 111 | blob_append_escaped_arg(&zCmd, zHost); |
| 112 | fossil_free(zHost); |
| 113 | }else{ |
| 114 | blob_append_escaped_arg(&zCmd, pUrlData->name); |
| 115 | } |
| 116 | blob_append_escaped_arg(&zCmd, pUrlData->fossil); |
| 117 | blob_append(&zCmd, " test-http", 10); |
| 118 | if( pUrlData->path && pUrlData->path[0] ){ |
| 119 | blob_append_escaped_arg(&zCmd, pUrlData->path); |
| 120 |
| --- src/http_transport.c | |
| +++ src/http_transport.c | |
| @@ -73,10 +73,25 @@ | |
| 73 | if( resetFlag ){ |
| 74 | transport.nSent = 0; |
| 75 | transport.nRcvd = 0; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | /* |
| 80 | ** Check zFossil to see if it is a reasonable "fossil" command to |
| 81 | ** run on the server. Do not allow an attacker to substitute something |
| 82 | ** like "/bin/rm". |
| 83 | */ |
| 84 | static int is_safe_fossil_command(const char *zFossil){ |
| 85 | static const char *azSafe[] = { "*/fossil", "*/echo" }; |
| 86 | int i; |
| 87 | for(i=0; i<sizeof(azSafe)/sizeof(azSafe[0]); i++){ |
| 88 | if( sqlite3_strglob(azSafe[i], zFossil)==0 ) return 1; |
| 89 | if( strcmp(azSafe[i]+2, zFossil)==0 ) return 1; |
| 90 | } |
| 91 | return 0; |
| 92 | } |
| 93 | |
| 94 | /* |
| 95 | ** Default SSH command |
| 96 | */ |
| 97 | #ifdef _WIN32 |
| @@ -110,10 +125,14 @@ | |
| 125 | zHost = mprintf("%s@%s", pUrlData->user, pUrlData->name); |
| 126 | blob_append_escaped_arg(&zCmd, zHost); |
| 127 | fossil_free(zHost); |
| 128 | }else{ |
| 129 | blob_append_escaped_arg(&zCmd, pUrlData->name); |
| 130 | } |
| 131 | if( !is_safe_fossil_command(pUrlData->fossil) ){ |
| 132 | fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on " |
| 133 | "the server.", pUrlData->fossil); |
| 134 | } |
| 135 | blob_append_escaped_arg(&zCmd, pUrlData->fossil); |
| 136 | blob_append(&zCmd, " test-http", 10); |
| 137 | if( pUrlData->path && pUrlData->path[0] ){ |
| 138 | blob_append_escaped_arg(&zCmd, pUrlData->path); |
| 139 |