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