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 <osl/diagnose.h>
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>
37 #include "permissions.h"
40 using namespace ::std
;
41 using namespace ::osl
;
42 using namespace ::com::sun::star
;
43 using namespace css::uno
;
49 static inline 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 inline 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();
100 class SocketPermission
: public Permission
102 static char const * s_actions
[];
106 sal_Int32 m_lowerPort
;
107 sal_Int32 m_upperPort
;
108 mutable OUString m_ip
;
109 mutable bool m_resolveErr
;
110 mutable bool m_resolvedHost
;
113 inline bool resolveHost() const;
117 connection::SocketPermission
const & perm
,
118 ::rtl::Reference
< Permission
> const & next
= ::rtl::Reference
< Permission
>() );
119 virtual bool implies( Permission
const & perm
) const override
;
120 virtual OUString
toString() const override
;
123 char const * SocketPermission::s_actions
[] = { "accept", "connect", "listen", "resolve", nullptr };
125 SocketPermission::SocketPermission(
126 connection::SocketPermission
const & perm
,
127 ::rtl::Reference
< Permission
> const & next
)
128 : Permission( SOCKET
, next
)
129 , m_actions( makeMask( perm
.Actions
, s_actions
) )
130 , m_host( perm
.Host
)
132 , m_upperPort( 65535 )
133 , m_resolveErr( false )
134 , m_resolvedHost( false )
135 , m_wildCardHost( !perm
.Host
.isEmpty() && '*' == perm
.Host
.pData
->buffer
[ 0 ] )
137 if (0xe0000000 & m_actions
) // if any (except resolve) is given => resolve implied
138 m_actions
|= 0x10000000;
140 // separate host from portrange
141 sal_Int32 colon
= m_host
.indexOf( ':' );
142 if (colon
>= 0) // port [range] given
144 sal_Int32 minus
= m_host
.indexOf( '-', colon
+1 );
147 m_lowerPort
= m_upperPort
= m_host
.copy( colon
+1 ).toInt32();
149 else if (minus
== (colon
+1)) // -N
151 m_upperPort
= m_host
.copy( minus
+1 ).toInt32();
153 else if (minus
== (m_host
.getLength() -1)) // N-
155 m_lowerPort
= m_host
.copy( colon
+1, m_host
.getLength() -1 -colon
-1 ).toInt32();
159 m_lowerPort
= m_host
.copy( colon
+1, minus
- colon
-1 ).toInt32();
160 m_upperPort
= m_host
.copy( minus
+1 ).toInt32();
162 m_host
= m_host
.copy( 0, colon
);
166 inline bool SocketPermission::resolveHost() const
171 if (! m_resolvedHost
)
175 SocketAddr::resolveHostname( m_host
, addr
);
177 m_resolveErr
= (::osl_Socket_Ok
!= ::osl_getDottedInetAddrOfSocketAddr(
178 addr
.getHandle(), &ip
.pData
));
182 MutexGuard
guard( Mutex::getGlobalMutex() );
183 if (! m_resolvedHost
)
186 m_resolvedHost
= true;
189 return m_resolvedHost
;
192 bool SocketPermission::implies( Permission
const & perm
) const
195 if (SOCKET
!= perm
.m_type
)
197 SocketPermission
const & demanded
= static_cast< SocketPermission
const & >( perm
);
200 if ((m_actions
& demanded
.m_actions
) != demanded
.m_actions
)
204 if (demanded
.m_lowerPort
< m_lowerPort
)
206 if (demanded
.m_upperPort
> m_upperPort
)
209 // quick check host (DNS names: RFC 1034/1035)
210 if (m_host
.equalsIgnoreAsciiCase( demanded
.m_host
))
212 // check for host wildcards
215 OUString
const & demanded_host
= demanded
.m_host
;
216 if (demanded_host
.getLength() <= m_host
.getLength())
218 sal_Int32 len
= m_host
.getLength() -1; // skip star
219 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
220 demanded_host
.getStr() + demanded_host
.getLength() - len
, len
,
221 m_host
.pData
->buffer
+ 1, len
));
223 if (demanded
.m_wildCardHost
)
226 // compare IP addresses
229 if (! demanded
.resolveHost())
231 return m_ip
== demanded
.m_ip
;
234 OUString
SocketPermission::toString() const
236 OUStringBuffer
buf( 48 );
238 buf
.append( "com.sun.star.connection.SocketPermission (host=\"" );
239 buf
.append( m_host
);
247 if (0 != m_lowerPort
|| 65535 != m_upperPort
)
251 buf
.append( m_lowerPort
);
252 if (m_upperPort
> m_lowerPort
)
255 if (m_upperPort
< 65535)
256 buf
.append( m_upperPort
);
260 buf
.append( "\", actions=\"" );
261 buf
.append( makeStrings( m_actions
, s_actions
) );
263 return buf
.makeStringAndClear();
267 class FilePermission
: public Permission
269 static char const * s_actions
[];
277 io::FilePermission
const & perm
,
278 ::rtl::Reference
< Permission
> const & next
= ::rtl::Reference
< Permission
>() );
279 virtual bool implies( Permission
const & perm
) const override
;
280 virtual OUString
toString() const override
;
283 char const * FilePermission::s_actions
[] = { "read", "write", "execute", "delete", nullptr };
285 static OUString
const & getWorkingDir()
287 static OUString
* s_workingDir
= nullptr;
291 ::osl_getProcessWorkingDir( &workingDir
.pData
);
293 MutexGuard
guard( Mutex::getGlobalMutex() );
296 static OUString
s_dir( workingDir
);
297 s_workingDir
= &s_dir
;
300 return *s_workingDir
;
303 FilePermission::FilePermission(
304 io::FilePermission
const & perm
,
305 ::rtl::Reference
< Permission
> const & next
)
306 : Permission( FILE, next
)
307 , m_actions( makeMask( perm
.Actions
, s_actions
) )
309 , m_allFiles( perm
.URL
== "<<ALL FILES>>" )
315 OUStringBuffer
buf( 64 );
316 buf
.append( getWorkingDir() );
318 m_url
= buf
.makeStringAndClear();
320 else if ( m_url
== "-" )
322 OUStringBuffer
buf( 64 );
323 buf
.append( getWorkingDir() );
325 m_url
= buf
.makeStringAndClear();
327 else if (!m_url
.startsWith("file:///"))
331 oslFileError rc
= ::osl_getAbsoluteFileURL(
332 getWorkingDir().pData
, perm
.URL
.pData
, &out
.pData
);
333 m_url
= (osl_File_E_None
== rc
? out
: perm
.URL
); // fallback
336 // correct win drive letters
337 if (9 < m_url
.getLength() && '|' == m_url
[ 9 ]) // file:///X|
339 static OUString s_colon
= ":";
340 // common case in API is a ':' (sal), so convert '|' to ':'
341 m_url
= m_url
.replaceAt( 9, 1, s_colon
);
347 bool FilePermission::implies( Permission
const & perm
) const
350 if (FILE != perm
.m_type
)
352 FilePermission
const & demanded
= static_cast< FilePermission
const & >( perm
);
355 if ((m_actions
& demanded
.m_actions
) != demanded
.m_actions
)
361 if (demanded
.m_allFiles
)
365 if (m_url
.equalsIgnoreAsciiCase( demanded
.m_url
))
368 if (m_url
== demanded
.m_url
)
371 if (m_url
.getLength() > demanded
.m_url
.getLength())
373 // check /- wildcard: all files and recursive in that path
374 if (m_url
.endsWith("/-"))
376 // demanded url must start with granted path (including path trailing path sep)
377 sal_Int32 len
= m_url
.getLength() -1;
379 return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
380 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
));
382 return (0 == ::rtl_ustr_reverseCompare_WithLength(
383 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
));
386 // check /* wildcard: all files in that path (not recursive!)
387 if (m_url
.endsWith("/*"))
389 // demanded url must start with granted path (including path trailing path sep)
390 sal_Int32 len
= m_url
.getLength() -1;
392 return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength(
393 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
)) &&
394 (0 > demanded
.m_url
.indexOf( '/', len
))); // in addition, no deeper paths
396 return ((0 == ::rtl_ustr_reverseCompare_WithLength(
397 demanded
.m_url
.pData
->buffer
, len
, m_url
.pData
->buffer
, len
)) &&
398 (0 > demanded
.m_url
.indexOf( '/', len
))); // in addition, no deeper paths
404 OUString
FilePermission::toString() const
406 OUStringBuffer
buf( 48 );
408 buf
.append( "com.sun.star.io.FilePermission (url=\"" );
411 buf
.append( "\", actions=\"" );
412 buf
.append( makeStrings( m_actions
, s_actions
) );
414 return buf
.makeStringAndClear();
418 class RuntimePermission
: public Permission
424 security::RuntimePermission
const & perm
,
425 ::rtl::Reference
< Permission
> const & next
= ::rtl::Reference
< Permission
>() )
426 : Permission( RUNTIME
, next
)
427 , m_name( perm
.Name
)
429 virtual bool implies( Permission
const & perm
) const override
;
430 virtual OUString
toString() const override
;
433 bool RuntimePermission::implies( Permission
const & perm
) const
436 if (RUNTIME
!= perm
.m_type
)
438 RuntimePermission
const & demanded
= static_cast< RuntimePermission
const & >( perm
);
441 return m_name
== demanded
.m_name
;
444 OUString
RuntimePermission::toString() const
446 OUStringBuffer
buf( 48 );
447 buf
.append( "com.sun.star.security.RuntimePermission (name=\"" );
448 buf
.append( m_name
);
450 return buf
.makeStringAndClear();
454 bool AllPermission::implies( Permission
const & ) const
459 OUString
AllPermission::toString() const
461 return OUString("com.sun.star.security.AllPermission");
465 PermissionCollection::PermissionCollection(
466 Sequence
< Any
> const & permissions
, PermissionCollection
const & addition
)
467 : m_head( addition
.m_head
)
469 Any
const * perms
= permissions
.getConstArray();
470 for ( sal_Int32 nPos
= permissions
.getLength(); nPos
--; )
472 Any
const & perm
= perms
[ nPos
];
473 Type
const & perm_type
= perm
.getValueType();
475 // supported permission types
476 if (perm_type
.equals( cppu::UnoType
<io::FilePermission
>::get()))
478 m_head
= new FilePermission(
479 *static_cast< io::FilePermission
const * >( perm
.pData
), m_head
);
481 else if (perm_type
.equals( cppu::UnoType
<connection::SocketPermission
>::get()))
483 m_head
= new SocketPermission(
484 *static_cast< connection::SocketPermission
const * >( perm
.pData
), m_head
);
486 else if (perm_type
.equals( cppu::UnoType
<security::RuntimePermission
>::get()))
488 m_head
= new RuntimePermission(
489 *static_cast< security::RuntimePermission
const * >( perm
.pData
), m_head
);
491 else if (perm_type
.equals( cppu::UnoType
<security::AllPermission
>::get()))
493 m_head
= new AllPermission( m_head
);
497 throw RuntimeException( "checking for unsupported permission type: " + perm_type
.getTypeName() );
503 Sequence
< OUString
> PermissionCollection::toStrings() const
505 vector
< OUString
> strings
;
506 strings
.reserve( 8 );
507 for ( Permission
* perm
= m_head
.get(); perm
; perm
= perm
->m_next
.get() )
509 strings
.push_back( perm
->toString() );
511 return Sequence
< OUString
>( strings
.data(), strings
.size() );
515 inline static bool implies(
516 ::rtl::Reference
< Permission
> const & head
, Permission
const & demanded
)
518 for ( Permission
* perm
= head
.get(); perm
; perm
= perm
->m_next
.get() )
520 if (perm
->implies( demanded
))
528 static void demanded_diag(
529 Permission
const & perm
)
531 OUStringBuffer
buf( 48 );
532 buf
.append( "demanding " );
533 buf
.append( perm
.toString() );
534 buf
.append( " => ok." );
536 OUStringToOString( buf
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
) );
537 SAL_INFO("stoc",( "%s", str
.getStr() );
541 static void throwAccessControlException(
542 Permission
const & perm
, Any
const & demanded_perm
)
544 throw security::AccessControlException(
545 "access denied: " + perm
.toString(),
546 Reference
< XInterface
>(), demanded_perm
);
549 void PermissionCollection::checkPermission( Any
const & perm
) const
551 Type
const & demanded_type
= perm
.getValueType();
553 // supported permission types
554 // stack object of SimpleReferenceObject are ok, as long as they are not
555 // assigned to a ::rtl::Reference<> (=> delete this)
556 if (demanded_type
.equals( cppu::UnoType
<io::FilePermission
>::get()))
558 FilePermission
demanded(
559 *static_cast< io::FilePermission
const * >( perm
.pData
) );
560 if (implies( m_head
, demanded
))
563 demanded_diag( demanded
);
567 throwAccessControlException( demanded
, perm
);
569 else if (demanded_type
.equals( cppu::UnoType
<connection::SocketPermission
>::get()))
571 SocketPermission
demanded(
572 *static_cast< connection::SocketPermission
const * >( perm
.pData
) );
573 if (implies( m_head
, demanded
))
576 demanded_diag( demanded
);
580 throwAccessControlException( demanded
, perm
);
582 else if (demanded_type
.equals( cppu::UnoType
<security::RuntimePermission
>::get()))
584 RuntimePermission
demanded(
585 *static_cast< security::RuntimePermission
const * >( perm
.pData
) );
586 if (implies( m_head
, demanded
))
589 demanded_diag( demanded
);
593 throwAccessControlException( demanded
, perm
);
595 else if (demanded_type
.equals( cppu::UnoType
<security::AllPermission
>::get()))
597 AllPermission demanded
;
598 if (implies( m_head
, demanded
))
601 demanded_diag( demanded
);
605 throwAccessControlException( demanded
, perm
);
609 throw RuntimeException( "checking for unsupported permission type: " + demanded_type
.getTypeName() );
615 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */