From 6ec50a1fc262ec7c67ab20f0952524c0a8d4a766 Mon Sep 17 00:00:00 2001 From: Thibaut Mattern Date: Wed, 26 Nov 2003 08:09:58 +0000 Subject: http_helper: add a http url parser input_http: use http_helper try to fix potential security holes CVS patchset: 5783 CVS date: 2003/11/26 08:09:58 --- src/input/Makefile.am | 4 +- src/input/http_helper.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++++ src/input/http_helper.h | 38 +++++++ src/input/input_http.c | 216 +++++++++++----------------------------- 4 files changed, 353 insertions(+), 163 deletions(-) create mode 100644 src/input/http_helper.c create mode 100644 src/input/http_helper.h (limited to 'src') diff --git a/src/input/Makefile.am b/src/input/Makefile.am index 0645e9173..dfa6c631f 100644 --- a/src/input/Makefile.am +++ b/src/input/Makefile.am @@ -92,7 +92,7 @@ xineplug_inp_rtp_la_SOURCES = input_rtp.c xineplug_inp_rtp_la_LIBADD = $(XINE_LIB) xineplug_inp_rtp_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ -xineplug_inp_http_la_SOURCES = input_http.c net_buf_ctrl.c +xineplug_inp_http_la_SOURCES = input_http.c net_buf_ctrl.c http_helper.c xineplug_inp_http_la_LIBADD = $(XINE_LIB) xineplug_inp_http_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ @@ -125,4 +125,4 @@ xineplug_inp_pvr_la_LIBADD = $(XINE_LIB) xineplug_inp_pvr_la_LDFLAGS = -avoid-version -module @XINE_PLUGIN_MIN_SYMS@ include_HEADERS = input_plugin.h -noinst_HEADERS = net_buf_ctrl.h mms.h mmsh.h pnm.h media_helper.h videodev2.h +noinst_HEADERS = net_buf_ctrl.h mms.h mmsh.h pnm.h media_helper.h videodev2.h http_helper.h diff --git a/src/input/http_helper.c b/src/input/http_helper.c new file mode 100644 index 000000000..730deccad --- /dev/null +++ b/src/input/http_helper.c @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2000-2003 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * URL helper functions + * + * $Id: http_helper.c,v 1.1 2003/11/26 08:09:58 tmattern Exp $ + */ +#include "xine_internal.h" +#include "http_helper.h" + +static char *_strndup(const char *s, size_t n) { + char *ret; + + ret = malloc (n + 1); + strncpy(ret, s, n); + ret[n] = '\0'; + return ret; +} + +int _x_parse_url (char *url, char **proto, char** host, int *port, + char **user, char **password, char **uri) { + char *start = NULL; + char *authcolon = NULL; + char *at = NULL; + char *portcolon = NULL; + char *slash = NULL; + char *end = NULL; + char *strtol_err = NULL; + + if (!url) abort(); + if (!proto) abort(); + if (!user) abort(); + if (!password) abort(); + if (!host) abort(); + if (!port) abort(); + if (!uri) abort(); + + *proto = NULL; + *port = 0; + *user = NULL; + *host = NULL; + *password = NULL; + *uri = NULL; + + /* proto */ + start = strstr(url, "://"); + end = start + strlen(start) - 1; + if (!start || (start == url)) + goto error; + + *proto = _strndup(url, start - url); + + /* user:password */ + start += 3; + at = strchr(start, '@'); + slash = strchr(start, '/'); + + if (at && slash && (at > slash)) + at = NULL; + + if (at) { + authcolon = strchr(start, ':'); + if(authcolon && authcolon < at) { + *user = _strndup(start, authcolon - start); + *password = _strndup(authcolon + 1, at - authcolon - 1); + if ((authcolon == start) || (at == (authcolon + 1))) goto error; + } else { + /* no password */ + *user = _strndup(start, at - start); + if (at == start) goto error; + } + start = at + 1; + } + + /* host:port (ipv4) */ + /* [host]:port (ipv6) */ + if (*start != '[') + { + /* ipv4*/ + portcolon = strchr(start, ':'); + if (slash) { + if (portcolon && portcolon < slash) { + *host = _strndup(start, portcolon - start); + if (portcolon == start) goto error; + *port = strtol(portcolon + 1, &strtol_err, 10); + if ((strtol_err != slash) || (strtol_err == portcolon + 1)) + goto error; + } else { + *host = _strndup(start, slash - start); + if (slash == start) goto error; + } + } else { + if (portcolon) { + *host = _strndup(start, portcolon - start); + if (portcolon < end) { + *port = strtol(portcolon + 1, &strtol_err, 10); + if (*strtol_err != '\0') goto error; + } else { + goto error; + } + } else { + if (*start == '\0') goto error; + *host = strdup(start); + } + } + } else { + /* ipv6*/ + char *hostendbracket; + + hostendbracket = strchr(start, ']'); + if (hostendbracket != NULL) { + if (hostendbracket == start + 1) goto error; + *host = _strndup(start + 1, hostendbracket - start - 1); + + if (hostendbracket < end) { + /* Might have a trailing port */ + if (*(hostendbracket + 1) == ':') { + portcolon = hostendbracket + 1; + if (portcolon < end) { + *port = strtol(portcolon + 1, &strtol_err, 10); + if ((*strtol_err != '\0') && (*strtol_err != '/')) goto error; + } else { + goto error; + } + } + } + } else { + goto error; + } + } + + /* uri */ + start = slash; + if (start) + *uri = strdup(start); + else + *uri = strdup("/"); + + return 1; + +error: + if (*proto) { + free (*proto); + *proto = NULL; + } + if (*user) { + free (*user); + *user = NULL; + } + if (*password) { + free (*password); + *password = NULL; + } + if (*host) { + free (*host); + *host = NULL; + } + if (*port) { + *port = 0; + } + if (*uri) { + free (*uri); + *uri = NULL; + } + return 0; +} + + +#ifdef TEST_URL +/* + * url parser test program + */ + +static int check_url(char *url, int ok) { + char *proto, *host, *user, *password, *uri; + int port; + int res; + + printf("--------------------------------\n"); + printf("url=%s\n", url); + res = _x_parse_url (url, + &proto, &host, &port, &user, &password, &uri); + if (res) { + printf("proto=%s, host=%s, port=%d, user=%s, password=%s, uri=%s\n", + proto, host, port, user, password, uri); + free(proto); + free(host); + free(user); + free(password); + free(uri); + } else { + printf("bad url\n"); + } + if (res == ok) { + printf("test OK\n", url); + return 1; + } else { + printf("test KO\n", url); + return 0; + } +} + +int main(int argc, char** argv) { + char *proto, host, port, user, password, uri; + int res = 0; + + res += check_url("http://www.toto.com/test1.asx", 1); + res += check_url("http://www.toto.com:8080/test2.asx", 1); + res += check_url("http://titi:pass@www.toto.com:8080/test3.asx", 1); + res += check_url("http://www.toto.com", 1); + res += check_url("http://www.toto.com/", 1); + res += check_url("http://www.toto.com:80", 1); + res += check_url("http://www.toto.com:80/", 1); + res += check_url("http://www.toto.com:", 0); + res += check_url("http://www.toto.com:/", 0); + res += check_url("http://www.toto.com:abc", 0); + res += check_url("http://www.toto.com:abc/", 0); + res += check_url("http://titi@www.toto.com:8080/test4.asx", 1); + res += check_url("http://@www.toto.com:8080/test5.asx", 0); + res += check_url("http://:@www.toto.com:8080/test6.asx", 0); + res += check_url("http:///test6.asx", 0); + res += check_url("http://:/test7.asx", 0); + res += check_url("http://", 0); + res += check_url("http://:", 0); + res += check_url("http://@", 0); + res += check_url("http://:@", 0); + res += check_url("http://:/@", 0); + res += check_url("http://www.toto.com:80a/", 0); + res += check_url("http://[www.toto.com]", 1); + res += check_url("http://[www.toto.com]/", 1); + res += check_url("http://[www.toto.com]:80", 1); + res += check_url("http://[www.toto.com]:80/", 1); + res += check_url("http://[12:12]:80/", 1); + res += check_url("http://user:pass@[12:12]:80/", 1); + printf("================================\n"); + if (res != 28) { + printf("result: KO\n"); + } else { + printf("result: OK\n"); + } +} +#endif diff --git a/src/input/http_helper.h b/src/input/http_helper.h new file mode 100644 index 000000000..a89f93c3b --- /dev/null +++ b/src/input/http_helper.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2000-2003 the xine project + * + * This file is part of xine, a free video player. + * + * xine is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * xine is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * + * URL helper functions + * + * $Id: http_helper.h,v 1.1 2003/11/26 08:09:58 tmattern Exp $ + */ + +/* + * url parser + * {proto}://{user}:{password}@{host}:{port}{uri} + * {proto}://{user}:{password}@{[host]}:{port}{uri} + * + * return: + * 0 invalid url + * 1 valid url + */ +#ifndef HTTP_HELPER_H +#define HTTP_HELPER_H +int _x_parse_url (char *url, char **proto, char** host, int *port, + char **user, char **password, char **uri); +#endif /* HTTP_HELPER_H */ diff --git a/src/input/input_http.c b/src/input/input_http.c index 176cee289..e1a7bb9fa 100644 --- a/src/input/input_http.c +++ b/src/input/input_http.c @@ -19,7 +19,7 @@ * * input plugin for http network streams * - * $Id: input_http.c,v 1.74 2003/11/16 23:33:44 f1rmb Exp $ + * $Id: input_http.c,v 1.75 2003/11/26 08:09:58 tmattern Exp $ */ #ifdef HAVE_CONFIG_H @@ -46,7 +46,7 @@ #include "xineutils.h" #include "input_plugin.h" #include "net_buf_ctrl.h" -#include "io_helper.h" +#include "http_helper.h" /* #define LOG @@ -70,18 +70,17 @@ typedef struct { off_t contentlength; char buf[BUFSIZE]; - char mrlbuf[BUFSIZE]; - char mrlbuf2[BUFSIZE]; /* icecast */ char proxybuf[BUFSIZE]; char auth[BUFSIZE]; char proxyauth[BUFSIZE]; + char *proto; char *user; char *password; char *host; int port; - char *filename; + char *uri; char preview[MAX_PREVIEW_SIZE]; off_t preview_size; @@ -143,122 +142,6 @@ static void proxy_port_change_cb(void *data, xine_cfg_entry_t *cfg) { this->proxyport = cfg->num_value; } -static int http_plugin_parse_url (char *urlbuf, char **user, char **password, - char** host, int *port, char **filename) { - char *start = NULL; - char *authcolon = NULL; - char *at = NULL; - char *portcolon = NULL; - char *slash = NULL; - - if (user != NULL) - *user = NULL; - - if (password != NULL) - *password = NULL; - - if (host != NULL) - *host = NULL; - - if (filename != NULL) - *filename = NULL; - - if (port != NULL) - *port = 0; - - start = strstr(urlbuf, "://"); - if (start != NULL) - start += 3; - else - start = urlbuf; - - at = strchr(start, '@'); - slash = strchr(start, '/'); - - if (at != NULL && slash != NULL && at > slash) - at = NULL; - - if (at != NULL) - { - authcolon = strchr(start, ':'); - if(authcolon != NULL && authcolon > at) - authcolon = NULL; - - portcolon = strchr(at, ':'); - } else - portcolon = strchr(start, ':'); - - if (portcolon != NULL && slash != NULL && portcolon > slash) - portcolon = NULL; - - if (at != NULL) - { - *at = '\0'; - - if (user != NULL) - *user = start; - - if (authcolon != NULL) - { - *authcolon = '\0'; - - if (password != NULL) - *password = authcolon + 1; - } - - if (host != NULL) - *host = at + 1; - } else - if (host != NULL) - *host = start; - -#ifdef ENABLE_IPV6 - /* Add support for RFC 2732 */ - { - char *hostbracket, *hostendbracket; - - hostbracket = strchr(start, '['); - if (hostbracket != NULL) { - - hostendbracket = strchr(hostbracket, ']'); - - if (hostendbracket != NULL) { - - *hostendbracket = '\0'; - *host = (hostbracket + 1); - - /* Might have a trailing port */ - - if (*(hostendbracket+1) == ':') { - portcolon = (hostendbracket + 1); - } - } - } - } -#endif - - if (slash != 0) - { - *slash = '\0'; - - if (filename != NULL) - *filename = slash + 1; - } else { - if (filename != NULL) - *filename = urlbuf + strlen(urlbuf); - } - - if (portcolon != NULL) - { - *portcolon = '\0'; - - if (port != NULL) - *port = atoi(portcolon + 1); - } - - return 0; -} - static int http_plugin_basicauth (const char *user, const char *password, char* dest, int len) { static char *enctable="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; char *tmp; @@ -563,8 +446,8 @@ static uint32_t http_plugin_get_capabilities (input_plugin_t *this_gen) { uint32_t caps = INPUT_CAP_PREVIEW; /* Nullsoft asked to not allow saving streaming nsv files */ - if (this->filename && - !strncmp(this->filename + strlen(this->filename) - 4, ".nsv", 4)) + if (this->uri && + !strncmp(this->uri + strlen(this->uri) - 4, ".nsv", 4)) caps |= INPUT_CAP_RIP_FORBIDDEN; return caps; @@ -622,7 +505,7 @@ static off_t http_plugin_seek(input_plugin_t *this_gen, off_t offset, int origin static char* http_plugin_get_mrl (input_plugin_t *this_gen) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; - return this->mrlbuf2; + return this->mrl; } static int http_plugin_get_optional_data (input_plugin_t *this_gen, @@ -655,6 +538,12 @@ static void http_plugin_dispose (input_plugin_t *this_gen ) { this->nbc = NULL; } + if (this->mrl) free(this->mrl); + if (this->proto) free(this->proto); + if (this->host) free(this->host); + if (this->user) free(this->user); + if (this->password) free(this->password); + if (this->uri) free(this->uri); free (this); } @@ -676,10 +565,10 @@ static void report_progress (xine_stream_t *stream, int p) { static int http_plugin_open (input_plugin_t *this_gen ) { http_input_plugin_t *this = (http_input_plugin_t *) this_gen; http_input_class_t *this_class = (http_input_class_t *) this->input_plugin.input_class; - int done,len,linenum; + int done, len, linenum; int shoutcast = 0, httpcode; - int length; int res, progress; + int buflen; this->shoutcast_pos = 0; @@ -698,10 +587,11 @@ static int http_plugin_open (input_plugin_t *this_gen ) { } - if (http_plugin_parse_url (this->mrlbuf, &this->user, &this->password, - &this->host, &this->port, &this->filename)) + if (!_x_parse_url(this->mrl, &this->proto, &this->host, &this->port, + &this->user, &this->password, &this->uri)) { + _x_message(this->stream, XINE_MSG_GENERAL_WARNING, "malformed url", NULL); return 0; - + } if (this->port == 0) this->port = DEFAULT_HTTP_PORT; @@ -745,40 +635,46 @@ static int http_plugin_open (input_plugin_t *this_gen ) { if (this_class->proxyhost && strlen(this_class->proxyhost)) { if (this->port != DEFAULT_HTTP_PORT) { - sprintf (this->buf, "GET http://%s:%d/%s HTTP/1.0\015\012", - this->host, this->port, this->filename); + snprintf (this->buf, BUFSIZE, "GET http://%s:%d%s HTTP/1.0\015\012", + this->host, this->port, this->uri); } else { - sprintf (this->buf, "GET http://%s/%s HTTP/1.0\015\012", - this->host, this->filename); + snprintf (this->buf, BUFSIZE, "GET http://%s%s HTTP/1.0\015\012", + this->host, this->uri); } } else - sprintf (this->buf, "GET /%s HTTP/1.0\015\012", this->filename); + snprintf (this->buf, BUFSIZE, "GET %s HTTP/1.0\015\012", this->uri); + buflen = strlen(this->buf); if (this->port != DEFAULT_HTTP_PORT) - sprintf (this->buf + strlen(this->buf), "Host: %s:%d\015\012", + snprintf (this->buf + buflen, BUFSIZE - buflen, "Host: %s:%d\015\012", this->host, this->port); else - sprintf (this->buf + strlen(this->buf), "Host: %s\015\012", + snprintf (this->buf + buflen, BUFSIZE - buflen, "Host: %s\015\012", this->host); - if (this_class->proxyuser && strlen(this_class->proxyuser)) - sprintf (this->buf + strlen(this->buf), "Proxy-Authorization: Basic %s\015\012", - this->proxyauth); - - if (this->user && strlen(this->user)) - sprintf (this->buf + strlen(this->buf), "Authorization: Basic %s\015\012", - this->auth); - - sprintf (this->buf + strlen(this->buf), "User-Agent: xine/%s\015\012", - VERSION); - strcat (this->buf, "Accept: */*\015\012"); /* * */ - strcat (this->buf, "Icy-MetaData: 1\015\012"); + buflen = strlen(this->buf); + if (this_class->proxyuser && strlen(this_class->proxyuser)) { + snprintf (this->buf + buflen, BUFSIZE - buflen, + "Proxy-Authorization: Basic %s\015\012", this->proxyauth); + buflen = strlen(this->buf); + } + if (this->user && strlen(this->user)) { + snprintf (this->buf + buflen, BUFSIZE - buflen, + "Authorization: Basic %s\015\012", this->auth); + buflen = strlen(this->buf); + } - strcat (this->buf, "\015\012"); - - length = strlen(this->buf); - if (_x_io_tcp_write (this->stream, this->fh, this->buf, length) != length) { + snprintf (this->buf + buflen, BUFSIZE - buflen, + "User-Agent: xine/%s\015\012", VERSION); + buflen = strlen(this->buf); + strncat (this->buf, "Accept: */*\015\012", BUFSIZE - buflen); /* * */ + buflen = strlen(this->buf); + strncat (this->buf, "Icy-MetaData: 1\015\012", BUFSIZE - buflen); + buflen = strlen(this->buf); + strncat (this->buf, "\015\012", BUFSIZE - buflen); + buflen = strlen(this->buf); + if (_x_io_tcp_write (this->stream, this->fh, this->buf, buflen) != buflen) { _x_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "couldn't send request", NULL); xprintf(this_class->xine, XINE_VERBOSITY_DEBUG, "input_http: couldn't send request\n"); return 0; @@ -861,8 +757,8 @@ static int http_plugin_open (input_plugin_t *this_gen ) { lprintf ("input_http: trying to open target of redirection: >%s<\n", href); - strncpy (this->mrlbuf, href, BUFSIZE); - strncpy (this->mrlbuf2, href, BUFSIZE); + free(this->mrl); + this->mrl = strdup(href); return http_plugin_open(this_gen); } } @@ -891,10 +787,10 @@ static int http_plugin_open (input_plugin_t *this_gen ) { this->shoutcast_songtitle = NULL; if (shoutcast || !strncasecmp(this->preview, "ICY", 3)) { - this->mrlbuf2[0] = 'i'; - this->mrlbuf2[1] = 'c'; - this->mrlbuf2[2] = 'e'; - this->mrlbuf2[3] = ' '; + this->mrl[0] = 'i'; + this->mrl[1] = 'c'; + this->mrl[2] = 'e'; + this->mrl[3] = ' '; if (read_shoutcast_header(this)) { /* problem when reading shoutcast header */ _x_message(this->stream, XINE_MSG_CONNECTION_REFUSED, "can't read shoutcast header", NULL); @@ -923,9 +819,7 @@ static input_plugin_t *http_class_get_instance (input_class_t *cls_gen, xine_str } this = (http_input_plugin_t *) xine_xmalloc(sizeof(http_input_plugin_t)); - strncpy (this->mrlbuf, mrl, BUFSIZE); - strncpy (this->mrlbuf2, mrl, BUFSIZE); - this->mrl = this->mrlbuf2; + this->mrl = strdup(mrl); this->stream = stream; this->fh = -1; this->nbc = nbc_init (this->stream); -- cgit v1.2.3