Control: tags -1 + patch Control: found -1 5.1.4+dfsg-4 Hi Dominic,
On Tue, Dec 30, 2014 at 02:56:31AM +0100, Moritz Muehlenhoff wrote: > Source: movabletype-opensource > Severity: grave > Tags: security > > Hi, > please see https://movabletype.org/news/2014/12/6.0.6.html Attaches is the extracted patch for the 5.2.x series. Regards, Salvatore
--- a/lib/MT/XMLRPCServer.pm 2013-11-07 04:55:39.000000000 +0100 +++ b/lib/MT/XMLRPCServer.pm 2014-12-24 11:13:10.000000000 +0100 @@ -78,6 +78,18 @@ $HAVE_XML_PARSER = $@ ? 0 : 1; } +sub _validate_params { + my ($params) = @_; + + foreach my $p (@$params) { + die _fault( MT->translate("Invalid parameter") ) + if ( 'ARRAY' eq ref $p ) + or ( 'HASH' eq ref $p ); + } + + return 1; +} + sub _fault { my $mt = MT::XMLRPCServer::Util::mt_new(); my $enc = $mt->config('PublishCharset'); @@ -126,6 +138,7 @@ sub _login { my $class = shift; my ( $user, $pass, $blog_id ) = @_; + my $mt = MT::XMLRPCServer::Util::mt_new(); my $enc = $mt->config('PublishCharset'); require MT::Author; @@ -274,11 +287,10 @@ my $cat_class = MT->model('category'); # The spec says to ignore invalid category names. - @categories = grep {defined} $cat_class->search( - { blog_id => $entry->blog_id, - label => $cats, - } - ); + @categories + = grep {defined} + $cat_class->search( + { blog_id => $entry->blog_id, label => $cats, } ); } } @@ -288,10 +300,7 @@ my $place; if ($is_primary_placement) { $place = MT::Placement->load( - { entry_id => $entry->id, - is_primary => 1, - } - ); + { entry_id => $entry->id, is_primary => 1, } ); } if ( !$place ) { $place = MT::Placement->new; @@ -310,10 +319,7 @@ # Delete all the secondary placements, so each of the remaining # iterations of the loop make a brand new placement. my @old_places = MT::Placement->load( - { entry_id => $entry->id, - is_primary => 0, - } - ); + { entry_id => $entry->id, is_primary => 0, } ); for my $place (@old_places) { $place->remove; } @@ -391,8 +397,7 @@ ); $entry->allow_comments( $item->{mt_allow_comments} ) if exists $item->{mt_allow_comments}; - $entry->title( $item->{title} ) - if exists $item->{title}; + $entry->title( $item->{title} ) if exists $item->{title}; $class->_apply_basename( $entry, $item, \%param ); @@ -488,6 +493,21 @@ else { ( $blog_id, $user, $pass, $item, $publish ) = @_; } + + _validate_params( [ $blog_id, $user, $pass, $publish ] ) or return; + my $values; + foreach my $k ( keys %$item ) { + if ( 'categories' eq $k || 'mt_tb_ping_urls' eq $k ) { + + # XMLRPC supports categories array and mt_tb_ping_urls array + _validate_params( \@{ $item->{$k} } ) or return; + } + else { + push @$values, $item->{$k}; + } + } + _validate_params( \@$values ) or return; + $class->_new_entry( blog_id => $blog_id, user => $user, @@ -500,6 +520,21 @@ sub newPage { my $class = shift; my ( $blog_id, $user, $pass, $item, $publish ) = @_; + + _validate_params( [ $blog_id, $user, $pass, $publish ] ) or return; + my $values; + foreach my $k ( keys %$item ) { + if ( 'mt_tb_ping_urls' eq $k ) { + + # XMLRPC supports mt_tb_ping_urls array + _validate_params( \@{ $item->{$k} } ) or return; + } + else { + push @$values, $item->{$k}; + } + } + _validate_params( \@$values ) or return; + $class->_new_entry( blog_id => $blog_id, user => $user, @@ -648,6 +683,21 @@ else { ( $entry_id, $user, $pass, $item, $publish ) = @_; } + + _validate_params( [ $entry_id, $user, $pass, $publish ] ) or return; + my $values; + foreach my $k ( keys %$item ) { + if ( 'categories' eq $k || 'mt_tb_ping_urls' eq $k ) { + + # XMLRPC supports categories array and mt_tb_ping_urls array + _validate_params( \@{ $item->{$k} } ) or return; + } + else { + push @$values, $item->{$k}; + } + } + _validate_params( \@$values ) or return; + $class->_edit_entry( entry_id => $entry_id, user => $user, @@ -660,6 +710,22 @@ sub editPage { my $class = shift; my ( $blog_id, $entry_id, $user, $pass, $item, $publish ) = @_; + + _validate_params( [ $blog_id, $entry_id, $user, $pass, $publish ] ) + or return; + my $values; + foreach my $k ( keys %$item ) { + if ( 'mt_tb_ping_urls' eq $k ) { + + # XMLRPC supports mt_tb_ping_urls array + _validate_params( \@{ $item->{$k} } ) or return; + } + else { + push @$values, $item->{$k}; + } + } + _validate_params( \@$values ) or return; + $class->_edit_entry( blog_id => $blog_id, entry_id => $entry_id, @@ -680,6 +746,9 @@ $class = __PACKAGE__; } my ( $appkey, $user, $pass ) = @_; + + _validate_params( [ $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. my ($author) = $class->_login( $user, $pass ); die _fault( MT->translate("Invalid login") ) unless $author; @@ -728,6 +797,9 @@ $class = __PACKAGE__; } my ( $appkey, $user, $pass ) = @_; + + _validate_params( [ $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. my ($author) = $class->_login( $user, $pass ); die _fault( MT->translate("Invalid login") ) unless $author; @@ -753,8 +825,7 @@ die _fault( MT->translate("Invalid login") ) unless $author; die _fault( MT->translate("Permission denied.") ) if !$author->is_superuser - && ( !$perms - || !$perms->can_do('get_entries_via_xmlrpc_server') ); + && ( !$perms || !$perms->can_do('get_entries_via_xmlrpc_server') ); my $iter = MT->model($obj_type)->load_iter( { blog_id => $blog_id }, { 'sort' => 'authored_on', @@ -820,6 +891,9 @@ else { ( $blog_id, $user, $pass, $num ) = @_; } + + _validate_params( [ $blog_id, $user, $pass, $num ] ) or return; + $class->_get_entries( blog_id => $blog_id, user => $user, @@ -831,6 +905,9 @@ sub getRecentPostTitles { my $class = shift; my ( $blog_id, $user, $pass, $num ) = @_; + + _validate_params( [ $blog_id, $user, $pass, $num ] ) or return; + $class->_get_entries( blog_id => $blog_id, user => $user, @@ -843,6 +920,9 @@ sub getPages { my $class = shift; my ( $blog_id, $user, $pass ) = @_; + + _validate_params( [ $blog_id, $user, $pass ] ) or return; + $class->_get_entries( blog_id => $blog_id, user => $user, @@ -879,16 +959,13 @@ # Rebuild archives if (%recipe) { - $mt->rebuild_archives( - Blog => $blog, - Recipe => \%recipe, - ) or die _fault( $class->errstr ); + $mt->rebuild_archives( Blog => $blog, Recipe => \%recipe, ) + or die _fault( $class->errstr ); } # Rebuild index files if ( $mt->config('RebuildAtDelete') ) { - $mt->rebuild_indexes( Blog => $blog ) - or die _fault( $class->errstr ); + $mt->rebuild_indexes( Blog => $blog ) or die _fault( $class->errstr ); } $mt->log( @@ -915,6 +992,9 @@ $class = __PACKAGE__; } my ( $appkey, $entry_id, $user, $pass, $publish ) = @_; + + _validate_params( [ $entry_id, $user, $pass, $publish ] ) or return; + $class->_delete_entry( entry_id => $entry_id, user => $user, @@ -926,6 +1006,9 @@ sub deletePage { my $class = shift; my ( $blog_id, $user, $pass, $entry_id ) = @_; + + _validate_params( [ $blog_id, $user, $pass, $entry_id ] ) or return; + $class->_delete_entry( blog_id => $blog_id, entry_id => $entry_id, @@ -954,8 +1037,8 @@ die _fault( MT->translate("Not allowed to get entry") ) if !$author->is_superuser && ( !$perms || !$perms->can_edit_entry( $entry, $author ) ); - my $co = sprintf "%04d%02d%02dT%02d:%02d:%02d", - unpack 'A4A2A2A2A2A2', $entry->authored_on; + my $co = sprintf "%04d%02d%02dT%02d:%02d:%02d", unpack 'A4A2A2A2A2A2', + $entry->authored_on; my $link = $entry->permalink; require MT::Tag; my $delim = chr( $author->entry_prefs->{tag_delim} ); @@ -999,12 +1082,18 @@ sub getPost { my $class = shift; my ( $entry_id, $user, $pass ) = @_; + + _validate_params( [ $entry_id, $user, $pass ] ) or return; + $class->_get_entry( entry_id => $entry_id, user => $user, pass => $pass ); } sub getPage { my $class = shift; my ( $blog_id, $entry_id, $user, $pass ) = @_; + + _validate_params( [ $blog_id, $entry_id, $user, $pass ] ) or return; + $class->_get_entry( blog_id => $blog_id, entry_id => $entry_id, @@ -1020,9 +1109,8 @@ 'metaWeblog.getPost', 'metaWeblog.newPost', 'metaWeblog.editPost', 'metaWeblog.getRecentPosts', 'metaWeblog.newMediaObject', 'metaWeblog.getCategories', 'metaWeblog.deletePost', - 'metaWeblog.getUsersBlogs', - 'wp.newPage', 'wp.getPages', 'wp.getPage', 'wp.editPage', - 'wp.deletePage', + 'metaWeblog.getUsersBlogs', 'wp.newPage', 'wp.getPages', 'wp.getPage', + 'wp.editPage', 'wp.deletePage', # not yet supported: metaWeblog.getTemplate, metaWeblog.setTemplate 'mt.getCategoryList', 'mt.setPostCategories', 'mt.getPostCategories', @@ -1056,6 +1144,9 @@ sub getCategoryList { my $class = shift; my ( $blog_id, $user, $pass ) = @_; + + _validate_params( [ $blog_id, $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. my ( $author, $perms ) = $class->_login( $user, $pass, $blog_id ); die _fault( MT->translate("Invalid login") ) unless $author; @@ -1080,13 +1171,15 @@ sub getCategories { my $class = shift; my ( $blog_id, $user, $pass ) = @_; + + _validate_params( [ $blog_id, $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. my ( $author, $perms ) = $class->_login( $user, $pass, $blog_id ); die _fault( MT->translate("Invalid login") ) unless $author; die _fault( MT->translate("Permission denied.") ) if !$author->is_superuser - && ( !$perms - || !$perms->can_do('get_categories_via_xmlrpc_server') ); + && ( !$perms || !$perms->can_do('get_categories_via_xmlrpc_server') ); require MT::Category; my $iter = MT::Category->load_iter( { blog_id => $blog_id } ); my @data; @@ -1117,13 +1210,15 @@ sub getTagList { my $class = shift; my ( $blog_id, $user, $pass ) = @_; + + _validate_params( [ $blog_id, $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. my ( $author, $perms ) = $class->_login( $user, $pass, $blog_id ); die _fault( MT->translate("Invalid login") ) unless $author; die _fault( MT->translate("Permission denied.") ) if !$author->is_superuser - && ( !$perms - || !$perms->can_do('get_tag_list_via_xmlrpc_server') ); + && ( !$perms || !$perms->can_do('get_tag_list_via_xmlrpc_server') ); require MT::Tag; require MT::ObjectTag; my $iter = MT::Tag->load_iter( @@ -1149,6 +1244,9 @@ sub getPostCategories { my $class = shift; my ( $entry_id, $user, $pass ) = @_; + + _validate_params( [ $entry_id, $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. require MT::Entry; my $entry = MT::Entry->load($entry_id) @@ -1179,6 +1277,12 @@ sub setPostCategories { my $class = shift; my ( $entry_id, $user, $pass, $cats ) = @_; + + _validate_params( [ $entry_id, $user, $pass ] ) or return; + foreach my $c (@$cats) { + _validate_params( [ values %$c ] ) or return; + } + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. require MT::Entry; require MT::Placement; @@ -1227,11 +1331,13 @@ sub getTrackbackPings { my $class = shift; my ($entry_id) = @_; + + _validate_params( [$entry_id] ) or return; + require MT::Trackback; require MT::TBPing; my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. - my $tb = MT::Trackback->load( { entry_id => $entry_id } ) - or return []; + my $tb = MT::Trackback->load( { entry_id => $entry_id } ) or return []; my $iter = MT::TBPing->load_iter( { tb_id => $tb->id } ); my @data; @@ -1249,6 +1355,9 @@ sub publishPost { my $class = shift; my ( $entry_id, $user, $pass ) = @_; + + _validate_params( [ $entry_id, $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. require MT::Entry; my $entry = MT::Entry->load($entry_id) @@ -1269,6 +1378,8 @@ my $class = shift; my ( $user, $pass ) = @_; + _validate_params( [ $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); my $author = $class->_login( $user, $pass ); die _fault( MT->translate("Invalid login") ) unless $author; @@ -1282,6 +1393,8 @@ my $class = shift; my ( $blog_id, $user, $pass ) = @_; + _validate_params( [ $blog_id, $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); my $author = $class->_login( $user, $pass ); die _fault( MT->translate("Invalid login") ) unless $author; @@ -1318,8 +1431,7 @@ if ( $entry && $entry->authored_on <= $now ) { $entry->status( MT::Entry::RELEASE() ); $entry->discover_tb_from_entry(); - $entry->save - or die $entry->errstr; + $entry->save or die $entry->errstr; $types{ $entry->class } = 1; start_background_task( @@ -1336,18 +1448,17 @@ $blog->save if $changed && ( keys %types ); if ($changed) { - $mt->rebuild_indexes( Blog => $blog ) - or die $mt->errstr; + $mt->rebuild_indexes( Blog => $blog ) or die $mt->errstr; } - { responseCode => 'success', - publishedCount => $total_changed, - }; + { responseCode => 'success', publishedCount => $total_changed, }; } sub getNextScheduled { my $class = shift; my ( $user, $pass ) = @_; + _validate_params( [ $user, $pass ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); my $author = $class->_login( $user, $pass ); die _fault( MT->translate("Invalid login") ) unless $author; @@ -1360,6 +1471,11 @@ sub setRemoteAuthToken { my $class = shift; my ( $user, $pass, $remote_auth_username, $remote_auth_token ) = @_; + + _validate_params( + [ $user, $pass, $remote_auth_username, $remote_auth_token ] ) + or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. my ($author) = $class->_login( $user, $pass ); die _fault( MT->translate("Invalid login") ) unless $author; @@ -1372,13 +1488,16 @@ sub newMediaObject { my $class = shift; my ( $blog_id, $user, $pass, $file ) = @_; + + _validate_params( [ $blog_id, $user, $pass ] ) or return; + _validate_params( [ values %$file ] ) or return; + my $mt = MT::XMLRPCServer::Util::mt_new(); ## Will die if MT->new fails. my ( $author, $perms ) = $class->_login( $user, $pass, $blog_id ); die _fault( MT->translate("Invalid login") ) unless $author; die _fault( MT->translate("Not allowed to upload files") ) if !$author->is_superuser - && ( !$perms - || !$perms->can_do('upload_asset_via_xmlrpc_server') ); + && ( !$perms || !$perms->can_do('upload_asset_via_xmlrpc_server') ); require MT::Blog; require File::Spec; @@ -1418,15 +1537,15 @@ } my $local_file = File::Spec->catfile( $blog->site_path, $file->{name} ); - my $ext - = ( File::Basename::fileparse( $local_file, qr/[A-Za-z0-9]+$/ ) )[2]; + my $ext = ( File::Basename::fileparse( $local_file, qr/[A-Za-z0-9]+$/ ) )[2]; require MT::Asset::Image; if ( MT::Asset::Image->can_handle($ext) ) { require MT::Image; my $fh; my $data = $file->{bits}; open( $fh, "+<", \$data ); - close($fh), die _fault( + close($fh), + die _fault( MT->translate( "Saving [_1] failed: [_2]", $file->{name}, @@ -1471,8 +1590,7 @@ my $asset_pkg = MT::Asset->handler_for_file($local_basename); my $is_image = 0; if ( defined($w) && defined($h) ) { - $is_image = 1 - if $asset_pkg->isa('MT::Asset::Image'); + $is_image = 1 if $asset_pkg->isa('MT::Asset::Image'); } else {