summaryrefslogtreecommitdiffstats
path: root/stoc/source/security/permissions.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'stoc/source/security/permissions.cxx')
-rw-r--r--stoc/source/security/permissions.cxx604
1 files changed, 604 insertions, 0 deletions
diff --git a/stoc/source/security/permissions.cxx b/stoc/source/security/permissions.cxx
new file mode 100644
index 000000000..1799f9c8c
--- /dev/null
+++ b/stoc/source/security/permissions.cxx
@@ -0,0 +1,604 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <vector>
+
+#include <osl/process.h>
+#include <osl/socket.hxx>
+#include <osl/mutex.hxx>
+
+#include <rtl/string.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <com/sun/star/security/RuntimePermission.hpp>
+#include <com/sun/star/security/AllPermission.hpp>
+#include <com/sun/star/io/FilePermission.hpp>
+#include <com/sun/star/connection/SocketPermission.hpp>
+#include <com/sun/star/security/AccessControlException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include "permissions.h"
+
+using namespace ::std;
+using namespace ::osl;
+using namespace ::com::sun::star;
+using namespace css::uno;
+
+namespace stoc_sec
+{
+
+
+static sal_Int32 makeMask(
+ OUString const & items, char const * const * strings )
+{
+ sal_Int32 mask = 0;
+
+ sal_Int32 n = 0;
+ do
+ {
+ OUString item( o3tl::trim(o3tl::getToken(items, 0, ',', n )) );
+ if ( item.isEmpty())
+ continue;
+ sal_Int32 nPos = 0;
+ while (strings[ nPos ])
+ {
+ if (item.equalsAscii( strings[ nPos ] ))
+ {
+ mask |= (0x80000000 >> nPos);
+ break;
+ }
+ ++nPos;
+ }
+#if OSL_DEBUG_LEVEL > 0
+ if (! strings[ nPos ])
+ {
+ SAL_WARN("stoc", "ignoring unknown socket action: " << item );
+ }
+#endif
+ }
+ while (n >= 0); // all items
+ return mask;
+}
+
+static OUString makeStrings(
+ sal_Int32 mask, char const * const * strings )
+{
+ OUStringBuffer buf( 48 );
+ while (mask)
+ {
+ if (0x80000000 & mask)
+ {
+ buf.appendAscii( *strings );
+ if ((mask << 1) != 0) // more items following
+ buf.append( ',' );
+ }
+ mask = (mask << 1);
+ ++strings;
+ }
+ return buf.makeStringAndClear();
+}
+
+namespace {
+
+class SocketPermission : public Permission
+{
+ static char const * s_actions [];
+ sal_Int32 m_actions;
+
+ OUString m_host;
+ sal_Int32 m_lowerPort;
+ sal_Int32 m_upperPort;
+ mutable OUString m_ip;
+ mutable bool m_resolveErr;
+ mutable bool m_resolvedHost;
+ bool m_wildCardHost;
+
+ inline bool resolveHost() const;
+
+public:
+ SocketPermission(
+ connection::SocketPermission const & perm,
+ ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
+ virtual bool implies( Permission const & perm ) const override;
+ virtual OUString toString() const override;
+};
+
+}
+
+char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", nullptr };
+
+SocketPermission::SocketPermission(
+ connection::SocketPermission const & perm,
+ ::rtl::Reference< Permission > const & next )
+ : Permission( SOCKET, next )
+ , m_actions( makeMask( perm.Actions, s_actions ) )
+ , m_host( perm.Host )
+ , m_lowerPort( 0 )
+ , m_upperPort( 65535 )
+ , m_resolveErr( false )
+ , m_resolvedHost( false )
+ , m_wildCardHost( !perm.Host.isEmpty() && '*' == perm.Host.pData->buffer[ 0 ] )
+{
+ if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied
+ m_actions |= 0x10000000;
+
+ // separate host from portrange
+ sal_Int32 colon = m_host.indexOf( ':' );
+ if (colon < 0) // port [range] not given
+ return;
+
+ sal_Int32 minus = m_host.indexOf( '-', colon +1 );
+ if (minus < 0)
+ {
+ m_lowerPort = m_upperPort = o3tl::toInt32(m_host.subView( colon +1 ));
+ }
+ else if (minus == (colon +1)) // -N
+ {
+ m_upperPort = o3tl::toInt32(m_host.subView( minus +1 ));
+ }
+ else if (minus == (m_host.getLength() -1)) // N-
+ {
+ m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, m_host.getLength() -1 -colon -1 ));
+ }
+ else // A-B
+ {
+ m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, minus - colon -1 ));
+ m_upperPort = o3tl::toInt32(m_host.subView( minus +1 ));
+ }
+ m_host = m_host.copy( 0, colon );
+}
+
+inline bool SocketPermission::resolveHost() const
+{
+ if (m_resolveErr)
+ return false;
+
+ if (! m_resolvedHost)
+ {
+ // dns lookup
+ SocketAddr addr;
+ SocketAddr::resolveHostname( m_host, addr );
+ OUString ip;
+ m_resolveErr = (::osl_Socket_Ok != ::osl_getDottedInetAddrOfSocketAddr(
+ addr.getHandle(), &ip.pData ));
+ if (m_resolveErr)
+ return false;
+
+ MutexGuard guard( Mutex::getGlobalMutex() );
+ if (! m_resolvedHost)
+ {
+ m_ip = ip;
+ m_resolvedHost = true;
+ }
+ }
+ return m_resolvedHost;
+}
+
+bool SocketPermission::implies( Permission const & perm ) const
+{
+ // check type
+ if (SOCKET != perm.m_type)
+ return false;
+ SocketPermission const & demanded = static_cast< SocketPermission const & >( perm );
+
+ // check actions
+ if ((m_actions & demanded.m_actions) != demanded.m_actions)
+ return false;
+
+ // check ports
+ if (demanded.m_lowerPort < m_lowerPort)
+ return false;
+ if (demanded.m_upperPort > m_upperPort)
+ return false;
+
+ // quick check host (DNS names: RFC 1034/1035)
+ if (m_host.equalsIgnoreAsciiCase( demanded.m_host ))
+ return true;
+ // check for host wildcards
+ if (m_wildCardHost)
+ {
+ OUString const & demanded_host = demanded.m_host;
+ if (demanded_host.getLength() <= m_host.getLength())
+ return false;
+ sal_Int32 len = m_host.getLength() -1; // skip star
+ return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
+ demanded_host.getStr() + demanded_host.getLength() - len, len,
+ m_host.pData->buffer + 1, len ));
+ }
+ if (demanded.m_wildCardHost)
+ return false;
+
+ // compare IP addresses
+ if (! resolveHost())
+ return false;
+ if (! demanded.resolveHost())
+ return false;
+ return m_ip == demanded.m_ip;
+}
+
+OUString SocketPermission::toString() const
+{
+ OUStringBuffer buf( 48 );
+ // host
+ buf.append( "com.sun.star.connection.SocketPermission (host=\"" );
+ buf.append( m_host );
+ if (m_resolvedHost)
+ {
+ buf.append( '[' );
+ buf.append( m_ip );
+ buf.append( ']' );
+ }
+ // port
+ if (0 != m_lowerPort || 65535 != m_upperPort)
+ {
+ buf.append( ':' );
+ if (m_lowerPort > 0)
+ buf.append( m_lowerPort );
+ if (m_upperPort > m_lowerPort)
+ {
+ buf.append( '-' );
+ if (m_upperPort < 65535)
+ buf.append( m_upperPort );
+ }
+ }
+ // actions
+ buf.append( "\", actions=\"" );
+ buf.append( makeStrings( m_actions, s_actions ) );
+ buf.append( "\")" );
+ return buf.makeStringAndClear();
+}
+
+namespace {
+
+class FilePermission : public Permission
+{
+ static char const * s_actions [];
+ sal_Int32 m_actions;
+
+ OUString m_url;
+ bool m_allFiles;
+
+public:
+ FilePermission(
+ io::FilePermission const & perm,
+ ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
+ virtual bool implies( Permission const & perm ) const override;
+ virtual OUString toString() const override;
+};
+
+}
+
+char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", nullptr };
+
+static OUString const & getWorkingDir()
+{
+ static OUString s_workingDir = []() {
+ OUString workingDir;
+ ::osl_getProcessWorkingDir(&workingDir.pData);
+ return workingDir;
+ }();
+ return s_workingDir;
+}
+
+FilePermission::FilePermission(
+ io::FilePermission const & perm,
+ ::rtl::Reference< Permission > const & next )
+ : Permission( FILE, next )
+ , m_actions( makeMask( perm.Actions, s_actions ) )
+ , m_url( perm.URL )
+ , m_allFiles( perm.URL == "<<ALL FILES>>" )
+{
+ if ( m_allFiles)
+ return;
+
+ if ( m_url == "*" )
+ {
+ m_url = getWorkingDir() + "/*";
+ }
+ else if ( m_url == "-" )
+ {
+ m_url = getWorkingDir() + "/-";
+ }
+ else if (!m_url.startsWith("file:///"))
+ {
+ // relative path
+ OUString out;
+ oslFileError rc = ::osl_getAbsoluteFileURL(
+ getWorkingDir().pData, perm.URL.pData, &out.pData );
+ m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback
+ }
+#ifdef _WIN32
+ // correct win drive letters
+ if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X|
+ {
+ constexpr OUStringLiteral s_colon = u":";
+ // common case in API is a ':' (sal), so convert '|' to ':'
+ m_url = m_url.replaceAt( 9, 1, s_colon );
+ }
+#endif
+}
+
+bool FilePermission::implies( Permission const & perm ) const
+{
+ // check type
+ if (FILE != perm.m_type)
+ return false;
+ FilePermission const & demanded = static_cast< FilePermission const & >( perm );
+
+ // check actions
+ if ((m_actions & demanded.m_actions) != demanded.m_actions)
+ return false;
+
+ // check url
+ if (m_allFiles)
+ return true;
+ if (demanded.m_allFiles)
+ return false;
+
+#ifdef _WIN32
+ if (m_url.equalsIgnoreAsciiCase( demanded.m_url ))
+ return true;
+#else
+ if (m_url == demanded.m_url )
+ return true;
+#endif
+ if (m_url.getLength() > demanded.m_url.getLength())
+ return false;
+ // check /- wildcard: all files and recursive in that path
+ if (m_url.endsWith("/-"))
+ {
+ // demanded url must start with granted path (including path trailing path sep)
+ sal_Int32 len = m_url.getLength() -1;
+#ifdef _WIN32
+ return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
+ demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
+#else
+ return (0 == ::rtl_ustr_reverseCompare_WithLength(
+ demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
+#endif
+ }
+ // check /* wildcard: all files in that path (not recursive!)
+ if (m_url.endsWith("/*"))
+ {
+ // demanded url must start with granted path (including path trailing path sep)
+ sal_Int32 len = m_url.getLength() -1;
+#ifdef _WIN32
+ return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
+ demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
+ (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
+#else
+ return ((0 == ::rtl_ustr_reverseCompare_WithLength(
+ demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
+ (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
+#endif
+ }
+ return false;
+}
+
+OUString FilePermission::toString() const
+{
+ return
+ // url
+ "com.sun.star.io.FilePermission (url=\"" + m_url
+ // actions
+ + "\", actions=\"" + makeStrings( m_actions, s_actions ) + "\")";
+}
+
+namespace {
+
+class RuntimePermission : public Permission
+{
+ OUString m_name;
+
+public:
+ RuntimePermission(
+ security::RuntimePermission const & perm,
+ ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() )
+ : Permission( RUNTIME, next )
+ , m_name( perm.Name )
+ {}
+ virtual bool implies( Permission const & perm ) const override;
+ virtual OUString toString() const override;
+};
+
+}
+
+bool RuntimePermission::implies( Permission const & perm ) const
+{
+ // check type
+ if (RUNTIME != perm.m_type)
+ return false;
+ RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm );
+
+ // check name
+ return m_name == demanded.m_name;
+}
+
+OUString RuntimePermission::toString() const
+{
+ return "com.sun.star.security.RuntimePermission (name=\"" +
+ m_name + "\")";
+}
+
+
+bool AllPermission::implies( Permission const & ) const
+{
+ return true;
+}
+
+OUString AllPermission::toString() const
+{
+ return "com.sun.star.security.AllPermission";
+}
+
+
+PermissionCollection::PermissionCollection(
+ Sequence< Any > const & permissions, PermissionCollection const & addition )
+ : m_head( addition.m_head )
+{
+ Any const * perms = permissions.getConstArray();
+ for ( sal_Int32 nPos = permissions.getLength(); nPos--; )
+ {
+ Any const & perm = perms[ nPos ];
+ Type const & perm_type = perm.getValueType();
+
+ // supported permission types
+ if (perm_type.equals( cppu::UnoType<io::FilePermission>::get()))
+ {
+ m_head = new FilePermission(
+ *static_cast< io::FilePermission const * >( perm.pData ), m_head );
+ }
+ else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
+ {
+ m_head = new SocketPermission(
+ *static_cast< connection::SocketPermission const * >( perm.pData ), m_head );
+ }
+ else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
+ {
+ m_head = new RuntimePermission(
+ *static_cast< security::RuntimePermission const * >( perm.pData ), m_head );
+ }
+ else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get()))
+ {
+ m_head = new AllPermission( m_head );
+ }
+ else
+ {
+ throw RuntimeException( "checking for unsupported permission type: " + perm_type.getTypeName() );
+ }
+ }
+}
+#ifdef __DIAGNOSE
+
+Sequence< OUString > PermissionCollection::toStrings() const
+{
+ vector< OUString > strings;
+ strings.reserve( 8 );
+ for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() )
+ {
+ strings.push_back( perm->toString() );
+ }
+ return Sequence< OUString >( strings.data(), strings.size() );
+}
+#endif
+
+static bool implies(
+ ::rtl::Reference< Permission > const & head, Permission const & demanded )
+{
+ for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() )
+ {
+ if (perm->implies( demanded ))
+ return true;
+ }
+ return false;
+}
+
+#ifdef __DIAGNOSE
+
+static void demanded_diag(
+ Permission const & perm )
+{
+ OUStringBuffer buf( 48 );
+ buf.append( "demanding " );
+ buf.append( perm.toString() );
+ buf.append( " => ok." );
+ OString str(
+ OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
+ SAL_INFO("stoc",( "%s", str.getStr() );
+}
+#endif
+
+static void throwAccessControlException(
+ Permission const & perm, Any const & demanded_perm )
+{
+ throw security::AccessControlException(
+ "access denied: " + perm.toString(),
+ Reference< XInterface >(), demanded_perm );
+}
+
+void PermissionCollection::checkPermission( Any const & perm ) const
+{
+ Type const & demanded_type = perm.getValueType();
+
+ // supported permission types
+ // stack object of SimpleReferenceObject are ok, as long as they are not
+ // assigned to a ::rtl::Reference<> (=> delete this)
+ if (demanded_type.equals( cppu::UnoType<io::FilePermission>::get()))
+ {
+ FilePermission demanded(
+ *static_cast< io::FilePermission const * >( perm.pData ) );
+ if (implies( m_head, demanded ))
+ {
+#ifdef __DIAGNOSE
+ demanded_diag( demanded );
+#endif
+ return;
+ }
+ throwAccessControlException( demanded, perm );
+ }
+ else if (demanded_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
+ {
+ SocketPermission demanded(
+ *static_cast< connection::SocketPermission const * >( perm.pData ) );
+ if (implies( m_head, demanded ))
+ {
+#ifdef __DIAGNOSE
+ demanded_diag( demanded );
+#endif
+ return;
+ }
+ throwAccessControlException( demanded, perm );
+ }
+ else if (demanded_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
+ {
+ RuntimePermission demanded(
+ *static_cast< security::RuntimePermission const * >( perm.pData ) );
+ if (implies( m_head, demanded ))
+ {
+#ifdef __DIAGNOSE
+ demanded_diag( demanded );
+#endif
+ return;
+ }
+ throwAccessControlException( demanded, perm );
+ }
+ else if (demanded_type.equals( cppu::UnoType<security::AllPermission>::get()))
+ {
+ AllPermission demanded;
+ if (implies( m_head, demanded ))
+ {
+#ifdef __DIAGNOSE
+ demanded_diag( demanded );
+#endif
+ return;
+ }
+ throwAccessControlException( demanded, perm );
+ }
+ else
+ {
+ throw RuntimeException( "checking for unsupported permission type: " + demanded_type.getTypeName() );
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */