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>
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
;
49 static sal_Int32
makeMask(
50 OUString
const & items
, char const * const * strings
)
57 OUString
item( items
.getToken( 0, ',', n
).trim() );
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
= 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();
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
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=\"" );
242 buf
.append( m_host
);
250 if (0 != m_lowerPort
|| 65535 != m_upperPort
)
254 buf
.append( m_lowerPort
);
255 if (m_upperPort
> m_lowerPort
)
258 if (m_upperPort
< 65535)
259 buf
.append( m_upperPort
);
263 buf
.append( "\", actions=\"" );
264 buf
.append( makeStrings( m_actions
, s_actions
) );
266 return buf
.makeStringAndClear();
271 class FilePermission
: public Permission
273 static char const * s_actions
[];
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
= []() {
295 ::osl_getProcessWorkingDir(&workingDir
.pData
);
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
) )
307 , m_allFiles( perm
.URL
== "<<ALL FILES>>" )
314 OUStringBuffer
buf( 64 );
315 buf
.append( getWorkingDir() );
317 m_url
= buf
.makeStringAndClear();
319 else if ( m_url
== "-" )
321 OUStringBuffer
buf( 64 );
322 buf
.append( getWorkingDir() );
324 m_url
= buf
.makeStringAndClear();
326 else if (!m_url
.startsWith("file:///"))
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
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
);
345 bool FilePermission::implies( Permission
const & perm
) const
348 if (FILE != perm
.m_type
)
350 FilePermission
const & demanded
= static_cast< FilePermission
const & >( perm
);
353 if ((m_actions
& demanded
.m_actions
) != demanded
.m_actions
)
359 if (demanded
.m_allFiles
)
363 if (m_url
.equalsIgnoreAsciiCase( demanded
.m_url
))
366 if (m_url
== demanded
.m_url
)
369 if (m_url
.getLength() > demanded
.m_url
.getLength())
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;
377 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
378 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
));
380 return (0 == ::rtl_ustr_reverseCompare_WithLength(
381 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
));
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;
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
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
402 OUString
FilePermission::toString() const
404 OUStringBuffer
buf( 48 );
406 buf
.append( "com.sun.star.io.FilePermission (url=\"" );
409 buf
.append( "\", actions=\"" );
410 buf
.append( makeStrings( m_actions
, s_actions
) );
412 return buf
.makeStringAndClear();
417 class RuntimePermission
: public Permission
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
437 if (RUNTIME
!= perm
.m_type
)
439 RuntimePermission
const & demanded
= static_cast< RuntimePermission
const & >( perm
);
442 return m_name
== demanded
.m_name
;
445 OUString
RuntimePermission::toString() const
447 return "com.sun.star.security.RuntimePermission (name=\"" +
452 bool AllPermission::implies( Permission
const & ) const
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
);
495 throw RuntimeException( "checking for unsupported permission type: " + perm_type
.getTypeName() );
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() );
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
))
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." );
534 OUStringToOString( buf
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
) );
535 SAL_INFO("stoc",( "%s", str
.getStr() );
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
))
561 demanded_diag( demanded
);
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
))
574 demanded_diag( demanded
);
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
))
587 demanded_diag( demanded
);
591 throwAccessControlException( demanded
, perm
);
593 else if (demanded_type
.equals( cppu::UnoType
<security::AllPermission
>::get()))
595 AllPermission demanded
;
596 if (implies( m_head
, demanded
))
599 demanded_diag( demanded
);
603 throwAccessControlException( demanded
, perm
);
607 throw RuntimeException( "checking for unsupported permission type: " + demanded_type
.getTypeName() );
613 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */