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 : #include <osl/diagnose.h>
30 :
31 : #include <com/sun/star/security/RuntimePermission.hpp>
32 : #include <com/sun/star/security/AllPermission.hpp>
33 : #include <com/sun/star/io/FilePermission.hpp>
34 : #include <com/sun/star/connection/SocketPermission.hpp>
35 : #include <com/sun/star/security/AccessControlException.hpp>
36 :
37 : #include "permissions.h"
38 :
39 :
40 : using namespace ::std;
41 : using namespace ::osl;
42 : using namespace ::com::sun::star;
43 : using namespace css::uno;
44 :
45 : namespace stoc_sec
46 : {
47 :
48 :
49 0 : static inline sal_Int32 makeMask(
50 : OUString const & items, char const * const * strings )
51 : {
52 0 : sal_Int32 mask = 0;
53 :
54 0 : sal_Int32 n = 0;
55 0 : do
56 : {
57 0 : OUString item( items.getToken( 0, ',', n ).trim() );
58 0 : if ( item.isEmpty())
59 0 : continue;
60 0 : sal_Int32 nPos = 0;
61 0 : while (strings[ nPos ])
62 : {
63 0 : if (item.equalsAscii( strings[ nPos ] ))
64 : {
65 0 : mask |= (0x80000000 >> nPos);
66 0 : break;
67 : }
68 0 : ++nPos;
69 0 : }
70 : #if OSL_DEBUG_LEVEL > 0
71 : if (! strings[ nPos ])
72 : {
73 : OUStringBuffer buf( 48 );
74 : buf.append( "### ignoring unknown socket action: " );
75 : buf.append( item );
76 : OString str( OUStringToOString(
77 : buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
78 : OSL_TRACE( "%s", str.getStr() );
79 : }
80 : #endif
81 : }
82 0 : while (n >= 0); // all items
83 0 : return mask;
84 : }
85 :
86 0 : static inline OUString makeStrings(
87 : sal_Int32 mask, char const * const * strings )
88 : {
89 0 : OUStringBuffer buf( 48 );
90 0 : while (mask)
91 : {
92 0 : if (0x80000000 & mask)
93 : {
94 0 : buf.appendAscii( *strings );
95 0 : if (mask << 1) // more items following
96 0 : buf.append( ',' );
97 : }
98 0 : mask = (mask << 1);
99 0 : ++strings;
100 : }
101 0 : return buf.makeStringAndClear();
102 : }
103 :
104 :
105 :
106 :
107 0 : class SocketPermission : public Permission
108 : {
109 : static char const * s_actions [];
110 : sal_Int32 m_actions;
111 :
112 : OUString m_host;
113 : sal_Int32 m_lowerPort;
114 : sal_Int32 m_upperPort;
115 : mutable OUString m_ip;
116 : mutable bool m_resolveErr;
117 : mutable bool m_resolvedHost;
118 : bool m_wildCardHost;
119 :
120 : inline bool resolveHost() const;
121 :
122 : public:
123 : SocketPermission(
124 : connection::SocketPermission const & perm,
125 : ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
126 : virtual bool implies( Permission const & perm ) const SAL_OVERRIDE;
127 : virtual OUString toString() const SAL_OVERRIDE;
128 : };
129 :
130 : char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", 0 };
131 :
132 0 : SocketPermission::SocketPermission(
133 : connection::SocketPermission const & perm,
134 : ::rtl::Reference< Permission > const & next )
135 : : Permission( SOCKET, next )
136 0 : , m_actions( makeMask( perm.Actions, s_actions ) )
137 : , m_host( perm.Host )
138 : , m_lowerPort( 0 )
139 : , m_upperPort( 65535 )
140 : , m_resolveErr( false )
141 : , m_resolvedHost( false )
142 0 : , m_wildCardHost( !perm.Host.isEmpty() && '*' == perm.Host.pData->buffer[ 0 ] )
143 : {
144 0 : if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied
145 0 : m_actions |= 0x10000000;
146 :
147 : // separate host from portrange
148 0 : sal_Int32 colon = m_host.indexOf( ':' );
149 0 : if (colon >= 0) // port [range] given
150 : {
151 0 : sal_Int32 minus = m_host.indexOf( '-', colon +1 );
152 0 : if (minus < 0)
153 : {
154 0 : m_lowerPort = m_upperPort = m_host.copy( colon +1 ).toInt32();
155 : }
156 0 : else if (minus == (colon +1)) // -N
157 : {
158 0 : m_upperPort = m_host.copy( minus +1 ).toInt32();
159 : }
160 0 : else if (minus == (m_host.getLength() -1)) // N-
161 : {
162 0 : m_lowerPort = m_host.copy( colon +1, m_host.getLength() -1 -colon -1 ).toInt32();
163 : }
164 : else // A-B
165 : {
166 0 : m_lowerPort = m_host.copy( colon +1, minus - colon -1 ).toInt32();
167 0 : m_upperPort = m_host.copy( minus +1, m_host.getLength() -minus -1 ).toInt32();
168 : }
169 0 : m_host = m_host.copy( 0, colon );
170 : }
171 0 : }
172 :
173 0 : inline bool SocketPermission::resolveHost() const
174 : {
175 0 : if (m_resolveErr)
176 0 : return false;
177 :
178 0 : if (! m_resolvedHost)
179 : {
180 : // dns lookup
181 0 : SocketAddr addr;
182 0 : SocketAddr::resolveHostname( m_host, addr );
183 0 : OUString ip;
184 0 : m_resolveErr = (::osl_Socket_Ok != ::osl_getDottedInetAddrOfSocketAddr(
185 0 : addr.getHandle(), &ip.pData ));
186 0 : if (m_resolveErr)
187 0 : return false;
188 :
189 0 : MutexGuard guard( Mutex::getGlobalMutex() );
190 0 : if (! m_resolvedHost)
191 : {
192 0 : m_ip = ip;
193 0 : m_resolvedHost = true;
194 0 : }
195 : }
196 0 : return m_resolvedHost;
197 : }
198 :
199 0 : bool SocketPermission::implies( Permission const & perm ) const
200 : {
201 : // check type
202 0 : if (SOCKET != perm.m_type)
203 0 : return false;
204 0 : SocketPermission const & demanded = static_cast< SocketPermission const & >( perm );
205 :
206 : // check actions
207 0 : if ((m_actions & demanded.m_actions) != demanded.m_actions)
208 0 : return false;
209 :
210 : // check ports
211 0 : if (demanded.m_lowerPort < m_lowerPort)
212 0 : return false;
213 0 : if (demanded.m_upperPort > m_upperPort)
214 0 : return false;
215 :
216 : // quick check host (DNS names: RFC 1034/1035)
217 0 : if (m_host.equalsIgnoreAsciiCase( demanded.m_host ))
218 0 : return true;
219 : // check for host wildcards
220 0 : if (m_wildCardHost)
221 : {
222 0 : OUString const & demanded_host = demanded.m_host;
223 0 : if (demanded_host.getLength() <= m_host.getLength())
224 0 : return false;
225 0 : sal_Int32 len = m_host.getLength() -1; // skip star
226 : return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
227 0 : demanded_host.getStr() + demanded_host.getLength() - len, len,
228 0 : m_host.pData->buffer + 1, len ));
229 : }
230 0 : if (demanded.m_wildCardHost)
231 0 : return false;
232 :
233 : // compare IP addresses
234 0 : if (! resolveHost())
235 0 : return false;
236 0 : if (! demanded.resolveHost())
237 0 : return false;
238 0 : return m_ip.equals( demanded.m_ip );
239 : }
240 :
241 0 : OUString SocketPermission::toString() const
242 : {
243 0 : OUStringBuffer buf( 48 );
244 : // host
245 0 : buf.append( "com.sun.star.connection.SocketPermission (host=\"" );
246 0 : buf.append( m_host );
247 0 : if (m_resolvedHost)
248 : {
249 0 : buf.append( '[' );
250 0 : buf.append( m_ip );
251 0 : buf.append( ']' );
252 : }
253 : // port
254 0 : if (0 != m_lowerPort || 65535 != m_upperPort)
255 : {
256 0 : buf.append( ':' );
257 0 : if (m_lowerPort > 0)
258 0 : buf.append( m_lowerPort );
259 0 : if (m_upperPort > m_lowerPort)
260 : {
261 0 : buf.append( '-' );
262 0 : if (m_upperPort < 65535)
263 0 : buf.append( m_upperPort );
264 : }
265 : }
266 : // actions
267 0 : buf.append( "\", actions=\"" );
268 0 : buf.append( makeStrings( m_actions, s_actions ) );
269 0 : buf.append( "\")" );
270 0 : return buf.makeStringAndClear();
271 : }
272 :
273 :
274 :
275 :
276 0 : class FilePermission : public Permission
277 : {
278 : static char const * s_actions [];
279 : sal_Int32 m_actions;
280 :
281 : OUString m_url;
282 : bool m_allFiles;
283 :
284 : public:
285 : FilePermission(
286 : io::FilePermission const & perm,
287 : ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
288 : virtual bool implies( Permission const & perm ) const SAL_OVERRIDE;
289 : virtual OUString toString() const SAL_OVERRIDE;
290 : };
291 :
292 : char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", 0 };
293 :
294 0 : static OUString const & getWorkingDir()
295 : {
296 : static OUString * s_workingDir = 0;
297 0 : if (! s_workingDir)
298 : {
299 0 : OUString workingDir;
300 0 : ::osl_getProcessWorkingDir( &workingDir.pData );
301 :
302 0 : MutexGuard guard( Mutex::getGlobalMutex() );
303 0 : if (! s_workingDir)
304 : {
305 0 : static OUString s_dir( workingDir );
306 0 : s_workingDir = &s_dir;
307 0 : }
308 : }
309 0 : return *s_workingDir;
310 : }
311 :
312 0 : FilePermission::FilePermission(
313 : io::FilePermission const & perm,
314 : ::rtl::Reference< Permission > const & next )
315 : : Permission( FILE, next )
316 0 : , m_actions( makeMask( perm.Actions, s_actions ) )
317 : , m_url( perm.URL )
318 0 : , m_allFiles( perm.URL == "<<ALL FILES>>" )
319 : {
320 0 : if (! m_allFiles)
321 : {
322 0 : if ( m_url == "*" )
323 : {
324 0 : OUStringBuffer buf( 64 );
325 0 : buf.append( getWorkingDir() );
326 0 : buf.append( "/*" );
327 0 : m_url = buf.makeStringAndClear();
328 : }
329 0 : else if ( m_url == "-" )
330 : {
331 0 : OUStringBuffer buf( 64 );
332 0 : buf.append( getWorkingDir() );
333 0 : buf.append( "/-" );
334 0 : m_url = buf.makeStringAndClear();
335 : }
336 0 : else if (!m_url.startsWith("file:///"))
337 : {
338 : // relative path
339 0 : OUString out;
340 : oslFileError rc = ::osl_getAbsoluteFileURL(
341 0 : getWorkingDir().pData, perm.URL.pData, &out.pData );
342 0 : m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback
343 : }
344 : #ifdef SAL_W32
345 : // correct win drive letters
346 : if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X|
347 : {
348 : static OUString s_colon = ":";
349 : // common case in API is a ':' (sal), so convert '|' to ':'
350 : m_url = m_url.replaceAt( 9, 1, s_colon );
351 : }
352 : #endif
353 : }
354 0 : }
355 :
356 0 : bool FilePermission::implies( Permission const & perm ) const
357 : {
358 : // check type
359 0 : if (FILE != perm.m_type)
360 0 : return false;
361 0 : FilePermission const & demanded = static_cast< FilePermission const & >( perm );
362 :
363 : // check actions
364 0 : if ((m_actions & demanded.m_actions) != demanded.m_actions)
365 0 : return false;
366 :
367 : // check url
368 0 : if (m_allFiles)
369 0 : return true;
370 0 : if (demanded.m_allFiles)
371 0 : return false;
372 :
373 : #ifdef SAL_W32
374 : if (m_url.equalsIgnoreAsciiCase( demanded.m_url ))
375 : return true;
376 : #else
377 0 : if (m_url.equals( demanded.m_url ))
378 0 : return true;
379 : #endif
380 0 : if (m_url.getLength() > demanded.m_url.getLength())
381 0 : return false;
382 : // check /- wildcard: all files and recursive in that path
383 0 : if (1 < m_url.getLength() &&
384 0 : 0 == ::rtl_ustr_ascii_compare_WithLength( m_url.getStr() + m_url.getLength() - 2, 2, "/-" ))
385 : {
386 : // demanded url must start with granted path (including path trailing path sep)
387 0 : sal_Int32 len = m_url.getLength() -1;
388 : #ifdef SAL_W32
389 : return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
390 : demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
391 : #else
392 : return (0 == ::rtl_ustr_reverseCompare_WithLength(
393 0 : demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
394 : #endif
395 : }
396 : // check /* wildcard: all files in that path (not recursive!)
397 0 : if (1 < m_url.getLength() &&
398 0 : 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 0 : 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 : (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
406 : #else
407 : return ((0 == ::rtl_ustr_reverseCompare_WithLength(
408 0 : demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
409 0 : (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
410 : #endif
411 : }
412 0 : return false;
413 : }
414 :
415 0 : OUString FilePermission::toString() const
416 : {
417 0 : OUStringBuffer buf( 48 );
418 : // url
419 0 : buf.append( "com.sun.star.io.FilePermission (url=\"" );
420 0 : buf.append( m_url );
421 : // actions
422 0 : buf.append( "\", actions=\"" );
423 0 : buf.append( makeStrings( m_actions, s_actions ) );
424 0 : buf.append( "\")" );
425 0 : return buf.makeStringAndClear();
426 : }
427 :
428 :
429 :
430 :
431 0 : class RuntimePermission : public Permission
432 : {
433 : OUString m_name;
434 :
435 : public:
436 0 : inline RuntimePermission(
437 : security::RuntimePermission const & perm,
438 : ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() )
439 : : Permission( RUNTIME, next )
440 0 : , m_name( perm.Name )
441 0 : {}
442 : virtual bool implies( Permission const & perm ) const SAL_OVERRIDE;
443 : virtual OUString toString() const SAL_OVERRIDE;
444 : };
445 :
446 0 : bool RuntimePermission::implies( Permission const & perm ) const
447 : {
448 : // check type
449 0 : if (RUNTIME != perm.m_type)
450 0 : return false;
451 0 : RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm );
452 :
453 : // check name
454 0 : return m_name.equals( demanded.m_name );
455 : }
456 :
457 0 : OUString RuntimePermission::toString() const
458 : {
459 0 : OUStringBuffer buf( 48 );
460 0 : buf.append( "com.sun.star.security.RuntimePermission (name=\"" );
461 0 : buf.append( m_name );
462 0 : buf.append( "\")" );
463 0 : return buf.makeStringAndClear();
464 : }
465 :
466 :
467 :
468 :
469 0 : bool AllPermission::implies( Permission const & ) const
470 : {
471 0 : return true;
472 : }
473 :
474 0 : OUString AllPermission::toString() const
475 : {
476 0 : return OUString("com.sun.star.security.AllPermission");
477 : }
478 :
479 :
480 :
481 :
482 0 : PermissionCollection::PermissionCollection(
483 : Sequence< Any > const & permissions, PermissionCollection const & addition )
484 0 : : m_head( addition.m_head )
485 : {
486 0 : Any const * perms = permissions.getConstArray();
487 0 : for ( sal_Int32 nPos = permissions.getLength(); nPos--; )
488 : {
489 0 : Any const & perm = perms[ nPos ];
490 0 : Type const & perm_type = perm.getValueType();
491 :
492 : // supported permission types
493 0 : if (perm_type.equals( cppu::UnoType<io::FilePermission>::get()))
494 : {
495 0 : m_head = new FilePermission(
496 0 : *static_cast< io::FilePermission const * >( perm.pData ), m_head );
497 : }
498 0 : else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
499 : {
500 0 : m_head = new SocketPermission(
501 0 : *static_cast< connection::SocketPermission const * >( perm.pData ), m_head );
502 : }
503 0 : else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
504 : {
505 0 : m_head = new RuntimePermission(
506 0 : *static_cast< security::RuntimePermission const * >( perm.pData ), m_head );
507 : }
508 0 : else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get()))
509 : {
510 0 : m_head = new AllPermission( m_head );
511 : }
512 : else
513 : {
514 0 : OUStringBuffer buf( 48 );
515 0 : buf.append( "checking for unsupported permission type: " );
516 0 : buf.append( perm_type.getTypeName() );
517 0 : throw RuntimeException( buf.makeStringAndClear() );
518 : }
519 : }
520 0 : }
521 : #ifdef __DIAGNOSE
522 :
523 : Sequence< OUString > PermissionCollection::toStrings() const
524 : {
525 : vector< OUString > strings;
526 : strings.reserve( 8 );
527 : for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() )
528 : {
529 : strings.push_back( perm->toString() );
530 : }
531 : return Sequence< OUString >( strings.data(), 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 : *static_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 : *static_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 : *static_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: */
|