Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / stoc / source / security / permissions.cxx
blob48a1f907f01ca43fc8c910a8df49c584c3fa791b
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 <sal/log.hxx>
30 #include <o3tl/string_view.hxx>
32 #include <com/sun/star/security/RuntimePermission.hpp>
33 #include <com/sun/star/security/AllPermission.hpp>
34 #include <com/sun/star/io/FilePermission.hpp>
35 #include <com/sun/star/connection/SocketPermission.hpp>
36 #include <com/sun/star/security/AccessControlException.hpp>
37 #include <com/sun/star/uno/Sequence.hxx>
39 #include "permissions.h"
41 using namespace ::osl;
42 using namespace ::com::sun::star;
43 using namespace css::uno;
45 namespace stoc_sec
49 static sal_Int32 makeMask(
50 OUString const & items, char const * const * strings )
52 sal_Int32 mask = 0;
54 sal_Int32 n = 0;
57 OUString item( o3tl::trim(o3tl::getToken(items, 0, ',', n )) );
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 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();
99 namespace {
101 class SocketPermission : public Permission
103 static char const * s_actions [];
104 sal_Int32 m_actions;
106 OUString m_host;
107 sal_Int32 m_lowerPort;
108 sal_Int32 m_upperPort;
109 mutable OUString m_ip;
110 mutable bool m_resolveErr;
111 mutable bool m_resolvedHost;
112 bool m_wildCardHost;
114 inline bool resolveHost() const;
116 public:
117 SocketPermission(
118 connection::SocketPermission const & perm,
119 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
120 virtual bool implies( Permission const & perm ) const override;
121 virtual OUString toString() const override;
126 char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", nullptr };
128 SocketPermission::SocketPermission(
129 connection::SocketPermission const & perm,
130 ::rtl::Reference< Permission > const & next )
131 : Permission( SOCKET, next )
132 , m_actions( makeMask( perm.Actions, s_actions ) )
133 , m_host( perm.Host )
134 , m_lowerPort( 0 )
135 , m_upperPort( 65535 )
136 , m_resolveErr( false )
137 , m_resolvedHost( false )
138 , m_wildCardHost( !perm.Host.isEmpty() && '*' == perm.Host.pData->buffer[ 0 ] )
140 if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied
141 m_actions |= 0x10000000;
143 // separate host from portrange
144 sal_Int32 colon = m_host.indexOf( ':' );
145 if (colon < 0) // port [range] not given
146 return;
148 sal_Int32 minus = m_host.indexOf( '-', colon +1 );
149 if (minus < 0)
151 m_lowerPort = m_upperPort = o3tl::toInt32(m_host.subView( colon +1 ));
153 else if (minus == (colon +1)) // -N
155 m_upperPort = o3tl::toInt32(m_host.subView( minus +1 ));
157 else if (minus == (m_host.getLength() -1)) // N-
159 m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, m_host.getLength() -1 -colon -1 ));
161 else // A-B
163 m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, minus - colon -1 ));
164 m_upperPort = o3tl::toInt32(m_host.subView( minus +1 ));
166 m_host = m_host.copy( 0, colon );
169 inline bool SocketPermission::resolveHost() const
171 if (m_resolveErr)
172 return false;
174 if (! m_resolvedHost)
176 // dns lookup
177 SocketAddr addr;
178 SocketAddr::resolveHostname( m_host, addr );
179 OUString ip;
180 m_resolveErr = (::osl_Socket_Ok != ::osl_getDottedInetAddrOfSocketAddr(
181 addr.getHandle(), &ip.pData ));
182 if (m_resolveErr)
183 return false;
185 MutexGuard guard( Mutex::getGlobalMutex() );
186 if (! m_resolvedHost)
188 m_ip = ip;
189 m_resolvedHost = true;
192 return m_resolvedHost;
195 bool SocketPermission::implies( Permission const & perm ) const
197 // check type
198 if (SOCKET != perm.m_type)
199 return false;
200 SocketPermission const & demanded = static_cast< SocketPermission const & >( perm );
202 // check actions
203 if ((m_actions & demanded.m_actions) != demanded.m_actions)
204 return false;
206 // check ports
207 if (demanded.m_lowerPort < m_lowerPort)
208 return false;
209 if (demanded.m_upperPort > m_upperPort)
210 return false;
212 // quick check host (DNS names: RFC 1034/1035)
213 if (m_host.equalsIgnoreAsciiCase( demanded.m_host ))
214 return true;
215 // check for host wildcards
216 if (m_wildCardHost)
218 OUString const & demanded_host = demanded.m_host;
219 if (demanded_host.getLength() <= m_host.getLength())
220 return false;
221 sal_Int32 len = m_host.getLength() -1; // skip star
222 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
223 demanded_host.getStr() + demanded_host.getLength() - len, len,
224 m_host.pData->buffer + 1, len ));
226 if (demanded.m_wildCardHost)
227 return false;
229 // compare IP addresses
230 if (! resolveHost())
231 return false;
232 if (! demanded.resolveHost())
233 return false;
234 return m_ip == demanded.m_ip;
237 OUString SocketPermission::toString() const
239 OUStringBuffer buf( 48 );
240 // host
241 buf.append( "com.sun.star.connection.SocketPermission (host=\""
242 + m_host );
243 if (m_resolvedHost)
245 buf.append( "[" + m_ip + "]" );
247 // port
248 if (0 != m_lowerPort || 65535 != m_upperPort)
250 buf.append( ':' );
251 if (m_lowerPort > 0)
252 buf.append( m_lowerPort );
253 if (m_upperPort > m_lowerPort)
255 buf.append( '-' );
256 if (m_upperPort < 65535)
257 buf.append( m_upperPort );
260 // actions
261 buf.append( "\", actions=\""
262 + makeStrings( m_actions, s_actions )
263 + "\")" );
264 return buf.makeStringAndClear();
267 namespace {
269 class FilePermission : public Permission
271 static char const * s_actions [];
272 sal_Int32 m_actions;
274 OUString m_url;
275 bool m_allFiles;
277 public:
278 FilePermission(
279 io::FilePermission const & perm,
280 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
281 virtual bool implies( Permission const & perm ) const override;
282 virtual OUString toString() const override;
287 char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", nullptr };
289 static OUString const & getWorkingDir()
291 static OUString s_workingDir = []() {
292 OUString workingDir;
293 ::osl_getProcessWorkingDir(&workingDir.pData);
294 return workingDir;
295 }();
296 return s_workingDir;
299 FilePermission::FilePermission(
300 io::FilePermission const & perm,
301 ::rtl::Reference< Permission > const & next )
302 : Permission( FILE, next )
303 , m_actions( makeMask( perm.Actions, s_actions ) )
304 , m_url( perm.URL )
305 , m_allFiles( perm.URL == "<<ALL FILES>>" )
307 if ( m_allFiles)
308 return;
310 if ( m_url == "*" )
312 m_url = getWorkingDir() + "/*";
314 else if ( m_url == "-" )
316 m_url = getWorkingDir() + "/-";
318 else if (!m_url.startsWith("file:///"))
320 // relative path
321 OUString out;
322 oslFileError rc = ::osl_getAbsoluteFileURL(
323 getWorkingDir().pData, perm.URL.pData, &out.pData );
324 m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback
326 #ifdef _WIN32
327 // correct win drive letters
328 if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X|
330 constexpr OUStringLiteral s_colon = u":";
331 // common case in API is a ':' (sal), so convert '|' to ':'
332 m_url = m_url.replaceAt( 9, 1, s_colon );
334 #endif
337 bool FilePermission::implies( Permission const & perm ) const
339 // check type
340 if (FILE != perm.m_type)
341 return false;
342 FilePermission const & demanded = static_cast< FilePermission const & >( perm );
344 // check actions
345 if ((m_actions & demanded.m_actions) != demanded.m_actions)
346 return false;
348 // check url
349 if (m_allFiles)
350 return true;
351 if (demanded.m_allFiles)
352 return false;
354 #ifdef _WIN32
355 if (m_url.equalsIgnoreAsciiCase( demanded.m_url ))
356 return true;
357 #else
358 if (m_url == demanded.m_url )
359 return true;
360 #endif
361 if (m_url.getLength() > demanded.m_url.getLength())
362 return false;
363 // check /- wildcard: all files and recursive in that path
364 if (m_url.endsWith("/-"))
366 // demanded url must start with granted path (including path trailing path sep)
367 sal_Int32 len = m_url.getLength() -1;
368 #ifdef _WIN32
369 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
370 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
371 #else
372 return (0 == ::rtl_ustr_reverseCompare_WithLength(
373 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
374 #endif
376 // check /* wildcard: all files in that path (not recursive!)
377 if (m_url.endsWith("/*"))
379 // demanded url must start with granted path (including path trailing path sep)
380 sal_Int32 len = m_url.getLength() -1;
381 #ifdef _WIN32
382 return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
383 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
384 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
385 #else
386 return ((0 == ::rtl_ustr_reverseCompare_WithLength(
387 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
388 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
389 #endif
391 return false;
394 OUString FilePermission::toString() const
396 return
397 // url
398 "com.sun.star.io.FilePermission (url=\"" + m_url
399 // actions
400 + "\", actions=\"" + makeStrings( m_actions, s_actions ) + "\")";
403 namespace {
405 class RuntimePermission : public Permission
407 OUString m_name;
409 public:
410 RuntimePermission(
411 security::RuntimePermission const & perm,
412 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() )
413 : Permission( RUNTIME, next )
414 , m_name( perm.Name )
416 virtual bool implies( Permission const & perm ) const override;
417 virtual OUString toString() const override;
422 bool RuntimePermission::implies( Permission const & perm ) const
424 // check type
425 if (RUNTIME != perm.m_type)
426 return false;
427 RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm );
429 // check name
430 return m_name == demanded.m_name;
433 OUString RuntimePermission::toString() const
435 return "com.sun.star.security.RuntimePermission (name=\"" +
436 m_name + "\")";
440 bool AllPermission::implies( Permission const & ) const
442 return true;
445 OUString AllPermission::toString() const
447 return "com.sun.star.security.AllPermission";
451 PermissionCollection::PermissionCollection(
452 Sequence< Any > const & permissions, PermissionCollection const & addition )
453 : m_head( addition.m_head )
455 Any const * perms = permissions.getConstArray();
456 for ( sal_Int32 nPos = permissions.getLength(); nPos--; )
458 Any const & perm = perms[ nPos ];
459 Type const & perm_type = perm.getValueType();
461 // supported permission types
462 if (perm_type.equals( cppu::UnoType<io::FilePermission>::get()))
464 m_head = new FilePermission(
465 *static_cast< io::FilePermission const * >( perm.pData ), m_head );
467 else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
469 m_head = new SocketPermission(
470 *static_cast< connection::SocketPermission const * >( perm.pData ), m_head );
472 else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
474 m_head = new RuntimePermission(
475 *static_cast< security::RuntimePermission const * >( perm.pData ), m_head );
477 else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get()))
479 m_head = new AllPermission( m_head );
481 else
483 throw RuntimeException( "checking for unsupported permission type: " + perm_type.getTypeName() );
487 #ifdef __DIAGNOSE
489 Sequence< OUString > PermissionCollection::toStrings() const
491 std::vector< OUString > strings;
492 strings.reserve( 8 );
493 for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() )
495 strings.push_back( perm->toString() );
497 return Sequence< OUString >( strings.data(), strings.size() );
499 #endif
501 static bool implies(
502 ::rtl::Reference< Permission > const & head, Permission const & demanded )
504 for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() )
506 if (perm->implies( demanded ))
507 return true;
509 return false;
512 #ifdef __DIAGNOSE
514 static void demanded_diag(
515 Permission const & perm )
517 OUStringBuffer buf( 48 );
518 buf.append( "demanding " );
519 buf.append( perm.toString() );
520 buf.append( " => ok." );
521 OString str(
522 OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
523 SAL_INFO("stoc",( "%s", str.getStr() );
525 #endif
527 static void throwAccessControlException(
528 Permission const & perm, Any const & demanded_perm )
530 throw security::AccessControlException(
531 "access denied: " + perm.toString(),
532 Reference< XInterface >(), demanded_perm );
535 void PermissionCollection::checkPermission( Any const & perm ) const
537 Type const & demanded_type = perm.getValueType();
539 // supported permission types
540 // stack object of SimpleReferenceObject are ok, as long as they are not
541 // assigned to a ::rtl::Reference<> (=> delete this)
542 if (demanded_type.equals( cppu::UnoType<io::FilePermission>::get()))
544 FilePermission demanded(
545 *static_cast< io::FilePermission const * >( perm.pData ) );
546 if (implies( m_head, demanded ))
548 #ifdef __DIAGNOSE
549 demanded_diag( demanded );
550 #endif
551 return;
553 throwAccessControlException( demanded, perm );
555 else if (demanded_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
557 SocketPermission demanded(
558 *static_cast< connection::SocketPermission const * >( perm.pData ) );
559 if (implies( m_head, demanded ))
561 #ifdef __DIAGNOSE
562 demanded_diag( demanded );
563 #endif
564 return;
566 throwAccessControlException( demanded, perm );
568 else if (demanded_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
570 RuntimePermission demanded(
571 *static_cast< security::RuntimePermission const * >( perm.pData ) );
572 if (implies( m_head, demanded ))
574 #ifdef __DIAGNOSE
575 demanded_diag( demanded );
576 #endif
577 return;
579 throwAccessControlException( demanded, perm );
581 else if (demanded_type.equals( cppu::UnoType<security::AllPermission>::get()))
583 AllPermission demanded;
584 if (implies( m_head, demanded ))
586 #ifdef __DIAGNOSE
587 demanded_diag( demanded );
588 #endif
589 return;
591 throwAccessControlException( demanded, perm );
593 else
595 throw RuntimeException( "checking for unsupported permission type: " + demanded_type.getTypeName() );
601 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */