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