Fossil SCM
Add a safety scan of all strings passed into fossil_system(). Fatal error if the input string seems unsafe.
Commit
a046f916d3e3be3d5b6b17e851d484a20caebd563f8bed8b3ce5e3b171f95fce
Parent
57d8a71f4257418…
1 file changed
+60
+60
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -155,10 +155,68 @@ | ||
| 155 | 155 | zIn++; |
| 156 | 156 | } |
| 157 | 157 | } |
| 158 | 158 | return zStart; |
| 159 | 159 | } |
| 160 | + | |
| 161 | +/* | |
| 162 | +** Check the input string to ensure that it is safe to pass into system(). | |
| 163 | +** A string is unsafe for system() if it contains any of the following: | |
| 164 | +** | |
| 165 | +** * Any occurrance of '$' or '`' except after \ | |
| 166 | +** * Any of the following characters, unquoted: ;|& or \n | |
| 167 | +** * Unbalanced single or double quotes | |
| 168 | +** | |
| 169 | +** This routine is intended as a second line of defense against attack. | |
| 170 | +** It should never fail. Dangerous shell strings should be detected and | |
| 171 | +** fixed before calling fossil_system(). This routine serves only as a | |
| 172 | +** safety net in case of bugs elsewhere in the system. | |
| 173 | +** | |
| 174 | +** If an unsafe string is seen, the process aborts. | |
| 175 | +*/ | |
| 176 | +void fossil_assert_safe_command_string(const char *z){ | |
| 177 | + int inQuote = 0; | |
| 178 | + int i, c; | |
| 179 | + int unsafe = 0; | |
| 180 | + for(i=0; (c = z[i])!=0; i++){ | |
| 181 | + switch( c ){ | |
| 182 | + case '$': | |
| 183 | + case '`': { | |
| 184 | + unsafe = i+1; | |
| 185 | + break; | |
| 186 | + } | |
| 187 | + case ';': | |
| 188 | + case '|': | |
| 189 | + case '&': | |
| 190 | + case '\n': { | |
| 191 | + if( inQuote==0 ) unsafe = i+1; | |
| 192 | + break; | |
| 193 | + } | |
| 194 | + case '"': | |
| 195 | + case '\'': { | |
| 196 | + if( inQuote==0 ){ | |
| 197 | + inQuote = c; | |
| 198 | + }else if( inQuote==c ){ | |
| 199 | + inQuote = 0; | |
| 200 | + } | |
| 201 | + break; | |
| 202 | + } | |
| 203 | + case '\\': { | |
| 204 | + if( z[i+1]==0 ){ | |
| 205 | + unsafe = i+1; | |
| 206 | + }else{ | |
| 207 | + i++; | |
| 208 | + } | |
| 209 | + break; | |
| 210 | + } | |
| 211 | + } | |
| 212 | + } | |
| 213 | + if( unsafe ){ | |
| 214 | + fossil_fatal("Unsafe command string: %s\n%*shere ----^", | |
| 215 | + z, unsafe+13, ""); | |
| 216 | + } | |
| 217 | +} | |
| 160 | 218 | |
| 161 | 219 | /* |
| 162 | 220 | ** This function implements a cross-platform "system()" interface. |
| 163 | 221 | */ |
| 164 | 222 | int fossil_system(const char *zOrigCmd){ |
| @@ -170,17 +228,19 @@ | ||
| 170 | 228 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 171 | 229 | wchar_t *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 172 | 230 | if( g.fSystemTrace ) { |
| 173 | 231 | fossil_trace("SYSTEM: %s\n", zNewCmd); |
| 174 | 232 | } |
| 233 | + fossil_assert_safe_command_string(zOrigCmd); | |
| 175 | 234 | rc = _wsystem(zUnicode); |
| 176 | 235 | fossil_unicode_free(zUnicode); |
| 177 | 236 | free(zNewCmd); |
| 178 | 237 | #else |
| 179 | 238 | /* On unix, evaluate the command directly. |
| 180 | 239 | */ |
| 181 | 240 | if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd); |
| 241 | + fossil_assert_safe_command_string(zOrigCmd); | |
| 182 | 242 | |
| 183 | 243 | /* Unix systems should never shell-out while processing an HTTP request, |
| 184 | 244 | ** either via CGI, SCGI, or direct HTTP. The following assert verifies |
| 185 | 245 | ** this. And the following assert proves that Fossil is not vulnerable |
| 186 | 246 | ** to the ShellShock or BashDoor bug. |
| 187 | 247 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -155,10 +155,68 @@ | |
| 155 | zIn++; |
| 156 | } |
| 157 | } |
| 158 | return zStart; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** This function implements a cross-platform "system()" interface. |
| 163 | */ |
| 164 | int fossil_system(const char *zOrigCmd){ |
| @@ -170,17 +228,19 @@ | |
| 170 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 171 | wchar_t *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 172 | if( g.fSystemTrace ) { |
| 173 | fossil_trace("SYSTEM: %s\n", zNewCmd); |
| 174 | } |
| 175 | rc = _wsystem(zUnicode); |
| 176 | fossil_unicode_free(zUnicode); |
| 177 | free(zNewCmd); |
| 178 | #else |
| 179 | /* On unix, evaluate the command directly. |
| 180 | */ |
| 181 | if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd); |
| 182 | |
| 183 | /* Unix systems should never shell-out while processing an HTTP request, |
| 184 | ** either via CGI, SCGI, or direct HTTP. The following assert verifies |
| 185 | ** this. And the following assert proves that Fossil is not vulnerable |
| 186 | ** to the ShellShock or BashDoor bug. |
| 187 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -155,10 +155,68 @@ | |
| 155 | zIn++; |
| 156 | } |
| 157 | } |
| 158 | return zStart; |
| 159 | } |
| 160 | |
| 161 | /* |
| 162 | ** Check the input string to ensure that it is safe to pass into system(). |
| 163 | ** A string is unsafe for system() if it contains any of the following: |
| 164 | ** |
| 165 | ** * Any occurrance of '$' or '`' except after \ |
| 166 | ** * Any of the following characters, unquoted: ;|& or \n |
| 167 | ** * Unbalanced single or double quotes |
| 168 | ** |
| 169 | ** This routine is intended as a second line of defense against attack. |
| 170 | ** It should never fail. Dangerous shell strings should be detected and |
| 171 | ** fixed before calling fossil_system(). This routine serves only as a |
| 172 | ** safety net in case of bugs elsewhere in the system. |
| 173 | ** |
| 174 | ** If an unsafe string is seen, the process aborts. |
| 175 | */ |
| 176 | void fossil_assert_safe_command_string(const char *z){ |
| 177 | int inQuote = 0; |
| 178 | int i, c; |
| 179 | int unsafe = 0; |
| 180 | for(i=0; (c = z[i])!=0; i++){ |
| 181 | switch( c ){ |
| 182 | case '$': |
| 183 | case '`': { |
| 184 | unsafe = i+1; |
| 185 | break; |
| 186 | } |
| 187 | case ';': |
| 188 | case '|': |
| 189 | case '&': |
| 190 | case '\n': { |
| 191 | if( inQuote==0 ) unsafe = i+1; |
| 192 | break; |
| 193 | } |
| 194 | case '"': |
| 195 | case '\'': { |
| 196 | if( inQuote==0 ){ |
| 197 | inQuote = c; |
| 198 | }else if( inQuote==c ){ |
| 199 | inQuote = 0; |
| 200 | } |
| 201 | break; |
| 202 | } |
| 203 | case '\\': { |
| 204 | if( z[i+1]==0 ){ |
| 205 | unsafe = i+1; |
| 206 | }else{ |
| 207 | i++; |
| 208 | } |
| 209 | break; |
| 210 | } |
| 211 | } |
| 212 | } |
| 213 | if( unsafe ){ |
| 214 | fossil_fatal("Unsafe command string: %s\n%*shere ----^", |
| 215 | z, unsafe+13, ""); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | /* |
| 220 | ** This function implements a cross-platform "system()" interface. |
| 221 | */ |
| 222 | int fossil_system(const char *zOrigCmd){ |
| @@ -170,17 +228,19 @@ | |
| 228 | char *zNewCmd = mprintf("\"%s\"", zOrigCmd); |
| 229 | wchar_t *zUnicode = fossil_utf8_to_unicode(zNewCmd); |
| 230 | if( g.fSystemTrace ) { |
| 231 | fossil_trace("SYSTEM: %s\n", zNewCmd); |
| 232 | } |
| 233 | fossil_assert_safe_command_string(zOrigCmd); |
| 234 | rc = _wsystem(zUnicode); |
| 235 | fossil_unicode_free(zUnicode); |
| 236 | free(zNewCmd); |
| 237 | #else |
| 238 | /* On unix, evaluate the command directly. |
| 239 | */ |
| 240 | if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd); |
| 241 | fossil_assert_safe_command_string(zOrigCmd); |
| 242 | |
| 243 | /* Unix systems should never shell-out while processing an HTTP request, |
| 244 | ** either via CGI, SCGI, or direct HTTP. The following assert verifies |
| 245 | ** this. And the following assert proves that Fossil is not vulnerable |
| 246 | ** to the ShellShock or BashDoor bug. |
| 247 |