upnphttp: improve robustness against malformed (possibly malicious) requests

This commit is contained in:
Catalin Patulea 2014-03-02 23:33:50 -05:00 committed by Justin Maggard
parent 01532b0490
commit 57c6510fe4

View File

@ -150,7 +150,7 @@ ParseHttpHeaders(struct upnphttp * h)
if(strncasecmp(line, "Content-Length", 14)==0) if(strncasecmp(line, "Content-Length", 14)==0)
{ {
p = colon; p = colon;
while(*p < '0' || *p > '9') while(*p && (*p < '0' || *p > '9'))
p++; p++;
h->req_contentlen = atoi(p); h->req_contentlen = atoi(p);
} }
@ -161,13 +161,13 @@ ParseHttpHeaders(struct upnphttp * h)
while(*p == ':' || *p == ' ' || *p == '\t') while(*p == ':' || *p == ' ' || *p == '\t')
p++; p++;
while(p[n] >= ' ') while(p[n] >= ' ')
{
n++; n++;
} if(n >= 2 &&
if((p[0] == '"' && p[n-1] == '"') ((p[0] == '"' && p[n-1] == '"') ||
|| (p[0] == '\'' && p[n-1] == '\'')) (p[0] == '\'' && p[n-1] == '\'')))
{ {
p++; n -= 2; p++;
n -= 2;
} }
h->req_soapAction = p; h->req_soapAction = p;
h->req_soapActionLen = n; h->req_soapActionLen = n;
@ -175,10 +175,10 @@ ParseHttpHeaders(struct upnphttp * h)
else if(strncasecmp(line, "Callback", 8)==0) else if(strncasecmp(line, "Callback", 8)==0)
{ {
p = colon; p = colon;
while(*p != '<' && *p != '\r' ) while(*p && *p != '<' && *p != '\r' )
p++; p++;
n = 0; n = 0;
while(p[n] != '>' && p[n] != '\r' ) while(p[n] && p[n] != '>' && p[n] != '\r' )
n++; n++;
h->req_Callback = p + 1; h->req_Callback = p + 1;
h->req_CallbackLen = MAX(0, n - 1); h->req_CallbackLen = MAX(0, n - 1);
@ -200,7 +200,7 @@ ParseHttpHeaders(struct upnphttp * h)
while(isspace(*p)) while(isspace(*p))
p++; p++;
n = 0; n = 0;
while(!isspace(p[n])) while(p[n] && !isspace(p[n]))
n++; n++;
h->req_SID = p; h->req_SID = p;
h->req_SIDLen = n; h->req_SIDLen = n;
@ -212,7 +212,7 @@ ParseHttpHeaders(struct upnphttp * h)
while(isspace(*p)) while(isspace(*p))
p++; p++;
n = 0; n = 0;
while(!isspace(p[n])) while(p[n] && !isspace(p[n]))
n++; n++;
h->req_NT = p; h->req_NT = p;
h->req_NTLen = n; h->req_NTLen = n;
@ -396,8 +396,11 @@ ParseHttpHeaders(struct upnphttp * h)
} }
} }
next_header: next_header:
while(!(line[0] == '\r' && line[1] == '\n')) line = strstr(line, "\r\n");
line++; if (!line)
{
return;
}
line += 2; line += 2;
} }
if( h->reqflags & FLAG_CHUNKED ) if( h->reqflags & FLAG_CHUNKED )
@ -410,9 +413,10 @@ next_header:
(h->req_chunklen = strtol(line, &endptr, 16)) && (h->req_chunklen = strtol(line, &endptr, 16)) &&
(endptr != line) ) (endptr != line) )
{ {
while(!(endptr[0] == '\r' && endptr[1] == '\n')) endptr = strstr(endptr, "\r\n");
if (!endptr)
{ {
endptr++; return;
} }
line = endptr+h->req_chunklen+2; line = endptr+h->req_chunklen+2;
} }
@ -541,18 +545,6 @@ Send501(struct upnphttp * h)
CloseSocket_upnphttp(h); CloseSocket_upnphttp(h);
} }
static const char *
findendheaders(const char * s, int len)
{
while(len-- > 0)
{
if(s[0]=='\r' && s[1]=='\n' && s[2]=='\r' && s[3]=='\n')
return s;
s++;
}
return NULL;
}
/* Sends the description generated by the parameter */ /* Sends the description generated by the parameter */
static void static void
sendXMLdesc(struct upnphttp * h, char * (f)(int *)) sendXMLdesc(struct upnphttp * h, char * (f)(int *))
@ -807,7 +799,7 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
p = h->req_buf; p = h->req_buf;
if(!p) if(!p)
return; return;
for(i = 0; i<15 && *p != ' ' && *p != '\r'; i++) for(i = 0; i<15 && *p && *p != ' ' && *p != '\r'; i++)
HttpCommand[i] = *(p++); HttpCommand[i] = *(p++);
HttpCommand[i] = '\0'; HttpCommand[i] = '\0';
while(*p==' ') while(*p==' ')
@ -818,13 +810,13 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
while(*p!='/') while(*p!='/')
p++; p++;
} }
for(i = 0; i<511 && *p != ' ' && *p != '\r'; i++) for(i = 0; i<511 && *p && *p != ' ' && *p != '\r'; i++)
HttpUrl[i] = *(p++); HttpUrl[i] = *(p++);
HttpUrl[i] = '\0'; HttpUrl[i] = '\0';
while(*p==' ') while(*p==' ')
p++; p++;
HttpVer = h->HttpVer; HttpVer = h->HttpVer;
for(i = 0; i<15 && *p != '\r'; i++) for(i = 0; i<15 && *p && *p != '\r'; i++)
HttpVer[i] = *(p++); HttpVer[i] = *(p++);
HttpVer[i] = '\0'; HttpVer[i] = '\0';
/*DPRINTF(E_INFO, L_HTTP, "HTTP REQUEST : %s %s (%s)\n", /*DPRINTF(E_INFO, L_HTTP, "HTTP REQUEST : %s %s (%s)\n",
@ -846,6 +838,11 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
/* see if we need to wait for remaining data */ /* see if we need to wait for remaining data */
if( (h->reqflags & FLAG_CHUNKED) ) if( (h->reqflags & FLAG_CHUNKED) )
{ {
if( h->req_chunklen == -1)
{
Send400(h);
return;
}
if( h->req_chunklen ) if( h->req_chunklen )
{ {
h->state = 2; h->state = 2;
@ -856,9 +853,11 @@ ProcessHttpQuery_upnphttp(struct upnphttp * h)
while( (h->req_chunklen = strtol(chunk, &endptr, 16)) && (endptr != chunk) ) while( (h->req_chunklen = strtol(chunk, &endptr, 16)) && (endptr != chunk) )
{ {
while(!(endptr[0] == '\r' && endptr[1] == '\n')) endptr = strstr(endptr, "\r\n");
if (!endptr)
{ {
endptr++; Send400(h);
return;
} }
endptr += 2; endptr += 2;
@ -1071,7 +1070,7 @@ Process_upnphttp(struct upnphttp * h)
h->req_buflen += n; h->req_buflen += n;
h->req_buf[h->req_buflen] = '\0'; h->req_buf[h->req_buflen] = '\0';
/* search for the string "\r\n\r\n" */ /* search for the string "\r\n\r\n" */
endheaders = findendheaders(h->req_buf, h->req_buflen); endheaders = strstr(h->req_buf, "\r\n\r\n");
if(endheaders) if(endheaders)
{ {
h->req_contentoff = endheaders - h->req_buf + 4; h->req_contentoff = endheaders - h->req_buf + 4;
@ -1143,56 +1142,43 @@ BuildHeader_upnphttp(struct upnphttp * h, int respcode,
time_t curtime = time(NULL); time_t curtime = time(NULL);
char date[30]; char date[30];
int templen; int templen;
struct string_s res;
if(!h->res_buf) if(!h->res_buf)
{ {
templen = sizeof(httpresphead) + 256 + bodylen; templen = sizeof(httpresphead) + 256 + bodylen;
h->res_buf = (char *)malloc(templen); h->res_buf = (char *)malloc(templen);
h->res_buf_alloclen = templen; h->res_buf_alloclen = templen;
} }
h->res_buflen = snprintf(h->res_buf, h->res_buf_alloclen, res.data = h->res_buf;
httpresphead, "HTTP/1.1", res.size = h->res_buf_alloclen;
res.off = 0;
strcatf(&res, httpresphead, "HTTP/1.1",
respcode, respmsg, respcode, respmsg,
(h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"", (h->respflags&FLAG_HTML)?"text/html":"text/xml; charset=\"utf-8\"",
bodylen); bodylen);
/* Additional headers */ /* Additional headers */
if(h->respflags & FLAG_TIMEOUT) { if(h->respflags & FLAG_TIMEOUT) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen, strcatf(&res, "Timeout: Second-");
h->res_buf_alloclen - h->res_buflen,
"Timeout: Second-");
if(h->req_Timeout) { if(h->req_Timeout) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen, strcatf(&res, "%d\r\n", h->req_Timeout);
h->res_buf_alloclen - h->res_buflen,
"%d\r\n", h->req_Timeout);
} else { } else {
h->res_buflen += snprintf(h->res_buf + h->res_buflen, strcatf(&res, "300\r\n");
h->res_buf_alloclen - h->res_buflen,
"300\r\n");
} }
} }
if(h->respflags & FLAG_SID) { if(h->respflags & FLAG_SID) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen, strcatf(&res, "SID: %.*s\r\n", h->req_SIDLen, h->req_SID);
h->res_buf_alloclen - h->res_buflen,
"SID: %.*s\r\n", h->req_SIDLen, h->req_SID);
} }
if(h->reqflags & FLAG_LANGUAGE) { if(h->reqflags & FLAG_LANGUAGE) {
h->res_buflen += snprintf(h->res_buf + h->res_buflen, strcatf(&res, "Content-Language: en\r\n");
h->res_buf_alloclen - h->res_buflen,
"Content-Language: en\r\n");
} }
strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime)); strftime(date, 30,"%a, %d %b %Y %H:%M:%S GMT" , gmtime(&curtime));
h->res_buflen += snprintf(h->res_buf + h->res_buflen, strcatf(&res, "Date: %s\r\n", date);
h->res_buf_alloclen - h->res_buflen, strcatf(&res, "EXT:\r\n");
"Date: %s\r\n", date);
h->res_buflen += snprintf(h->res_buf + h->res_buflen,
h->res_buf_alloclen - h->res_buflen,
"EXT:\r\n");
#if 0 // DLNA #if 0 // DLNA
h->res_buflen += snprintf(h->res_buf + h->res_buflen, strcatf(&res, "contentFeatures.dlna.org: \r\n");
h->res_buf_alloclen - h->res_buflen,
"contentFeatures.dlna.org: \r\n");
#endif #endif
h->res_buf[h->res_buflen++] = '\r'; strcatf(&res, "\r\n");
h->res_buf[h->res_buflen++] = '\n'; h->res_buflen = res.off;
if(h->res_buf_alloclen < (h->res_buflen + bodylen)) if(h->res_buf_alloclen < (h->res_buflen + bodylen))
{ {
h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen)); h->res_buf = (char *)realloc(h->res_buf, (h->res_buflen + bodylen));
@ -1524,6 +1510,7 @@ SendResp_thumbnail(struct upnphttp * h, char * object)
if( access(path, F_OK) != 0 ) if( access(path, F_OK) != 0 )
{ {
DPRINTF(E_ERROR, L_HTTP, "Error accessing %s\n", path); DPRINTF(E_ERROR, L_HTTP, "Error accessing %s\n", path);
Send404(h);
sqlite3_free(path); sqlite3_free(path);
return; return;
} }