1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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
;
49 static sal_Int32
makeMask(
50 OUString
const & items
, char const * const * strings
)
57 OUString
item( o3tl::trim(o3tl::getToken(items
, 0, ',', n
)) );
61 while (strings
[ nPos
])
63 if (item
.equalsAscii( strings
[ nPos
] ))
65 mask
|= (0x80000000 >> nPos
);
70 #if OSL_DEBUG_LEVEL > 0
71 if (! strings
[ nPos
])
73 SAL_WARN("stoc", "ignoring unknown socket action: " << item
);
77 while (n
>= 0); // all items
81 static OUString
makeStrings(
82 sal_Int32 mask
, char const * const * strings
)
84 OUStringBuffer
buf( 48 );
87 if (0x80000000 & mask
)
89 buf
.appendAscii( *strings
);
90 if ((mask
<< 1) != 0) // more items following
96 return buf
.makeStringAndClear();
101 class SocketPermission
: public Permission
103 static char const * s_actions
[];
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
;
114 inline bool resolveHost() const;
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
)
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
148 sal_Int32 minus
= m_host
.indexOf( '-', colon
+1 );
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 ));
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
174 if (! m_resolvedHost
)
178 SocketAddr::resolveHostname( m_host
, addr
);
180 m_resolveErr
= (::osl_Socket_Ok
!= ::osl_getDottedInetAddrOfSocketAddr(
181 addr
.getHandle(), &ip
.pData
));
185 MutexGuard
guard( Mutex::getGlobalMutex() );
186 if (! m_resolvedHost
)
189 m_resolvedHost
= true;
192 return m_resolvedHost
;
195 bool SocketPermission::implies( Permission
const & perm
) const
198 if (SOCKET
!= perm
.m_type
)
200 SocketPermission
const & demanded
= static_cast< SocketPermission
const & >( perm
);
203 if ((m_actions
& demanded
.m_actions
) != demanded
.m_actions
)
207 if (demanded
.m_lowerPort
< m_lowerPort
)
209 if (demanded
.m_upperPort
> m_upperPort
)
212 // quick check host (DNS names: RFC 1034/1035)
213 if (m_host
.equalsIgnoreAsciiCase( demanded
.m_host
))
215 // check for host wildcards
218 OUString
const & demanded_host
= demanded
.m_host
;
219 if (demanded_host
.getLength() <= m_host
.getLength())
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
)
229 // compare IP addresses
232 if (! demanded
.resolveHost())
234 return m_ip
== demanded
.m_ip
;
237 OUString
SocketPermission::toString() const
239 OUStringBuffer
buf( 48 );
241 buf
.append( "com.sun.star.connection.SocketPermission (host=\""
245 buf
.append( "[" + m_ip
+ "]" );
248 if (0 != m_lowerPort
|| 65535 != m_upperPort
)
252 buf
.append( m_lowerPort
);
253 if (m_upperPort
> m_lowerPort
)
256 if (m_upperPort
< 65535)
257 buf
.append( m_upperPort
);
261 buf
.append( "\", actions=\""
262 + makeStrings( m_actions
, s_actions
)
264 return buf
.makeStringAndClear();
269 class FilePermission
: public Permission
271 static char const * s_actions
[];
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
= []() {
293 ::osl_getProcessWorkingDir(&workingDir
.pData
);
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
) )
305 , m_allFiles( perm
.URL
== "<<ALL FILES>>" )
312 m_url
= getWorkingDir() + "/*";
314 else if ( m_url
== "-" )
316 m_url
= getWorkingDir() + "/-";
318 else if (!m_url
.startsWith("file:///"))
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
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
);
337 bool FilePermission::implies( Permission
const & perm
) const
340 if (FILE != perm
.m_type
)
342 FilePermission
const & demanded
= static_cast< FilePermission
const & >( perm
);
345 if ((m_actions
& demanded
.m_actions
) != demanded
.m_actions
)
351 if (demanded
.m_allFiles
)
355 if (m_url
.equalsIgnoreAsciiCase( demanded
.m_url
))
358 if (m_url
== demanded
.m_url
)
361 if (m_url
.getLength() > demanded
.m_url
.getLength())
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;
369 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
370 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
));
372 return (0 == ::rtl_ustr_reverseCompare_WithLength(
373 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
));
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;
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
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
394 OUString
FilePermission::toString() const
398 "com.sun.star.io.FilePermission (url=\"" + m_url
400 + "\", actions=\"" + makeStrings( m_actions
, s_actions
) + "\")";
405 class RuntimePermission
: public Permission
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
425 if (RUNTIME
!= perm
.m_type
)
427 RuntimePermission
const & demanded
= static_cast< RuntimePermission
const & >( perm
);
430 return m_name
== demanded
.m_name
;
433 OUString
RuntimePermission::toString() const
435 return "com.sun.star.security.RuntimePermission (name=\"" +
440 bool AllPermission::implies( Permission
const & ) const
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
);
483 throw RuntimeException( "checking for unsupported permission type: " + perm_type
.getTypeName() );
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() );
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
))
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." );
522 OUStringToOString( buf
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
) );
523 SAL_INFO("stoc",( "%s", str
.getStr() );
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
))
549 demanded_diag( demanded
);
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
))
562 demanded_diag( demanded
);
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
))
575 demanded_diag( demanded
);
579 throwAccessControlException( demanded
, perm
);
581 else if (demanded_type
.equals( cppu::UnoType
<security::AllPermission
>::get()))
583 AllPermission demanded
;
584 if (implies( m_head
, demanded
))
587 demanded_diag( demanded
);
591 throwAccessControlException( demanded
, perm
);
595 throw RuntimeException( "checking for unsupported permission type: " + demanded_type
.getTypeName() );
601 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */