1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_stoc.hxx" 26 27 #include <vector> 28 29 #include <osl/process.h> 30 #include <osl/socket.hxx> 31 #include <osl/mutex.hxx> 32 33 #include <rtl/string.hxx> 34 #include <rtl/ustrbuf.hxx> 35 36 #include <com/sun/star/security/RuntimePermission.hpp> 37 #include <com/sun/star/security/AllPermission.hpp> 38 #include <com/sun/star/io/FilePermission.hpp> 39 #include <com/sun/star/connection/SocketPermission.hpp> 40 #include <com/sun/star/security/AccessControlException.hpp> 41 42 #include "permissions.h" 43 44 #define OUSTR(x) ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(x) ) 45 46 47 using namespace ::std; 48 using namespace ::osl; 49 using namespace ::com::sun::star; 50 using namespace ::com::sun::star::uno; 51 using ::rtl::OUString; 52 using ::rtl::OUStringBuffer; 53 54 namespace stoc_sec 55 { 56 57 //-------------------------------------------------------------------------------------------------- 58 static inline sal_Int32 makeMask( 59 OUString const & items, char const * const * strings ) SAL_THROW( () ) 60 { 61 sal_Int32 mask = 0; 62 63 sal_Int32 n = 0; 64 do 65 { 66 OUString item( items.getToken( 0, ',', n ).trim() ); 67 if (! item.getLength()) 68 continue; 69 sal_Int32 nPos = 0; 70 while (strings[ nPos ]) 71 { 72 if (item.equalsAscii( strings[ nPos ] )) 73 { 74 mask |= (0x80000000 >> nPos); 75 break; 76 } 77 ++nPos; 78 } 79 #if OSL_DEBUG_LEVEL > 0 80 if (! strings[ nPos ]) 81 { 82 OUStringBuffer buf( 48 ); 83 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("### ignoring unknown socket action: ") ); 84 buf.append( item ); 85 ::rtl::OString str( ::rtl::OUStringToOString( 86 buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) ); 87 OSL_TRACE( str.getStr() ); 88 } 89 #endif 90 } 91 while (n >= 0); // all items 92 return mask; 93 } 94 //-------------------------------------------------------------------------------------------------- 95 static inline OUString makeStrings( 96 sal_Int32 mask, char const * const * strings ) SAL_THROW( () ) 97 { 98 OUStringBuffer buf( 48 ); 99 while (mask) 100 { 101 if (0x80000000 & mask) 102 { 103 buf.appendAscii( *strings ); 104 if (mask << 1) // more items following 105 buf.append( (sal_Unicode)',' ); 106 } 107 mask = (mask << 1); 108 ++strings; 109 } 110 return buf.makeStringAndClear(); 111 } 112 113 //################################################################################################## 114 115 //================================================================================================== 116 class SocketPermission : public Permission 117 { 118 static char const * s_actions []; 119 sal_Int32 m_actions; 120 121 OUString m_host; 122 sal_Int32 m_lowerPort; 123 sal_Int32 m_upperPort; 124 mutable OUString m_ip; 125 mutable bool m_resolveErr; 126 mutable bool m_resolvedHost; 127 bool m_wildCardHost; 128 129 inline bool resolveHost() const SAL_THROW( () ); 130 131 public: 132 SocketPermission( 133 connection::SocketPermission const & perm, 134 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ) 135 SAL_THROW( () ); 136 virtual bool implies( Permission const & perm ) const SAL_THROW( () ); 137 virtual OUString toString() const SAL_THROW( () ); 138 }; 139 //__________________________________________________________________________________________________ 140 char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", 0 }; 141 //__________________________________________________________________________________________________ 142 SocketPermission::SocketPermission( 143 connection::SocketPermission const & perm, 144 ::rtl::Reference< Permission > const & next ) 145 SAL_THROW( () ) 146 : Permission( SOCKET, next ) 147 , m_actions( makeMask( perm.Actions, s_actions ) ) 148 , m_host( perm.Host ) 149 , m_lowerPort( 0 ) 150 , m_upperPort( 65535 ) 151 , m_resolveErr( false ) 152 , m_resolvedHost( false ) 153 , m_wildCardHost( perm.Host.getLength() && '*' == perm.Host.pData->buffer[ 0 ] ) 154 { 155 if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied 156 m_actions |= 0x10000000; 157 158 // separate host from portrange 159 sal_Int32 colon = m_host.indexOf( ':' ); 160 if (colon >= 0) // port [range] given 161 { 162 sal_Int32 minus = m_host.indexOf( '-', colon +1 ); 163 if (minus < 0) 164 { 165 m_lowerPort = m_upperPort = m_host.copy( colon +1 ).toInt32(); 166 } 167 else if (minus == (colon +1)) // -N 168 { 169 m_upperPort = m_host.copy( minus +1 ).toInt32(); 170 } 171 else if (minus == (m_host.getLength() -1)) // N- 172 { 173 m_lowerPort = m_host.copy( colon +1, m_host.getLength() -1 -colon -1 ).toInt32(); 174 } 175 else // A-B 176 { 177 m_lowerPort = m_host.copy( colon +1, minus - colon -1 ).toInt32(); 178 m_upperPort = m_host.copy( minus +1, m_host.getLength() -minus -1 ).toInt32(); 179 } 180 m_host = m_host.copy( 0, colon ); 181 } 182 } 183 //__________________________________________________________________________________________________ 184 inline bool SocketPermission::resolveHost() const SAL_THROW( () ) 185 { 186 if (m_resolveErr) 187 return false; 188 189 if (! m_resolvedHost) 190 { 191 // dns lookup 192 SocketAddr addr; 193 SocketAddr::resolveHostname( m_host, addr ); 194 OUString ip; 195 m_resolveErr = (::osl_Socket_Ok != ::osl_getDottedInetAddrOfSocketAddr( 196 addr.getHandle(), &ip.pData )); 197 if (m_resolveErr) 198 return false; 199 200 MutexGuard guard( Mutex::getGlobalMutex() ); 201 if (! m_resolvedHost) 202 { 203 m_ip = ip; 204 m_resolvedHost = true; 205 } 206 } 207 return m_resolvedHost; 208 } 209 //__________________________________________________________________________________________________ 210 bool SocketPermission::implies( Permission const & perm ) const SAL_THROW( () ) 211 { 212 // check type 213 if (SOCKET != perm.m_type) 214 return false; 215 SocketPermission const & demanded = static_cast< SocketPermission const & >( perm ); 216 217 // check actions 218 if ((m_actions & demanded.m_actions) != demanded.m_actions) 219 return false; 220 221 // check ports 222 if (demanded.m_lowerPort < m_lowerPort) 223 return false; 224 if (demanded.m_upperPort > m_upperPort) 225 return false; 226 227 // quick check host (DNS names: RFC 1034/1035) 228 if (m_host.equalsIgnoreAsciiCase( demanded.m_host )) 229 return true; 230 // check for host wildcards 231 if (m_wildCardHost) 232 { 233 OUString const & demanded_host = demanded.m_host; 234 if (demanded_host.getLength() <= m_host.getLength()) 235 return false; 236 sal_Int32 len = m_host.getLength() -1; // skip star 237 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( 238 demanded_host.getStr() + demanded_host.getLength() - len, len, 239 m_host.pData->buffer + 1, len )); 240 } 241 if (demanded.m_wildCardHost) 242 return false; 243 244 // compare IP addresses 245 if (! resolveHost()) 246 return false; 247 if (! demanded.resolveHost()) 248 return false; 249 return (sal_False != m_ip.equals( demanded.m_ip )); 250 } 251 //__________________________________________________________________________________________________ 252 OUString SocketPermission::toString() const SAL_THROW( () ) 253 { 254 OUStringBuffer buf( 48 ); 255 // host 256 buf.appendAscii( 257 RTL_CONSTASCII_STRINGPARAM("com.sun.star.connection.SocketPermission (host=\"") ); 258 buf.append( m_host ); 259 if (m_resolvedHost) 260 { 261 buf.append( (sal_Unicode)'[' ); 262 buf.append( m_ip ); 263 buf.append( (sal_Unicode)']' ); 264 } 265 // port 266 if (0 != m_lowerPort || 65535 != m_upperPort) 267 { 268 buf.append( (sal_Unicode)':' ); 269 if (m_lowerPort > 0) 270 buf.append( m_lowerPort ); 271 if (m_upperPort > m_lowerPort) 272 { 273 buf.append( (sal_Unicode)'-' ); 274 if (m_upperPort < 65535) 275 buf.append( m_upperPort ); 276 } 277 } 278 // actions 279 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\", actions=\"") ); 280 buf.append( makeStrings( m_actions, s_actions ) ); 281 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\")") ); 282 return buf.makeStringAndClear(); 283 } 284 285 //################################################################################################## 286 287 //================================================================================================== 288 class FilePermission : public Permission 289 { 290 static char const * s_actions []; 291 sal_Int32 m_actions; 292 293 OUString m_url; 294 bool m_allFiles; 295 296 public: 297 FilePermission( 298 io::FilePermission const & perm, 299 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ) 300 SAL_THROW( () ); 301 virtual bool implies( Permission const & perm ) const SAL_THROW( () ); 302 virtual OUString toString() const SAL_THROW( () ); 303 }; 304 //__________________________________________________________________________________________________ 305 char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", 0 }; 306 //-------------------------------------------------------------------------------------------------- 307 static OUString const & getWorkingDir() SAL_THROW( () ) 308 { 309 static OUString * s_workingDir = 0; 310 if (! s_workingDir) 311 { 312 OUString workingDir; 313 ::osl_getProcessWorkingDir( &workingDir.pData ); 314 315 MutexGuard guard( Mutex::getGlobalMutex() ); 316 if (! s_workingDir) 317 { 318 static OUString s_dir( workingDir ); 319 s_workingDir = &s_dir; 320 } 321 } 322 return *s_workingDir; 323 } 324 //__________________________________________________________________________________________________ 325 FilePermission::FilePermission( 326 io::FilePermission const & perm, 327 ::rtl::Reference< Permission > const & next ) 328 SAL_THROW( () ) 329 : Permission( FILE, next ) 330 , m_actions( makeMask( perm.Actions, s_actions ) ) 331 , m_url( perm.URL ) 332 , m_allFiles( sal_False != perm.URL.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("<<ALL FILES>>")) ) 333 { 334 if (! m_allFiles) 335 { 336 if (m_url.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("*") )) 337 { 338 OUStringBuffer buf( 64 ); 339 buf.append( getWorkingDir() ); 340 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("/*") ); 341 m_url = buf.makeStringAndClear(); 342 } 343 else if (m_url.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM("-") )) 344 { 345 OUStringBuffer buf( 64 ); 346 buf.append( getWorkingDir() ); 347 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("/-") ); 348 m_url = buf.makeStringAndClear(); 349 } 350 else if (0 != m_url.compareToAscii( RTL_CONSTASCII_STRINGPARAM("file:///") )) 351 { 352 // relative path 353 OUString out; 354 oslFileError rc = ::osl_getAbsoluteFileURL( 355 getWorkingDir().pData, perm.URL.pData, &out.pData ); 356 m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback 357 } 358 #ifdef SAL_W32 359 // correct win drive letters 360 if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X| 361 { 362 static OUString s_colon = OUSTR(":"); 363 // common case in API is a ':' (sal), so convert '|' to ':' 364 m_url = m_url.replaceAt( 9, 1, s_colon ); 365 } 366 #endif 367 } 368 } 369 //__________________________________________________________________________________________________ 370 bool FilePermission::implies( Permission const & perm ) const SAL_THROW( () ) 371 { 372 // check type 373 if (FILE != perm.m_type) 374 return false; 375 FilePermission const & demanded = static_cast< FilePermission const & >( perm ); 376 377 // check actions 378 if ((m_actions & demanded.m_actions) != demanded.m_actions) 379 return false; 380 381 // check url 382 if (m_allFiles) 383 return true; 384 if (demanded.m_allFiles) 385 return false; 386 387 #ifdef SAL_W32 388 if (m_url.equalsIgnoreAsciiCase( demanded.m_url )) 389 return true; 390 #else 391 if (m_url.equals( demanded.m_url )) 392 return true; 393 #endif 394 if (m_url.getLength() > demanded.m_url.getLength()) 395 return false; 396 // check /- wildcard: all files and recursive in that path 397 if (1 < m_url.getLength() && 398 0 == ::rtl_ustr_ascii_compare_WithLength( m_url.getStr() + m_url.getLength() - 2, 2, "/-" )) 399 { 400 // demanded url must start with granted path (including path trailing path sep) 401 sal_Int32 len = m_url.getLength() -1; 402 #ifdef SAL_W32 403 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( 404 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )); 405 #else 406 return (0 == ::rtl_ustr_reverseCompare_WithLength( 407 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )); 408 #endif 409 } 410 // check /* wildcard: all files in that path (not recursive!) 411 if (1 < m_url.getLength() && 412 0 == ::rtl_ustr_ascii_compare_WithLength( m_url.getStr() + m_url.getLength() - 2, 2, "/*" )) 413 { 414 // demanded url must start with granted path (including path trailing path sep) 415 sal_Int32 len = m_url.getLength() -1; 416 #ifdef SAL_W32 417 return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( 418 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) && 419 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper pathes 420 #else 421 return ((0 == ::rtl_ustr_reverseCompare_WithLength( 422 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) && 423 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper pathes 424 #endif 425 } 426 return false; 427 } 428 //__________________________________________________________________________________________________ 429 OUString FilePermission::toString() const SAL_THROW( () ) 430 { 431 OUStringBuffer buf( 48 ); 432 // url 433 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("com.sun.star.io.FilePermission (url=\"") ); 434 buf.append( m_url ); 435 // actions 436 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\", actions=\"") ); 437 buf.append( makeStrings( m_actions, s_actions ) ); 438 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\")") ); 439 return buf.makeStringAndClear(); 440 } 441 442 //################################################################################################## 443 444 //================================================================================================== 445 class RuntimePermission : public Permission 446 { 447 OUString m_name; 448 449 public: 450 inline RuntimePermission( 451 security::RuntimePermission const & perm, 452 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ) 453 SAL_THROW( () ) 454 : Permission( RUNTIME, next ) 455 , m_name( perm.Name ) 456 {} 457 virtual bool implies( Permission const & perm ) const SAL_THROW( () ); 458 virtual OUString toString() const SAL_THROW( () ); 459 }; 460 //__________________________________________________________________________________________________ 461 bool RuntimePermission::implies( Permission const & perm ) const SAL_THROW( () ) 462 { 463 // check type 464 if (RUNTIME != perm.m_type) 465 return false; 466 RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm ); 467 468 // check name 469 return (sal_False != m_name.equals( demanded.m_name )); 470 } 471 //__________________________________________________________________________________________________ 472 OUString RuntimePermission::toString() const SAL_THROW( () ) 473 { 474 OUStringBuffer buf( 48 ); 475 buf.appendAscii( 476 RTL_CONSTASCII_STRINGPARAM("com.sun.star.security.RuntimePermission (name=\"") ); 477 buf.append( m_name ); 478 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("\")") ); 479 return buf.makeStringAndClear(); 480 } 481 482 //################################################################################################## 483 484 //__________________________________________________________________________________________________ 485 bool AllPermission::implies( Permission const & ) const SAL_THROW( () ) 486 { 487 return true; 488 } 489 //__________________________________________________________________________________________________ 490 OUString AllPermission::toString() const SAL_THROW( () ) 491 { 492 return OUSTR("com.sun.star.security.AllPermission"); 493 } 494 495 //################################################################################################## 496 497 //__________________________________________________________________________________________________ 498 PermissionCollection::PermissionCollection( 499 Sequence< Any > const & permissions, PermissionCollection const & addition ) 500 SAL_THROW( (RuntimeException) ) 501 : m_head( addition.m_head ) 502 { 503 Any const * perms = permissions.getConstArray(); 504 for ( sal_Int32 nPos = permissions.getLength(); nPos--; ) 505 { 506 Any const & perm = perms[ nPos ]; 507 Type const & perm_type = perm.getValueType(); 508 509 // supported permission types 510 if (perm_type.equals( ::getCppuType( (io::FilePermission const *)0 ) )) 511 { 512 m_head = new FilePermission( 513 *reinterpret_cast< io::FilePermission const * >( perm.pData ), m_head ); 514 } 515 else if (perm_type.equals( ::getCppuType( (connection::SocketPermission const *)0 ) )) 516 { 517 m_head = new SocketPermission( 518 *reinterpret_cast< connection::SocketPermission const * >( perm.pData ), m_head ); 519 } 520 else if (perm_type.equals( ::getCppuType( (security::RuntimePermission const *)0 ) )) 521 { 522 m_head = new RuntimePermission( 523 *reinterpret_cast< security::RuntimePermission const * >( perm.pData ), m_head ); 524 } 525 else if (perm_type.equals( ::getCppuType( (security::AllPermission const *)0 ) )) 526 { 527 m_head = new AllPermission( m_head ); 528 } 529 else 530 { 531 OUStringBuffer buf( 48 ); 532 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM( 533 "checking for unsupported permission type: ") ); 534 buf.append( perm_type.getTypeName() ); 535 throw RuntimeException( 536 buf.makeStringAndClear(), Reference< XInterface >() ); 537 } 538 } 539 } 540 #ifdef __DIAGNOSE 541 //__________________________________________________________________________________________________ 542 Sequence< OUString > PermissionCollection::toStrings() const SAL_THROW( () ) 543 { 544 vector< OUString > strings; 545 strings.reserve( 8 ); 546 for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() ) 547 { 548 strings.push_back( perm->toString() ); 549 } 550 return Sequence< OUString >( 551 strings.empty() ? 0 : &strings[ 0 ], strings.size() ); 552 } 553 #endif 554 //__________________________________________________________________________________________________ 555 inline static bool __implies( 556 ::rtl::Reference< Permission > const & head, Permission const & demanded ) SAL_THROW( () ) 557 { 558 for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() ) 559 { 560 if (perm->implies( demanded )) 561 return true; 562 } 563 return false; 564 } 565 566 #ifdef __DIAGNOSE 567 //-------------------------------------------------------------------------------------------------- 568 static void demanded_diag( 569 Permission const & perm ) 570 SAL_THROW( () ) 571 { 572 OUStringBuffer buf( 48 ); 573 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("demanding ") ); 574 buf.append( perm.toString() ); 575 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM(" => ok.") ); 576 ::rtl::OString str( 577 ::rtl::OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) ); 578 OSL_TRACE( str.getStr() ); 579 } 580 #endif 581 //-------------------------------------------------------------------------------------------------- 582 static void throwAccessControlException( 583 Permission const & perm, Any const & demanded_perm ) 584 SAL_THROW( (security::AccessControlException) ) 585 { 586 OUStringBuffer buf( 48 ); 587 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("access denied: ") ); 588 buf.append( perm.toString() ); 589 throw security::AccessControlException( 590 buf.makeStringAndClear(), Reference< XInterface >(), demanded_perm ); 591 } 592 //================================================================================================== 593 void PermissionCollection::checkPermission( Any const & perm ) const 594 SAL_THROW( (RuntimeException) ) 595 { 596 Type const & demanded_type = perm.getValueType(); 597 598 // supported permission types 599 // stack object of SimpleReferenceObject are ok, as long as they are not 600 // assigned to a ::rtl::Reference<> (=> delete this) 601 if (demanded_type.equals( ::getCppuType( (io::FilePermission const *)0 ) )) 602 { 603 FilePermission demanded( 604 *reinterpret_cast< io::FilePermission const * >( perm.pData ) ); 605 if (__implies( m_head, demanded )) 606 { 607 #ifdef __DIAGNOSE 608 demanded_diag( demanded ); 609 #endif 610 return; 611 } 612 throwAccessControlException( demanded, perm ); 613 } 614 else if (demanded_type.equals( ::getCppuType( (connection::SocketPermission const *)0 ) )) 615 { 616 SocketPermission demanded( 617 *reinterpret_cast< connection::SocketPermission const * >( perm.pData ) ); 618 if (__implies( m_head, demanded )) 619 { 620 #ifdef __DIAGNOSE 621 demanded_diag( demanded ); 622 #endif 623 return; 624 } 625 throwAccessControlException( demanded, perm ); 626 } 627 else if (demanded_type.equals( ::getCppuType( (security::RuntimePermission const *)0 ) )) 628 { 629 RuntimePermission demanded( 630 *reinterpret_cast< security::RuntimePermission const * >( perm.pData ) ); 631 if (__implies( m_head, demanded )) 632 { 633 #ifdef __DIAGNOSE 634 demanded_diag( demanded ); 635 #endif 636 return; 637 } 638 throwAccessControlException( demanded, perm ); 639 } 640 else if (demanded_type.equals( ::getCppuType( (security::AllPermission const *)0 ) )) 641 { 642 AllPermission demanded; 643 if (__implies( m_head, demanded )) 644 { 645 #ifdef __DIAGNOSE 646 demanded_diag( demanded ); 647 #endif 648 return; 649 } 650 throwAccessControlException( demanded, perm ); 651 } 652 else 653 { 654 OUStringBuffer buf( 48 ); 655 buf.appendAscii( RTL_CONSTASCII_STRINGPARAM("checking for unsupported permission type: ") ); 656 buf.append( demanded_type.getTypeName() ); 657 throw RuntimeException( 658 buf.makeStringAndClear(), Reference< XInterface >() ); 659 } 660 } 661 662 } 663