Fossil SCM
Limit the number of HTTP redirects that any http_exchange() call will follow to 20 (the limit used by most browsers). Previously, a misconfigured server or incorrect URL could cause Fossil to follow an endless trail of redirects without user intervention.
Commit
13ffb9b4d1eb3f97b9704d341a3f65eaadce1cc3
Parent
ba86c859dff83e8…
1 file changed
+19
-1
+19
-1
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -120,10 +120,16 @@ | ||
| 120 | 120 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| 121 | 121 | } |
| 122 | 122 | blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload)); |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | +/* | |
| 126 | +** Maximum number of HTTP redirects that any http_exchange() call will | |
| 127 | +** follow before throwing a fatal error. Most browsers use a limit of 20. | |
| 128 | +*/ | |
| 129 | +#define MAX_REDIRECTS 20 | |
| 130 | + | |
| 125 | 131 | /* |
| 126 | 132 | ** Sign the content in pSend, compress it, and send it to the server |
| 127 | 133 | ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply |
| 128 | 134 | ** in pRecv. pRecv is assumed to be uninitialized when |
| 129 | 135 | ** this routine is called - this routine will initialize it. |
| @@ -131,10 +137,19 @@ | ||
| 131 | 137 | ** The server address is contain in the "g" global structure. The |
| 132 | 138 | ** url_parse() routine should have been called prior to this routine |
| 133 | 139 | ** in order to fill this structure appropriately. |
| 134 | 140 | */ |
| 135 | 141 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin){ |
| 142 | + return _http_exchange(pSend, pReply, useLogin, 0); | |
| 143 | +} | |
| 144 | + | |
| 145 | +/* | |
| 146 | +** Actual implementation details of http_exchange(). This is done so we can | |
| 147 | +** track the number of redirects without changing the signature of that | |
| 148 | +** function and requiring callers to initialize numRedirects. | |
| 149 | +*/ | |
| 150 | +int _http_exchange(Blob *pSend, Blob *pReply, int useLogin, int numRedirects){ | |
| 136 | 151 | Blob login; /* The login card */ |
| 137 | 152 | Blob payload; /* The complete payload including login card */ |
| 138 | 153 | Blob hdr; /* The HTTP request header */ |
| 139 | 154 | int closeConnection; /* True to close the connection when done */ |
| 140 | 155 | int iLength; /* Length of the reply payload */ |
| @@ -230,10 +245,13 @@ | ||
| 230 | 245 | closeConnection = 1; |
| 231 | 246 | }else if( c=='k' || c=='K' ){ |
| 232 | 247 | closeConnection = 0; |
| 233 | 248 | } |
| 234 | 249 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 250 | + if ( numRedirects>=MAX_REDIRECTS ){ | |
| 251 | + fossil_fatal("redirect limit exceeded"); | |
| 252 | + } | |
| 235 | 253 | int i, j; |
| 236 | 254 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 237 | 255 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 238 | 256 | j = strlen(zLine) - 1; |
| 239 | 257 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| @@ -241,11 +259,11 @@ | ||
| 241 | 259 | zLine[j] = 0; |
| 242 | 260 | } |
| 243 | 261 | fossil_print("redirect to %s\n", &zLine[i]); |
| 244 | 262 | url_parse(&zLine[i]); |
| 245 | 263 | transport_close(); |
| 246 | - return http_exchange(pSend, pReply, useLogin); | |
| 264 | + return _http_exchange(pSend, pReply, useLogin, numRedirects + 1); | |
| 247 | 265 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 248 | 266 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 249 | 267 | isCompressed = 0; |
| 250 | 268 | }else if( fossil_strnicmp(&zLine[14], |
| 251 | 269 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 252 | 270 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -120,10 +120,16 @@ | |
| 120 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| 121 | } |
| 122 | blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload)); |
| 123 | } |
| 124 | |
| 125 | /* |
| 126 | ** Sign the content in pSend, compress it, and send it to the server |
| 127 | ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply |
| 128 | ** in pRecv. pRecv is assumed to be uninitialized when |
| 129 | ** this routine is called - this routine will initialize it. |
| @@ -131,10 +137,19 @@ | |
| 131 | ** The server address is contain in the "g" global structure. The |
| 132 | ** url_parse() routine should have been called prior to this routine |
| 133 | ** in order to fill this structure appropriately. |
| 134 | */ |
| 135 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin){ |
| 136 | Blob login; /* The login card */ |
| 137 | Blob payload; /* The complete payload including login card */ |
| 138 | Blob hdr; /* The HTTP request header */ |
| 139 | int closeConnection; /* True to close the connection when done */ |
| 140 | int iLength; /* Length of the reply payload */ |
| @@ -230,10 +245,13 @@ | |
| 230 | closeConnection = 1; |
| 231 | }else if( c=='k' || c=='K' ){ |
| 232 | closeConnection = 0; |
| 233 | } |
| 234 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 235 | int i, j; |
| 236 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 237 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 238 | j = strlen(zLine) - 1; |
| 239 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| @@ -241,11 +259,11 @@ | |
| 241 | zLine[j] = 0; |
| 242 | } |
| 243 | fossil_print("redirect to %s\n", &zLine[i]); |
| 244 | url_parse(&zLine[i]); |
| 245 | transport_close(); |
| 246 | return http_exchange(pSend, pReply, useLogin); |
| 247 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 248 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 249 | isCompressed = 0; |
| 250 | }else if( fossil_strnicmp(&zLine[14], |
| 251 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 252 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -120,10 +120,16 @@ | |
| 120 | blob_appendf(pHdr, "Content-Type: application/x-fossil\r\n"); |
| 121 | } |
| 122 | blob_appendf(pHdr, "Content-Length: %d\r\n\r\n", blob_size(pPayload)); |
| 123 | } |
| 124 | |
| 125 | /* |
| 126 | ** Maximum number of HTTP redirects that any http_exchange() call will |
| 127 | ** follow before throwing a fatal error. Most browsers use a limit of 20. |
| 128 | */ |
| 129 | #define MAX_REDIRECTS 20 |
| 130 | |
| 131 | /* |
| 132 | ** Sign the content in pSend, compress it, and send it to the server |
| 133 | ** via HTTP or HTTPS. Get a reply, uncompress the reply, and store the reply |
| 134 | ** in pRecv. pRecv is assumed to be uninitialized when |
| 135 | ** this routine is called - this routine will initialize it. |
| @@ -131,10 +137,19 @@ | |
| 137 | ** The server address is contain in the "g" global structure. The |
| 138 | ** url_parse() routine should have been called prior to this routine |
| 139 | ** in order to fill this structure appropriately. |
| 140 | */ |
| 141 | int http_exchange(Blob *pSend, Blob *pReply, int useLogin){ |
| 142 | return _http_exchange(pSend, pReply, useLogin, 0); |
| 143 | } |
| 144 | |
| 145 | /* |
| 146 | ** Actual implementation details of http_exchange(). This is done so we can |
| 147 | ** track the number of redirects without changing the signature of that |
| 148 | ** function and requiring callers to initialize numRedirects. |
| 149 | */ |
| 150 | int _http_exchange(Blob *pSend, Blob *pReply, int useLogin, int numRedirects){ |
| 151 | Blob login; /* The login card */ |
| 152 | Blob payload; /* The complete payload including login card */ |
| 153 | Blob hdr; /* The HTTP request header */ |
| 154 | int closeConnection; /* True to close the connection when done */ |
| 155 | int iLength; /* Length of the reply payload */ |
| @@ -230,10 +245,13 @@ | |
| 245 | closeConnection = 1; |
| 246 | }else if( c=='k' || c=='K' ){ |
| 247 | closeConnection = 0; |
| 248 | } |
| 249 | }else if( rc==302 && fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 250 | if ( numRedirects>=MAX_REDIRECTS ){ |
| 251 | fossil_fatal("redirect limit exceeded"); |
| 252 | } |
| 253 | int i, j; |
| 254 | for(i=9; zLine[i] && zLine[i]==' '; i++){} |
| 255 | if( zLine[i]==0 ) fossil_fatal("malformed redirect: %s", zLine); |
| 256 | j = strlen(zLine) - 1; |
| 257 | while( j>4 && fossil_strcmp(&zLine[j-4],"/xfer")==0 ){ |
| @@ -241,11 +259,11 @@ | |
| 259 | zLine[j] = 0; |
| 260 | } |
| 261 | fossil_print("redirect to %s\n", &zLine[i]); |
| 262 | url_parse(&zLine[i]); |
| 263 | transport_close(); |
| 264 | return _http_exchange(pSend, pReply, useLogin, numRedirects + 1); |
| 265 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 266 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 267 | isCompressed = 0; |
| 268 | }else if( fossil_strnicmp(&zLine[14], |
| 269 | "application/x-fossil-uncompressed", -1)==0 ){ |
| 270 |