LibreOffice Module stoc (master) 1
permissions.cxx
Go to the documentation of this file.
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 <sal/log.hxx>
30#include <o3tl/string_view.hxx>
31
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>
38
39#include "permissions.h"
40
41using namespace ::osl;
42using namespace ::com::sun::star;
43using namespace css::uno;
44
45namespace stoc_sec
46{
47
48
49static sal_Int32 makeMask(
50 OUString const & items, char const * const * strings )
51{
52 sal_Int32 mask = 0;
53
54 sal_Int32 n = 0;
55 do
56 {
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 ])
62 {
63 if (item.equalsAscii( strings[ nPos ] ))
64 {
65 mask |= (0x80000000 >> nPos);
66 break;
67 }
68 ++nPos;
69 }
70#if OSL_DEBUG_LEVEL > 0
71 if (! strings[ nPos ])
72 {
73 SAL_WARN("stoc", "ignoring unknown socket action: " << item );
74 }
75#endif
76 }
77 while (n >= 0); // all items
78 return mask;
79}
80
81static OUString makeStrings(
82 sal_Int32 mask, char const * const * strings )
83{
84 OUStringBuffer buf( 48 );
85 while (mask)
86 {
87 if (0x80000000 & mask)
88 {
89 buf.appendAscii( *strings );
90 if ((mask << 1) != 0) // more items following
91 buf.append( ',' );
92 }
93 mask = (mask << 1);
94 ++strings;
95 }
96 return buf.makeStringAndClear();
97}
98
99namespace {
100
101class SocketPermission : public Permission
102{
103 static char const * s_actions [];
104 sal_Int32 m_actions;
105
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;
113
114 inline bool resolveHost() const;
115
116public:
117 SocketPermission(
118 connection::SocketPermission const & perm,
120 virtual bool implies( Permission const & perm ) const override;
121 virtual OUString toString() const override;
122};
123
124}
125
126char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", nullptr };
127
128SocketPermission::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 ] )
139{
140 if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied
141 m_actions |= 0x10000000;
142
143 // separate host from portrange
144 sal_Int32 colon = m_host.indexOf( ':' );
145 if (colon < 0) // port [range] not given
146 return;
147
148 sal_Int32 minus = m_host.indexOf( '-', colon +1 );
149 if (minus < 0)
150 {
151 m_lowerPort = m_upperPort = o3tl::toInt32(m_host.subView( colon +1 ));
152 }
153 else if (minus == (colon +1)) // -N
154 {
155 m_upperPort = o3tl::toInt32(m_host.subView( minus +1 ));
156 }
157 else if (minus == (m_host.getLength() -1)) // N-
158 {
159 m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, m_host.getLength() -1 -colon -1 ));
160 }
161 else // A-B
162 {
163 m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, minus - colon -1 ));
164 m_upperPort = o3tl::toInt32(m_host.subView( minus +1 ));
165 }
166 m_host = m_host.copy( 0, colon );
167}
168
169inline bool SocketPermission::resolveHost() const
170{
171 if (m_resolveErr)
172 return false;
173
174 if (! m_resolvedHost)
175 {
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;
184
185 MutexGuard guard( Mutex::getGlobalMutex() );
186 if (! m_resolvedHost)
187 {
188 m_ip = ip;
189 m_resolvedHost = true;
190 }
191 }
192 return m_resolvedHost;
193}
194
195bool SocketPermission::implies( Permission const & perm ) const
196{
197 // check type
198 if (SOCKET != perm.m_type)
199 return false;
200 SocketPermission const & demanded = static_cast< SocketPermission const & >( perm );
201
202 // check actions
203 if ((m_actions & demanded.m_actions) != demanded.m_actions)
204 return false;
205
206 // check ports
207 if (demanded.m_lowerPort < m_lowerPort)
208 return false;
209 if (demanded.m_upperPort > m_upperPort)
210 return false;
211
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)
217 {
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 ));
225 }
226 if (demanded.m_wildCardHost)
227 return false;
228
229 // compare IP addresses
230 if (! resolveHost())
231 return false;
232 if (! demanded.resolveHost())
233 return false;
234 return m_ip == demanded.m_ip;
235}
236
237OUString SocketPermission::toString() const
238{
239 OUStringBuffer buf( 48 );
240 // host
241 buf.append( "com.sun.star.connection.SocketPermission (host=\""
242 + m_host );
243 if (m_resolvedHost)
244 {
245 buf.append( "[" + m_ip + "]" );
246 }
247 // port
248 if (0 != m_lowerPort || 65535 != m_upperPort)
249 {
250 buf.append( ':' );
251 if (m_lowerPort > 0)
252 buf.append( m_lowerPort );
254 {
255 buf.append( '-' );
256 if (m_upperPort < 65535)
257 buf.append( m_upperPort );
258 }
259 }
260 // actions
261 buf.append( "\", actions=\""
263 + "\")" );
264 return buf.makeStringAndClear();
265}
266
267namespace {
268
269class FilePermission : public Permission
270{
271 static char const * s_actions [];
272 sal_Int32 m_actions;
273
274 OUString m_url;
276
277public:
278 FilePermission(
279 io::FilePermission const & perm,
281 virtual bool implies( Permission const & perm ) const override;
282 virtual OUString toString() const override;
283};
284
285}
286
287char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", nullptr };
288
289static OUString const & getWorkingDir()
290{
291 static OUString s_workingDir = []() {
292 OUString workingDir;
293 ::osl_getProcessWorkingDir(&workingDir.pData);
294 return workingDir;
295 }();
296 return s_workingDir;
297}
298
299FilePermission::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>>" )
306{
307 if ( m_allFiles)
308 return;
309
310 if ( m_url == "*" )
311 {
312 m_url = getWorkingDir() + "/*";
313 }
314 else if ( m_url == "-" )
315 {
316 m_url = getWorkingDir() + "/-";
317 }
318 else if (!m_url.startsWith("file:///"))
319 {
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
325 }
326#ifdef _WIN32
327 // correct win drive letters
328 if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X|
329 {
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 );
333 }
334#endif
335}
336
337bool FilePermission::implies( Permission const & perm ) const
338{
339 // check type
340 if (FILE != perm.m_type)
341 return false;
342 FilePermission const & demanded = static_cast< FilePermission const & >( perm );
343
344 // check actions
345 if ((m_actions & demanded.m_actions) != demanded.m_actions)
346 return false;
347
348 // check url
349 if (m_allFiles)
350 return true;
351 if (demanded.m_allFiles)
352 return false;
353
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("/-"))
365 {
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
375 }
376 // check /* wildcard: all files in that path (not recursive!)
377 if (m_url.endsWith("/*"))
378 {
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
390 }
391 return false;
392}
393
394OUString FilePermission::toString() const
395{
396 return
397 // url
398 "com.sun.star.io.FilePermission (url=\"" + m_url
399 // actions
400 + "\", actions=\"" + makeStrings( m_actions, s_actions ) + "\")";
401}
402
403namespace {
404
405class RuntimePermission : public Permission
406{
407 OUString m_name;
408
409public:
410 RuntimePermission(
411 security::RuntimePermission const & perm,
413 : Permission( RUNTIME, next )
414 , m_name( perm.Name )
415 {}
416 virtual bool implies( Permission const & perm ) const override;
417 virtual OUString toString() const override;
418};
419
420}
421
422bool RuntimePermission::implies( Permission const & perm ) const
423{
424 // check type
425 if (RUNTIME != perm.m_type)
426 return false;
427 RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm );
428
429 // check name
430 return m_name == demanded.m_name;
431}
432
433OUString RuntimePermission::toString() const
434{
435 return "com.sun.star.security.RuntimePermission (name=\"" +
436 m_name + "\")";
437}
438
439
441{
442 return true;
443}
444
445OUString AllPermission::toString() const
446{
447 return "com.sun.star.security.AllPermission";
448}
449
450
451PermissionCollection::PermissionCollection(
452 Sequence< Any > const & permissions, PermissionCollection const & addition )
453 : m_head( addition.m_head )
454{
455 Any const * perms = permissions.getConstArray();
456 for ( sal_Int32 nPos = permissions.getLength(); nPos--; )
457 {
458 Any const & perm = perms[ nPos ];
459 Type const & perm_type = perm.getValueType();
460
461 // supported permission types
462 if (perm_type.equals( cppu::UnoType<io::FilePermission>::get()))
463 {
464 m_head = new FilePermission(
465 *static_cast< io::FilePermission const * >( perm.pData ), m_head );
466 }
467 else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
468 {
469 m_head = new SocketPermission(
470 *static_cast< connection::SocketPermission const * >( perm.pData ), m_head );
471 }
472 else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
473 {
474 m_head = new RuntimePermission(
475 *static_cast< security::RuntimePermission const * >( perm.pData ), m_head );
476 }
477 else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get()))
478 {
479 m_head = new AllPermission( m_head );
480 }
481 else
482 {
483 throw RuntimeException( "checking for unsupported permission type: " + perm_type.getTypeName() );
484 }
485 }
486}
487#ifdef __DIAGNOSE
488
489Sequence< OUString > PermissionCollection::toStrings() const
490{
491 std::vector< OUString > strings;
492 strings.reserve( 8 );
493 for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() )
494 {
495 strings.push_back( perm->toString() );
496 }
497 return Sequence< OUString >( strings.data(), strings.size() );
498}
499#endif
500
501static bool implies(
502 ::rtl::Reference< Permission > const & head, Permission const & demanded )
503{
504 for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() )
505 {
506 if (perm->implies( demanded ))
507 return true;
508 }
509 return false;
510}
511
512#ifdef __DIAGNOSE
513
514static void demanded_diag(
515 Permission const & perm )
516{
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() );
524}
525#endif
526
528 Permission const & perm, Any const & demanded_perm )
529{
530 throw security::AccessControlException(
531 "access denied: " + perm.toString(),
532 Reference< XInterface >(), demanded_perm );
533}
534
535void PermissionCollection::checkPermission( Any const & perm ) const
536{
537 Type const & demanded_type = perm.getValueType();
538
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()))
543 {
544 FilePermission demanded(
545 *static_cast< io::FilePermission const * >( perm.pData ) );
546 if (implies( m_head, demanded ))
547 {
548#ifdef __DIAGNOSE
549 demanded_diag( demanded );
550#endif
551 return;
552 }
553 throwAccessControlException( demanded, perm );
554 }
555 else if (demanded_type.equals( cppu::UnoType<connection::SocketPermission>::get()))
556 {
557 SocketPermission demanded(
558 *static_cast< connection::SocketPermission const * >( perm.pData ) );
559 if (implies( m_head, demanded ))
560 {
561#ifdef __DIAGNOSE
562 demanded_diag( demanded );
563#endif
564 return;
565 }
566 throwAccessControlException( demanded, perm );
567 }
568 else if (demanded_type.equals( cppu::UnoType<security::RuntimePermission>::get()))
569 {
570 RuntimePermission demanded(
571 *static_cast< security::RuntimePermission const * >( perm.pData ) );
572 if (implies( m_head, demanded ))
573 {
574#ifdef __DIAGNOSE
575 demanded_diag( demanded );
576#endif
577 return;
578 }
579 throwAccessControlException( demanded, perm );
580 }
581 else if (demanded_type.equals( cppu::UnoType<security::AllPermission>::get()))
582 {
583 AllPermission demanded;
584 if (implies( m_head, demanded ))
585 {
586#ifdef __DIAGNOSE
587 demanded_diag( demanded );
588#endif
589 return;
590 }
591 throwAccessControlException( demanded, perm );
592 }
593 else
594 {
595 throw RuntimeException( "checking for unsupported permission type: " + demanded_type.getTypeName() );
596 }
597}
598
599}
600
601/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
AllPermission(::rtl::Reference< Permission > const &next=::rtl::Reference< Permission >())
Definition: permissions.h:54
void checkPermission(css::uno::Any const &perm) const
::rtl::Reference< Permission > m_head
Definition: permissions.h:66
virtual OUString toString() const =0
float u
sal_Int64 n
sal_uInt16 nPos
#define SAL_WARN(area, stream)
#define SAL_INFO(area, stream)
std::unique_ptr< sal_Int32[]> pData
Type
std::basic_string_view< charT, traits > trim(std::basic_string_view< charT, traits > str)
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
static OUString const & getWorkingDir()
static OUString makeStrings(sal_Int32 mask, char const *const *strings)
Definition: permissions.cxx:81
static sal_Int32 makeMask(OUString const &items, char const *const *strings)
Definition: permissions.cxx:49
static bool implies(::rtl::Reference< Permission > const &head, Permission const &demanded)
static void throwAccessControlException(Permission const &perm, Any const &demanded_perm)
OUString toString(OptionInfo const *info)
sal_Int32 m_actions
bool m_wildCardHost
bool m_resolvedHost
bool m_allFiles
OUString m_url
sal_Int32 m_upperPort
static char const * s_actions[]
OUString m_name
bool m_resolveErr
sal_Int32 m_lowerPort
OUString m_ip
OUString m_host
OUString Name