Bump version to 21.06.18.1
[LibreOffice.git] / stoc / source / security / permissions.cxx
blob86e523e5a9fecfbcf64f9b199362da5a4a5204ff
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>
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 #include <com/sun/star/uno/Sequence.hxx>
38 #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 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 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 = m_host.copy( colon +1 ).toInt32();
153 else if (minus == (colon +1)) // -N
155 m_upperPort = m_host.copy( minus +1 ).toInt32();
157 else if (minus == (m_host.getLength() -1)) // N-
159 m_lowerPort = m_host.copy( colon +1, m_host.getLength() -1 -colon -1 ).toInt32();
161 else // A-B
163 m_lowerPort = m_host.copy( colon +1, minus - colon -1 ).toInt32();
164 m_upperPort = m_host.copy( minus +1 ).toInt32();
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 buf.append( m_host );
243 if (m_resolvedHost)
245 buf.append( '[' );
246 buf.append( m_ip );
247 buf.append( ']' );
249 // port
250 if (0 != m_lowerPort || 65535 != m_upperPort)
252 buf.append( ':' );
253 if (m_lowerPort > 0)
254 buf.append( m_lowerPort );
255 if (m_upperPort > m_lowerPort)
257 buf.append( '-' );
258 if (m_upperPort < 65535)
259 buf.append( m_upperPort );
262 // actions
263 buf.append( "\", actions=\"" );
264 buf.append( makeStrings( m_actions, s_actions ) );
265 buf.append( "\")" );
266 return buf.makeStringAndClear();
269 namespace {
271 class FilePermission : public Permission
273 static char const * s_actions [];
274 sal_Int32 m_actions;
276 OUString m_url;
277 bool m_allFiles;
279 public:
280 FilePermission(
281 io::FilePermission const & perm,
282 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() );
283 virtual bool implies( Permission const & perm ) const override;
284 virtual OUString toString() const override;
289 char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", nullptr };
291 static OUString const & getWorkingDir()
293 static OUString s_workingDir = []() {
294 OUString workingDir;
295 ::osl_getProcessWorkingDir(&workingDir.pData);
296 return workingDir;
297 }();
298 return s_workingDir;
301 FilePermission::FilePermission(
302 io::FilePermission const & perm,
303 ::rtl::Reference< Permission > const & next )
304 : Permission( FILE, next )
305 , m_actions( makeMask( perm.Actions, s_actions ) )
306 , m_url( perm.URL )
307 , m_allFiles( perm.URL == "<<ALL FILES>>" )
309 if ( m_allFiles)
310 return;
312 if ( m_url == "*" )
314 OUStringBuffer buf( 64 );
315 buf.append( getWorkingDir() );
316 buf.append( "/*" );
317 m_url = buf.makeStringAndClear();
319 else if ( m_url == "-" )
321 OUStringBuffer buf( 64 );
322 buf.append( getWorkingDir() );
323 buf.append( "/-" );
324 m_url = buf.makeStringAndClear();
326 else if (!m_url.startsWith("file:///"))
328 // relative path
329 OUString out;
330 oslFileError rc = ::osl_getAbsoluteFileURL(
331 getWorkingDir().pData, perm.URL.pData, &out.pData );
332 m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback
334 #ifdef _WIN32
335 // correct win drive letters
336 if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X|
338 static OUString s_colon = ":";
339 // common case in API is a ':' (sal), so convert '|' to ':'
340 m_url = m_url.replaceAt( 9, 1, s_colon );
342 #endif
345 bool FilePermission::implies( Permission const & perm ) const
347 // check type
348 if (FILE != perm.m_type)
349 return false;
350 FilePermission const & demanded = static_cast< FilePermission const & >( perm );
352 // check actions
353 if ((m_actions & demanded.m_actions) != demanded.m_actions)
354 return false;
356 // check url
357 if (m_allFiles)
358 return true;
359 if (demanded.m_allFiles)
360 return false;
362 #ifdef _WIN32
363 if (m_url.equalsIgnoreAsciiCase( demanded.m_url ))
364 return true;
365 #else
366 if (m_url == demanded.m_url )
367 return true;
368 #endif
369 if (m_url.getLength() > demanded.m_url.getLength())
370 return false;
371 // check /- wildcard: all files and recursive in that path
372 if (m_url.endsWith("/-"))
374 // demanded url must start with granted path (including path trailing path sep)
375 sal_Int32 len = m_url.getLength() -1;
376 #ifdef _WIN32
377 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
378 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
379 #else
380 return (0 == ::rtl_ustr_reverseCompare_WithLength(
381 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len ));
382 #endif
384 // check /* wildcard: all files in that path (not recursive!)
385 if (m_url.endsWith("/*"))
387 // demanded url must start with granted path (including path trailing path sep)
388 sal_Int32 len = m_url.getLength() -1;
389 #ifdef _WIN32
390 return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
391 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
392 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
393 #else
394 return ((0 == ::rtl_ustr_reverseCompare_WithLength(
395 demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) &&
396 (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths
397 #endif
399 return false;
402 OUString FilePermission::toString() const
404 OUStringBuffer buf( 48 );
405 // url
406 buf.append( "com.sun.star.io.FilePermission (url=\"" );
407 buf.append( m_url );
408 // actions
409 buf.append( "\", actions=\"" );
410 buf.append( makeStrings( m_actions, s_actions ) );
411 buf.append( "\")" );
412 return buf.makeStringAndClear();
415 namespace {
417 class RuntimePermission : public Permission
419 OUString m_name;
421 public:
422 RuntimePermission(
423 security::RuntimePermission const & perm,
424 ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() )
425 : Permission( RUNTIME, next )
426 , m_name( perm.Name )
428 virtual bool implies( Permission const & perm ) const override;
429 virtual OUString toString() const override;
434 bool RuntimePermission::implies( Permission const & perm ) const
436 // check type
437 if (RUNTIME != perm.m_type)
438 return false;
439 RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm );
441 // check name
442 return m_name == demanded.m_name;
445 OUString RuntimePermission::toString() const
447 return "com.sun.star.security.RuntimePermission (name=\"" +
448 m_name + "\")";
452 bool AllPermission::implies( Permission const & ) const
454 return true;
457 OUString AllPermission::toString() const
459 return "com.sun.star.security.AllPermission";
463 PermissionCollection::PermissionCollection(
464 Sequence< Any > const & permissions, PermissionCollection const & addition )
465 : m_head( addition.m_head )
467 Any const * perms = permissions.getConstArray();
468 for ( sal_Int32 nPos = permissions.getLength(); nPos--; )
470 Any const & perm = perms[ nPos ];
471 Type const & perm_type = perm.getValueType();
473 // supported permission types
474 if (perm_type.equals( cppu::UnoType<io::FilePermission>::get()))
476 m_head = new FilePermission(
477 *static_cast< io::FilePermission const * >( perm.pData ), m_head );
479 else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
481 m_head = new SocketPermission(
482 *static_cast< connection::SocketPermission const * >( perm.pData ), m_head );
484 else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
486 m_head = new RuntimePermission(
487 *static_cast< security::RuntimePermission const * >( perm.pData ), m_head );
489 else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get()))
491 m_head = new AllPermission( m_head );
493 else
495 throw RuntimeException( "checking for unsupported permission type: " + perm_type.getTypeName() );
499 #ifdef __DIAGNOSE
501 Sequence< OUString > PermissionCollection::toStrings() const
503 vector< OUString > strings;
504 strings.reserve( 8 );
505 for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() )
507 strings.push_back( perm->toString() );
509 return Sequence< OUString >( strings.data(), strings.size() );
511 #endif
513 static bool implies(
514 ::rtl::Reference< Permission > const & head, Permission const & demanded )
516 for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() )
518 if (perm->implies( demanded ))
519 return true;
521 return false;
524 #ifdef __DIAGNOSE
526 static void demanded_diag(
527 Permission const & perm )
529 OUStringBuffer buf( 48 );
530 buf.append( "demanding " );
531 buf.append( perm.toString() );
532 buf.append( " => ok." );
533 OString str(
534 OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) );
535 SAL_INFO("stoc",( "%s", str.getStr() );
537 #endif
539 static void throwAccessControlException(
540 Permission const & perm, Any const & demanded_perm )
542 throw security::AccessControlException(
543 "access denied: " + perm.toString(),
544 Reference< XInterface >(), demanded_perm );
547 void PermissionCollection::checkPermission( Any const & perm ) const
549 Type const & demanded_type = perm.getValueType();
551 // supported permission types
552 // stack object of SimpleReferenceObject are ok, as long as they are not
553 // assigned to a ::rtl::Reference<> (=> delete this)
554 if (demanded_type.equals( cppu::UnoType<io::FilePermission>::get()))
556 FilePermission demanded(
557 *static_cast< io::FilePermission const * >( perm.pData ) );
558 if (implies( m_head, demanded ))
560 #ifdef __DIAGNOSE
561 demanded_diag( demanded );
562 #endif
563 return;
565 throwAccessControlException( demanded, perm );
567 else if (demanded_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
569 SocketPermission demanded(
570 *static_cast< connection::SocketPermission const * >( perm.pData ) );
571 if (implies( m_head, demanded ))
573 #ifdef __DIAGNOSE
574 demanded_diag( demanded );
575 #endif
576 return;
578 throwAccessControlException( demanded, perm );
580 else if (demanded_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
582 RuntimePermission demanded(
583 *static_cast< security::RuntimePermission const * >( perm.pData ) );
584 if (implies( m_head, demanded ))
586 #ifdef __DIAGNOSE
587 demanded_diag( demanded );
588 #endif
589 return;
591 throwAccessControlException( demanded, perm );
593 else if (demanded_type.equals( cppu::UnoType<security::AllPermission>::get()))
595 AllPermission demanded;
596 if (implies( m_head, demanded ))
598 #ifdef __DIAGNOSE
599 demanded_diag( demanded );
600 #endif
601 return;
603 throwAccessControlException( demanded, perm );
605 else
607 throw RuntimeException( "checking for unsupported permission type: " + demanded_type.getTypeName() );
613 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */