Version 6.1.0.2, tag libreoffice-6.1.0.2
[LibreOffice.git] / stoc / source / security / permissions.cxx
blobf996df36bd152a2a09adc07ad37241c9e252282a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
21 #include <vector>
23 #include <osl/process.h>
24 #include <osl/socket.hxx>
25 #include <osl/mutex.hxx>
27 #include <rtl/string.hxx>
28 #include <rtl/ustrbuf.hxx>
29 #include <osl/diagnose.h>
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>
37 #include "permissions.h"
40 using namespace ::std;
41 using namespace ::osl;
42 using namespace ::com::sun::star;
43 using namespace css::uno;
45 namespace stoc_sec
49 static inline sal_Int32 makeMask(
50 OUString const & items, char const * const * strings )
52 sal_Int32 mask = 0;
54 sal_Int32 n = 0;
57 OUString item( items.getToken( 0, ',', n ).trim() );
58 if ( item.isEmpty())
59 continue;
60 sal_Int32 nPos = 0;
61 while (strings[ nPos ])
63 if (item.equalsAscii( strings[ nPos ] ))
65 mask |= (0x80000000 >> nPos);
66 break;
68 ++nPos;
70 #if OSL_DEBUG_LEVEL > 0
71 if (! strings[ nPos ])
73 SAL_WARN("stoc", "ignoring unknown socket action: " << item );
75 #endif
77 while (n >= 0); // all items
78 return mask;
81 static inline OUString makeStrings(
82 sal_Int32 mask, char const * const * strings )
84 OUStringBuffer buf( 48 );
85 while (mask)
87 if (0x80000000 & mask)
89 buf.appendAscii( *strings );
90 if ((mask << 1) != 0) // more items following
91 buf.append( ',' );
93 mask = (mask << 1);
94 ++strings;
96 return buf.makeStringAndClear();
100 class SocketPermission : public Permission
102 static char const * s_actions [];
103 sal_Int32 m_actions;
105 OUString m_host;
106 sal_Int32 m_lowerPort;
107 sal_Int32 m_upperPort;
108 mutable OUString m_ip;
109 mutable bool m_resolveErr;
110 mutable bool m_resolvedHost;
111 bool m_wildCardHost;
113 inline bool resolveHost() const;
115 public:
116 SocketPermission(
117 connection::SocketPermission const & perm,
118 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
119 virtual bool implies( Permission const & perm ) const override;
120 virtual OUString toString() const override;
123 char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", nullptr };
125 SocketPermission::SocketPermission(
126 connection::SocketPermission const & perm,
127 ::rtl::Reference< Permission > const & next )
128 : Permission( SOCKET, next )
129 , m_actions( makeMask( perm.Actions, s_actions ) )
130 , m_host( perm.Host )
131 , m_lowerPort( 0 )
132 , m_upperPort( 65535 )
133 , m_resolveErr( false )
134 , m_resolvedHost( false )
135 , m_wildCardHost( !perm.Host.isEmpty() && '*' == perm.Host.pData->buffer[ 0 ] )
137 if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied
138 m_actions |= 0x10000000;
140 // separate host from portrange
141 sal_Int32 colon = m_host.indexOf( ':' );
142 if (colon >= 0) // port [range] given
144 sal_Int32 minus = m_host.indexOf( '-', colon +1 );
145 if (minus < 0)
147 m_lowerPort = m_upperPort = m_host.copy( colon +1 ).toInt32();
149 else if (minus == (colon +1)) // -N
151 m_upperPort = m_host.copy( minus +1 ).toInt32();
153 else if (minus == (m_host.getLength() -1)) // N-
155 m_lowerPort = m_host.copy( colon +1, m_host.getLength() -1 -colon -1 ).toInt32();
157 else // A-B
159 m_lowerPort = m_host.copy( colon +1, minus - colon -1 ).toInt32();
160 m_upperPort = m_host.copy( minus +1 ).toInt32();
162 m_host = m_host.copy( 0, colon );
166 inline bool SocketPermission::resolveHost() const
168 if (m_resolveErr)
169 return false;
171 if (! m_resolvedHost)
173 // dns lookup
174 SocketAddr addr;
175 SocketAddr::resolveHostname( m_host, addr );
176 OUString ip;
177 m_resolveErr = (::osl_Socket_Ok != ::osl_getDottedInetAddrOfSocketAddr(
178 addr.getHandle(), &ip.pData ));
179 if (m_resolveErr)
180 return false;
182 MutexGuard guard( Mutex::getGlobalMutex() );
183 if (! m_resolvedHost)
185 m_ip = ip;
186 m_resolvedHost = true;
189 return m_resolvedHost;
192 bool SocketPermission::implies( Permission const & perm ) const
194 // check type
195 if (SOCKET != perm.m_type)
196 return false;
197 SocketPermission const & demanded = static_cast< SocketPermission const & >( perm );
199 // check actions
200 if ((m_actions & demanded.m_actions) != demanded.m_actions)
201 return false;
203 // check ports
204 if (demanded.m_lowerPort < m_lowerPort)
205 return false;
206 if (demanded.m_upperPort > m_upperPort)
207 return false;
209 // quick check host (DNS names: RFC 1034/1035)
210 if (m_host.equalsIgnoreAsciiCase( demanded.m_host ))
211 return true;
212 // check for host wildcards
213 if (m_wildCardHost)
215 OUString const & demanded_host = demanded.m_host;
216 if (demanded_host.getLength() <= m_host.getLength())
217 return false;
218 sal_Int32 len = m_host.getLength() -1; // skip star
219 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
220 demanded_host.getStr() + demanded_host.getLength() - len, len,
221 m_host.pData->buffer + 1, len ));
223 if (demanded.m_wildCardHost)
224 return false;
226 // compare IP addresses
227 if (! resolveHost())
228 return false;
229 if (! demanded.resolveHost())
230 return false;
231 return m_ip == demanded.m_ip;
234 OUString SocketPermission::toString() const
236 OUStringBuffer buf( 48 );
237 // host
238 buf.append( "com.sun.star.connection.SocketPermission (host=\"" );
239 buf.append( m_host );
240 if (m_resolvedHost)
242 buf.append( '[' );
243 buf.append( m_ip );
244 buf.append( ']' );
246 // port
247 if (0 != m_lowerPort || 65535 != m_upperPort)
249 buf.append( ':' );
250 if (m_lowerPort > 0)
251 buf.append( m_lowerPort );
252 if (m_upperPort > m_lowerPort)
254 buf.append( '-' );
255 if (m_upperPort < 65535)
256 buf.append( m_upperPort );
259 // actions
260 buf.append( "\", actions=\"" );
261 buf.append( makeStrings( m_actions, s_actions ) );
262 buf.append( "\")" );
263 return buf.makeStringAndClear();
267 class FilePermission : public Permission
269 static char const * s_actions [];
270 sal_Int32 m_actions;
272 OUString m_url;
273 bool m_allFiles;
275 public:
276 FilePermission(
277 io::FilePermission const & perm,
278 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
279 virtual bool implies( Permission const & perm ) const override;
280 virtual OUString toString() const override;
283 char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", nullptr };
285 static OUString const & getWorkingDir()
287 static OUString * s_workingDir = nullptr;
288 if (! s_workingDir)
290 OUString workingDir;
291 ::osl_getProcessWorkingDir( &workingDir.pData );
293 MutexGuard guard( Mutex::getGlobalMutex() );
294 if (! s_workingDir)
296 static OUString s_dir( workingDir );
297 s_workingDir = &s_dir;
300 return *s_workingDir;
303 FilePermission::FilePermission(
304 io::FilePermission const & perm,
305 ::rtl::Reference< Permission > const & next )
306 : Permission( FILE, next )
307 , m_actions( makeMask( perm.Actions, s_actions ) )
308 , m_url( perm.URL )
309 , m_allFiles( perm.URL == "<<ALL FILES>>" )
311 if (! m_allFiles)
313 if ( m_url == "*" )
315 OUStringBuffer buf( 64 );
316 buf.append( getWorkingDir() );
317 buf.append( "/*" );
318 m_url = buf.makeStringAndClear();
320 else if ( m_url == "-" )
322 OUStringBuffer buf( 64 );
323 buf.append( getWorkingDir() );
324 buf.append( "/-" );
325 m_url = buf.makeStringAndClear();
327 else if (!m_url.startsWith("file:///"))
329 // relative path
330 OUString out;
331 oslFileError rc = ::osl_getAbsoluteFileURL(
332 getWorkingDir().pData, perm.URL.pData, &out.pData );
333 m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback
335 #ifdef _WIN32
336 // correct win drive letters
337 if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X|
339 static OUString s_colon = ":";
340 // common case in API is a ':' (sal), so convert '|' to ':'
341 m_url = m_url.replaceAt( 9, 1, s_colon );
343 #endif
347 bool FilePermission::implies( Permission const & perm ) const
349 // check type
350 if (FILE != perm.m_type)
351 return false;
352 FilePermission const & demanded = static_cast< FilePermission const & >( perm );
354 // check actions
355 if ((m_actions & demanded.m_actions) != demanded.m_actions)
356 return false;
358 // check url
359 if (m_allFiles)
360 return true;
361 if (demanded.m_allFiles)
362 return false;
364 #ifdef _WIN32
365 if (m_url.equalsIgnoreAsciiCase( demanded.m_url ))
366 return true;
367 #else
368 if (m_url == demanded.m_url )
369 return true;
370 #endif
371 if (m_url.getLength() > demanded.m_url.getLength())
372 return false;
373 // check /- wildcard: all files and recursive in that path
374 if (m_url.endsWith("/-"))
376 // demanded url must start with granted path (including path trailing path sep)
377 sal_Int32 len = m_url.getLength() -1;
378 #ifdef _WIN32
379 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
380 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
381 #else
382 return (0 == ::rtl_ustr_reverseCompare_WithLength(
383 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
384 #endif
386 // check /* wildcard: all files in that path (not recursive!)
387 if (m_url.endsWith("/*"))
389 // demanded url must start with granted path (including path trailing path sep)
390 sal_Int32 len = m_url.getLength() -1;
391 #ifdef _WIN32
392 return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
393 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
394 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
395 #else
396 return ((0 == ::rtl_ustr_reverseCompare_WithLength(
397 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
398 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
399 #endif
401 return false;
404 OUString FilePermission::toString() const
406 OUStringBuffer buf( 48 );
407 // url
408 buf.append( "com.sun.star.io.FilePermission (url=\"" );
409 buf.append( m_url );
410 // actions
411 buf.append( "\", actions=\"" );
412 buf.append( makeStrings( m_actions, s_actions ) );
413 buf.append( "\")" );
414 return buf.makeStringAndClear();
418 class RuntimePermission : public Permission
420 OUString m_name;
422 public:
423 RuntimePermission(
424 security::RuntimePermission const & perm,
425 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() )
426 : Permission( RUNTIME, next )
427 , m_name( perm.Name )
429 virtual bool implies( Permission const & perm ) const override;
430 virtual OUString toString() const override;
433 bool RuntimePermission::implies( Permission const & perm ) const
435 // check type
436 if (RUNTIME != perm.m_type)
437 return false;
438 RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm );
440 // check name
441 return m_name == demanded.m_name;
444 OUString RuntimePermission::toString() const
446 OUStringBuffer buf( 48 );
447 buf.append( "com.sun.star.security.RuntimePermission (name=\"" );
448 buf.append( m_name );
449 buf.append( "\")" );
450 return buf.makeStringAndClear();
454 bool AllPermission::implies( Permission const & ) const
456 return true;
459 OUString AllPermission::toString() const
461 return OUString("com.sun.star.security.AllPermission");
465 PermissionCollection::PermissionCollection(
466 Sequence< Any > const & permissions, PermissionCollection const & addition )
467 : m_head( addition.m_head )
469 Any const * perms = permissions.getConstArray();
470 for ( sal_Int32 nPos = permissions.getLength(); nPos--; )
472 Any const & perm = perms[ nPos ];
473 Type const & perm_type = perm.getValueType();
475 // supported permission types
476 if (perm_type.equals( cppu::UnoType<io::FilePermission>::get()))
478 m_head = new FilePermission(
479 *static_cast< io::FilePermission const * >( perm.pData ), m_head );
481 else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
483 m_head = new SocketPermission(
484 *static_cast< connection::SocketPermission const * >( perm.pData ), m_head );
486 else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
488 m_head = new RuntimePermission(
489 *static_cast< security::RuntimePermission const * >( perm.pData ), m_head );
491 else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get()))
493 m_head = new AllPermission( m_head );
495 else
497 throw RuntimeException( "checking for unsupported permission type: " + perm_type.getTypeName() );
501 #ifdef __DIAGNOSE
503 Sequence< OUString > PermissionCollection::toStrings() const
505 vector< OUString > strings;
506 strings.reserve( 8 );
507 for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() )
509 strings.push_back( perm->toString() );
511 return Sequence< OUString >( strings.data(), strings.size() );
513 #endif
515 inline static bool implies(
516 ::rtl::Reference< Permission > const & head, Permission const & demanded )
518 for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() )
520 if (perm->implies( demanded ))
521 return true;
523 return false;
526 #ifdef __DIAGNOSE
528 static void demanded_diag(
529 Permission const & perm )
531 OUStringBuffer buf( 48 );
532 buf.append( "demanding " );
533 buf.append( perm.toString() );
534 buf.append( " => ok." );
535 OString str(
536 OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
537 SAL_INFO("stoc",( "%s", str.getStr() );
539 #endif
541 static void throwAccessControlException(
542 Permission const & perm, Any const & demanded_perm )
544 throw security::AccessControlException(
545 "access denied: " + perm.toString(),
546 Reference< XInterface >(), demanded_perm );
549 void PermissionCollection::checkPermission( Any const & perm ) const
551 Type const & demanded_type = perm.getValueType();
553 // supported permission types
554 // stack object of SimpleReferenceObject are ok, as long as they are not
555 // assigned to a ::rtl::Reference<> (=> delete this)
556 if (demanded_type.equals( cppu::UnoType<io::FilePermission>::get()))
558 FilePermission demanded(
559 *static_cast< io::FilePermission const * >( perm.pData ) );
560 if (implies( m_head, demanded ))
562 #ifdef __DIAGNOSE
563 demanded_diag( demanded );
564 #endif
565 return;
567 throwAccessControlException( demanded, perm );
569 else if (demanded_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
571 SocketPermission demanded(
572 *static_cast< connection::SocketPermission const * >( perm.pData ) );
573 if (implies( m_head, demanded ))
575 #ifdef __DIAGNOSE
576 demanded_diag( demanded );
577 #endif
578 return;
580 throwAccessControlException( demanded, perm );
582 else if (demanded_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
584 RuntimePermission demanded(
585 *static_cast< security::RuntimePermission const * >( perm.pData ) );
586 if (implies( m_head, demanded ))
588 #ifdef __DIAGNOSE
589 demanded_diag( demanded );
590 #endif
591 return;
593 throwAccessControlException( demanded, perm );
595 else if (demanded_type.equals( cppu::UnoType<security::AllPermission>::get()))
597 AllPermission demanded;
598 if (implies( m_head, demanded ))
600 #ifdef __DIAGNOSE
601 demanded_diag( demanded );
602 #endif
603 return;
605 throwAccessControlException( demanded, perm );
607 else
609 throw RuntimeException( "checking for unsupported permission type: " + demanded_type.getTypeName() );
615 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */