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