Patch for CVE-2009-1759. Source: Upstream SVN, rev 302 from the drorrent-3 branch. Index: bencode.h =================================================================== --- a/bencode.h (revision 300) +++ b/bencode.h (revision 302) @@ -25,7 +25,7 @@ size_t decode_list(const char *b,size_t len,const char *keylist); size_t decode_rev(const char *b,size_t len,const char *keylist); size_t decode_query(const char *b,size_t len,const char *keylist,const char **ps,size_t *pi,int64_t *pl,int method); -size_t decode_list2path(const char *b, size_t n, char *pathname); +size_t decode_list2path(const char *b, size_t n, char *pathname, size_t maxlen); size_t bencode_buf(const char *str,size_t len,FILE *fp); size_t bencode_str(const char *str, FILE *fp); size_t bencode_int(const uint64_t integer, FILE *fp); Index: bencode.cpp =================================================================== --- a/bencode.cpp (revision 300) +++ b/bencode.cpp (revision 302) @@ -233,22 +233,28 @@ return bencode_end_dict_list(fp); } -size_t decode_list2path(const char *b, size_t n, char *pathname) +size_t decode_list2path(const char *b, size_t n, char *pathname, size_t maxlen) { const char *pb = b; const char *s = (char *) 0; + const char *endmax = pathname + maxlen - 1; size_t r,q; if( 'l' != *pb ) return 0; pb++; n--; if( !n ) return 0; - for(; n;){ + while( n && pathname < endmax ){ if(!(r = buf_str(pb, n, &s, &q)) ) return 0; + if( q >= maxlen ) return 0; memcpy(pathname, s, q); pathname += q; - pb += r; n -= r; - if( 'e' != *pb ){*pathname = PATH_SP, pathname++;} else break; + maxlen -= q; + pb += r; + n -= r; + if( 'e' == *pb ) break; + if( pathname >= endmax ) return 0; + *pathname++ = PATH_SP; } *pathname = '\0'; return (pb - b + 1); Index: btfiles.cpp =================================================================== --- a/btfiles.cpp (revision 300) +++ b/btfiles.cpp (revision 302) @@ -449,7 +449,8 @@ return 0; } -int btFiles::BuildFromMI(const char *metabuf, const size_t metabuf_len, const char *saveas) +int btFiles::BuildFromMI(const char *metabuf, const size_t metabuf_len, + const char *saveas, unsigned char exam_only) { char path[MAXPATHLEN]; const char *s, *p; @@ -458,11 +459,19 @@ int f_warned = 0; if( !decode_query(metabuf, metabuf_len, "info|name", &s, &q, (int64_t*)0, - QUERY_STR) || MAXPATHLEN <= q ) + QUERY_STR) || MAXPATHLEN <= q ){ + errno = EINVAL; return -1; + } memcpy(path, s, q); path[q] = '\0'; + if( !exam_only && + (PATH_SP == path[0] || '/' == path[0] || 0==strncmp("..", path, 2)) ){ + CONSOLE.Warning(1, "error, unsafe path \"%s\" in torrent data", path); + errno = EINVAL; + return -1; + } r = decode_query(metabuf, metabuf_len, "info|files", (const char**)0, &q, (int64_t*)0, QUERY_POS); @@ -471,21 +480,31 @@ BTFILE *pbf_last = (BTFILE*) 0; BTFILE *pbf = (BTFILE*) 0; size_t dl; + unsigned long nfiles = 0; + if( decode_query(metabuf,metabuf_len,"info|length", - (const char**) 0,(size_t*) 0,(int64_t*) 0,QUERY_LONG) ) + (const char**) 0,(size_t*) 0,(int64_t*) 0,QUERY_LONG) ){ + errno = EINVAL; return -1; + } if( saveas ){ m_directory = new char[strlen(saveas) + 1]; #ifndef WINDOWS - if(!m_directory) return -1; + if( !m_directory ){ + errno = ENOMEM; + return -1; + } #endif strcpy(m_directory,saveas); }else{ int f_conv; char *tmpfn = new char[strlen(path)*2+5]; #ifndef WINDOWS - if( !tmpfn ) return -1; + if( !tmpfn ){ + errno = ENOMEM; + return -1; + } #endif if( f_conv = ConvertFilename(tmpfn, path, strlen(path)*2+5) ){ if( arg_flg_convert_filenames ){ @@ -493,6 +512,7 @@ #ifndef WINDOWS if( !m_directory ){ delete []tmpfn; + errno = ENOMEM; return -1; } #endif @@ -507,7 +527,10 @@ if( !f_conv || !arg_flg_convert_filenames ){ m_directory = new char[strlen(path) + 1]; #ifndef WINDOWS - if( !m_directory ) return -1; + if( !m_directory ){ + errno = ENOMEM; + return -1; + } #endif strcpy(m_directory,path); } @@ -517,24 +540,50 @@ p = metabuf + r + 1; q--; for(; q && 'e' != *p; p += dl, q -= dl){ - if(!(dl = decode_dict(p, q, (const char*) 0)) ) return -1; - if( !decode_query(p, dl, "length", (const char**) 0, - (size_t*) 0,&t,QUERY_LONG) ) return -1; + if( !(dl = decode_dict(p, q, (const char*) 0)) || + !decode_query(p, dl, "length", (const char**) 0, (size_t*) 0, &t, + QUERY_LONG) ){ + errno = EINVAL; + return -1; + } pbf = _new_bfnode(); #ifndef WINDOWS - if( !pbf ) return -1; + if( !pbf ){ + errno = ENOMEM; + return -1; + } #endif + nfiles++; pbf->bf_length = t; m_total_files_length += t; r = decode_query(p, dl, "path", (const char **)0, &n, (int64_t*)0, QUERY_POS); - if( !r ) return -1; - if(!decode_list2path(p + r, n, path)) return -1; + if( !r || !decode_list2path(p + r, n, path, sizeof(path)) ){ + CONSOLE.Warning(1, + "error, invalid path in torrent data for file %lu at offset %llu", + nfiles, m_total_files_length - t); + delete pbf; + errno = EINVAL; + return -1; + } + if( !exam_only && + (PATH_SP == path[0] || '/' == path[0] || 0==strncmp("..", path, 2)) ){ + CONSOLE.Warning(1, + "error, unsafe path \"%s\" in torrent data for file %lu", + path, nfiles); + delete pbf; + errno = EINVAL; + return -1; + } + int f_conv; char *tmpfn = new char[strlen(path)*2+5]; #ifndef WINDOWS - if( !tmpfn ) return -1; + if( !tmpfn ){ + errno = ENOMEM; + return -1; + } #endif if( f_conv = ConvertFilename(tmpfn, path, strlen(path)*2+5) ){ if( arg_flg_convert_filenames ){ @@ -542,6 +591,7 @@ #ifndef WINDOWS if( !pbf->bf_filename ){ delete []tmpfn; + errno = ENOMEM; return -1; } #endif @@ -556,7 +606,10 @@ if( !f_conv || !arg_flg_convert_filenames ){ pbf->bf_filename = new char[strlen(path) + 1]; #ifndef WINDOWS - if( !pbf->bf_filename ) return -1; + if( !pbf->bf_filename ){ + errno = ENOMEM; + return -1; + } #endif strcpy(pbf->bf_filename, path); } @@ -564,30 +617,42 @@ pbf_last = pbf; } }else{ - if( !decode_query(metabuf,metabuf_len,"info|length", - (const char**) 0,(size_t*) 0,&t,QUERY_LONG) ) + if( !decode_query(metabuf,metabuf_len, "info|length", + (const char**)0, (size_t*) 0, &t, QUERY_LONG) ){ + errno = EINVAL; return -1; + } m_btfhead = _new_bfnode(); #ifndef WINDOWS - if( !m_btfhead) return -1; + if( !m_btfhead ){ + errno = ENOMEM; + return -1; + } #endif m_btfhead->bf_length = m_total_files_length = t; if( saveas ){ m_btfhead->bf_filename = new char[strlen(saveas) + 1]; #ifndef WINDOWS - if(!m_btfhead->bf_filename ) return -1; + if( !m_btfhead->bf_filename ){ + errno = ENOMEM; + return -1; + } #endif strcpy(m_btfhead->bf_filename, saveas); }else if( arg_flg_convert_filenames ){ char *tmpfn = new char[strlen(path)*2+5]; #ifndef WINDOWS - if( !tmpfn ) return -1; + if( !tmpfn ){ + errno = ENOMEM; + return -1; + } #endif ConvertFilename(tmpfn, path, strlen(path)*2+5); m_btfhead->bf_filename = new char[strlen(tmpfn) + 1]; #ifndef WINDOWS if( !m_btfhead->bf_filename ){ delete []tmpfn; + errno = ENOMEM; return -1; } #endif @@ -596,7 +661,10 @@ }else{ m_btfhead->bf_filename = new char[strlen(path) + 1]; #ifndef WINDOWS - if(!m_btfhead->bf_filename ) return -1; + if( !m_btfhead->bf_filename ){ + errno = ENOMEM; + return -1; + } #endif strcpy(m_btfhead->bf_filename, path); } @@ -694,6 +762,32 @@ size_t btFiles::FillMetaInfo(FILE* fp) { BTFILE *p; + const char *refname, *s; + char path[MAXPATHLEN]; + + refname = m_directory ? m_directory : m_btfhead->bf_filename; + while( (s = strchr(refname, PATH_SP)) && *(s + 1) ){ + refname = s + 1; + } + if( m_directory && '.' == *refname ){ + char dir[MAXPATHLEN]; + if( getcwd(dir, sizeof(dir)) && 0==chdir(m_directory) ){ + if( getcwd(path, sizeof(path)) ){ + refname = path; + while( (s = strchr(refname, PATH_SP)) && *(s + 1) ){ + refname = s + 1; + } + } + chdir(dir); + } + } + if( '/' == *refname || '\0' == *refname || '.' == *refname ){ + CONSOLE.Warning(1, "error, inappropriate file or directory name \"%s\"", + m_directory ? m_directory : m_btfhead->bf_filename); + errno = EINVAL; + return 0; + } + if( m_directory ){ // multi files if( bencode_str("files", fp) != 1 ) return 0; @@ -715,16 +809,15 @@ if(bencode_end_dict_list(fp) != 1 ) return 0; if(bencode_str("name", fp) != 1) return 0; - return bencode_str(m_directory, fp); - + return bencode_str(refname, fp); }else{ if( bencode_str("length", fp) != 1 ) return 0; if( bencode_int(m_btfhead->bf_length, fp) != 1) return 0; if( bencode_str("name", fp) != 1 ) return 0; - return bencode_str(m_btfhead->bf_filename, fp); + return bencode_str(refname, fp); } - return 1; + return 0; } Index: btcontent.cpp =================================================================== --- a/btcontent.cpp (revision 300) +++ b/btcontent.cpp (revision 302) @@ -357,7 +357,11 @@ cfg_req_queue_length = (m_piece_length / cfg_req_slice_size) * 2 - 1; - if( m_btfiles.BuildFromMI(b, flen, saveas) < 0 ) ERR_RETURN(); + if( m_btfiles.BuildFromMI(b, flen, saveas, arg_flg_exam_only) < 0 ){ + if( EINVAL == errno ) + CONSOLE.Warning(1, "Torrent metainfo file data is invalid or unusable."); + ERR_RETURN(); + } delete []b; b = (char *)0; Index: btfiles.h =================================================================== --- a/btfiles.h (revision 300) +++ b/btfiles.h (revision 302) @@ -61,7 +61,7 @@ int BuildFromFS(const char *pathname); int BuildFromMI(const char *metabuf, const size_t metabuf_len, - const char *saveas); + const char *saveas, unsigned char exam_only); char *GetDataName() const; uint64_t GetTotalLength() const { return m_total_files_length; }