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/diagnose.h>
24 #include <osl/mutex.hxx>
25 #include <osl/thread.hxx>
27 #include <rtl/ref.hxx>
28 #include <rtl/ustrbuf.hxx>
30 #include <uno/current_context.h>
31 #include <uno/lbnames.h>
33 #include <cppuhelper/implbase.hxx>
34 #include <cppuhelper/compbase.hxx>
35 #include <cppuhelper/implementationentry.hxx>
36 #include <cppuhelper/supportsservice.hxx>
38 #include <com/sun/star/uno/XCurrentContext.hpp>
39 #include <com/sun/star/uno/DeploymentException.hpp>
40 #include <com/sun/star/lang/DisposedException.hpp>
41 #include <com/sun/star/lang/XComponent.hpp>
42 #include <com/sun/star/lang/XServiceInfo.hpp>
43 #include <com/sun/star/lang/XInitialization.hpp>
44 #include <com/sun/star/security/XAccessController.hpp>
45 #include <com/sun/star/security/XPolicy.hpp>
47 #include "lru_cache.h"
48 #include "permissions.h"
52 #define SERVICE_NAME "com.sun.star.security.AccessController"
53 #define USER_CREDS "access-control.user-credentials"
56 using namespace ::std
;
57 using namespace ::osl
;
58 using namespace ::cppu
;
59 using namespace ::com::sun::star
;
60 using namespace css::uno
;
61 using namespace stoc_sec
;
65 // static stuff initialized when loading lib
66 static OUString s_envType
= CPPU_CURRENT_LANGUAGE_BINDING_NAME
;
67 const char s_acRestriction
[] = "access-control.restriction";
70 /** ac context intersects permissions of two ac contexts
72 class acc_Intersection
73 : public WeakImplHelper
< security::XAccessControlContext
>
75 Reference
< security::XAccessControlContext
> m_x1
, m_x2
;
77 inline acc_Intersection(
78 Reference
< security::XAccessControlContext
> const & x1
,
79 Reference
< security::XAccessControlContext
> const & x2
);
82 static inline Reference
< security::XAccessControlContext
> create(
83 Reference
< security::XAccessControlContext
> const & x1
,
84 Reference
< security::XAccessControlContext
> const & x2
);
86 // XAccessControlContext impl
87 virtual void SAL_CALL
checkPermission(
88 Any
const & perm
) override
;
91 inline acc_Intersection::acc_Intersection(
92 Reference
< security::XAccessControlContext
> const & x1
,
93 Reference
< security::XAccessControlContext
> const & x2
)
98 inline Reference
< security::XAccessControlContext
> acc_Intersection::create(
99 Reference
< security::XAccessControlContext
> const & x1
,
100 Reference
< security::XAccessControlContext
> const & x2
)
106 return new acc_Intersection( x1
, x2
);
109 void acc_Intersection::checkPermission(
112 m_x1
->checkPermission( perm
);
113 m_x2
->checkPermission( perm
);
116 /** ac context unifies permissions of two ac contexts
119 : public WeakImplHelper
< security::XAccessControlContext
>
121 Reference
< security::XAccessControlContext
> m_x1
, m_x2
;
124 Reference
< security::XAccessControlContext
> const & x1
,
125 Reference
< security::XAccessControlContext
> const & x2
);
128 static inline Reference
< security::XAccessControlContext
> create(
129 Reference
< security::XAccessControlContext
> const & x1
,
130 Reference
< security::XAccessControlContext
> const & x2
);
132 // XAccessControlContext impl
133 virtual void SAL_CALL
checkPermission(
134 Any
const & perm
) override
;
137 inline acc_Union::acc_Union(
138 Reference
< security::XAccessControlContext
> const & x1
,
139 Reference
< security::XAccessControlContext
> const & x2
)
144 inline Reference
< security::XAccessControlContext
> acc_Union::create(
145 Reference
< security::XAccessControlContext
> const & x1
,
146 Reference
< security::XAccessControlContext
> const & x2
)
149 return Reference
< security::XAccessControlContext
>(); // unrestricted
151 return Reference
< security::XAccessControlContext
>(); // unrestricted
152 return new acc_Union( x1
, x2
);
155 void acc_Union::checkPermission(
160 m_x1
->checkPermission( perm
);
162 catch (security::AccessControlException
&)
164 m_x2
->checkPermission( perm
);
168 /** ac context doing permission checks on static permissions
171 : public WeakImplHelper
< security::XAccessControlContext
>
173 PermissionCollection m_permissions
;
177 PermissionCollection
const & permissions
)
178 : m_permissions( permissions
)
181 // XAccessControlContext impl
182 virtual void SAL_CALL
checkPermission(
183 Any
const & perm
) override
;
186 void acc_Policy::checkPermission(
189 m_permissions
.checkPermission( perm
);
192 /** current context overriding dynamic ac restriction
194 class acc_CurrentContext
195 : public WeakImplHelper
< XCurrentContext
>
197 Reference
< XCurrentContext
> m_xDelegate
;
201 inline acc_CurrentContext(
202 Reference
< XCurrentContext
> const & xDelegate
,
203 Reference
< security::XAccessControlContext
> const & xRestriction
);
205 // XCurrentContext impl
206 virtual Any SAL_CALL
getValueByName( OUString
const & name
) override
;
209 inline acc_CurrentContext::acc_CurrentContext(
210 Reference
< XCurrentContext
> const & xDelegate
,
211 Reference
< security::XAccessControlContext
> const & xRestriction
)
212 : m_xDelegate( xDelegate
)
214 if (xRestriction
.is())
216 m_restriction
<<= xRestriction
;
218 // return empty any otherwise on getValueByName(), not null interface
221 Any
acc_CurrentContext::getValueByName( OUString
const & name
)
223 if (name
== s_acRestriction
)
225 return m_restriction
;
227 else if (m_xDelegate
.is())
229 return m_xDelegate
->getValueByName( name
);
238 inline Reference
< security::XAccessControlContext
> getDynamicRestriction(
239 Reference
< XCurrentContext
> const & xContext
)
243 Any
acc(xContext
->getValueByName(s_acRestriction
));
244 if (typelib_TypeClass_INTERFACE
== acc
.pType
->eTypeClass
)
246 // avoid ref-counting
247 OUString
const & typeName
=
248 OUString::unacquired( &acc
.pType
->pTypeName
);
249 if ( typeName
== "com.sun.star.security.XAccessControlContext" )
251 return Reference
< security::XAccessControlContext
>(
252 *static_cast< security::XAccessControlContext
** const >( acc
.pData
) );
256 return Reference
< security::XAccessControlContext
>::query(
257 *static_cast< XInterface
** const >( acc
.pData
) );
261 return Reference
< security::XAccessControlContext
>();
268 explicit cc_reset( void * cc
)
271 { ::uno_setCurrentContext( m_cc
, s_envType
.pData
, nullptr ); }
278 typedef WeakComponentImplHelper
<
279 security::XAccessController
, lang::XServiceInfo
, lang::XInitialization
> t_helper
;
282 class AccessController
286 Reference
< XComponentContext
> m_xComponentContext
;
288 Reference
< security::XPolicy
> m_xPolicy
;
289 Reference
< security::XPolicy
> const & getPolicy();
292 enum Mode
{ OFF
, ON
, DYNAMIC_ONLY
, SINGLE_USER
, SINGLE_DEFAULT_USER
} m_mode
;
294 PermissionCollection m_defaultPermissions
;
295 // for single-user mode
296 PermissionCollection m_singleUserPermissions
;
297 OUString m_singleUserId
;
298 bool m_defaultPerm_init
;
299 bool m_singleUser_init
;
300 // for multi-user mode
301 lru_cache
< OUString
, PermissionCollection
, OUStringHash
, equal_to
< OUString
> >
305 typedef vector
< pair
< OUString
, Any
> > t_rec_vec
;
306 inline void clearPostPoned();
307 void checkAndClearPostPoned();
309 PermissionCollection
getEffectivePermissions(
310 Reference
< XCurrentContext
> const & xContext
,
311 Any
const & demanded_perm
);
314 virtual void SAL_CALL
disposing() override
;
317 explicit AccessController( Reference
< XComponentContext
> const & xComponentContext
);
319 // XInitialization impl
320 virtual void SAL_CALL
initialize(
321 Sequence
< Any
> const & arguments
) override
;
323 // XAccessController impl
324 virtual void SAL_CALL
checkPermission(
325 Any
const & perm
) override
;
326 virtual Any SAL_CALL
doRestricted(
327 Reference
< security::XAction
> const & xAction
,
328 Reference
< security::XAccessControlContext
> const & xRestriction
) override
;
329 virtual Any SAL_CALL
doPrivileged(
330 Reference
< security::XAction
> const & xAction
,
331 Reference
< security::XAccessControlContext
> const & xRestriction
) override
;
332 virtual Reference
< security::XAccessControlContext
> SAL_CALL
getContext() override
;
335 virtual OUString SAL_CALL
getImplementationName() override
;
336 virtual sal_Bool SAL_CALL
supportsService( OUString
const & serviceName
) override
;
337 virtual Sequence
< OUString
> SAL_CALL
getSupportedServiceNames() override
;
340 AccessController::AccessController( Reference
< XComponentContext
> const & xComponentContext
)
341 : t_helper( m_mutex
)
342 , m_xComponentContext( xComponentContext
)
343 , m_mode( ON
) // default
344 , m_defaultPerm_init( false )
345 , m_singleUser_init( false )
348 // The .../mode value had originally been set in
349 // cppu::add_access_control_entries (cppuhelper/source/servicefactory.cxx)
350 // to something other than "off" depending on various UNO_AC* bootstrap
351 // variables that are no longer supported, so this is mostly dead code now:
353 if (m_xComponentContext
->getValueByName( "/services/" SERVICE_NAME
"/mode" ) >>= mode
)
359 else if ( mode
== "on" )
363 else if ( mode
== "dynamic-only" )
365 m_mode
= DYNAMIC_ONLY
;
367 else if ( mode
== "single-user" )
369 m_xComponentContext
->getValueByName(
370 "/services/" SERVICE_NAME
"/single-user-id" ) >>= m_singleUserId
;
371 if (m_singleUserId
.isEmpty())
373 throw RuntimeException(
374 "expected a user id in component context entry "
375 "\"/services/" SERVICE_NAME
"/single-user-id\"!",
376 static_cast<OWeakObject
*>(this) );
378 m_mode
= SINGLE_USER
;
380 else if ( mode
== "single-default-user" )
382 m_mode
= SINGLE_DEFAULT_USER
;
386 // switch on caching for DYNAMIC_ONLY and ON (shareable multi-user process)
387 if (ON
== m_mode
|| DYNAMIC_ONLY
== m_mode
)
389 sal_Int32 cacheSize
= 0; // multi-user cache size
390 if (! (m_xComponentContext
->getValueByName(
391 "/services/" SERVICE_NAME
"/user-cache-size" ) >>= cacheSize
))
393 cacheSize
= 128; // reasonable default?
395 #ifdef __CACHE_DIAGNOSE
398 m_user2permissions
.setSize( cacheSize
);
402 void AccessController::disposing()
404 m_mode
= OFF
; // avoid checks from now on xxx todo review/ better DYNAMIC_ONLY?
406 m_xComponentContext
.clear();
409 // XInitialization impl
411 void AccessController::initialize(
412 Sequence
< Any
> const & arguments
)
414 // xxx todo: review for forking
415 // portal forking hack: re-initialize for another user-id
416 if (SINGLE_USER
!= m_mode
) // only if in single-user mode
418 throw RuntimeException(
419 "invalid call: ac must be in \"single-user\" mode!", static_cast<OWeakObject
*>(this) );
422 arguments
[ 0 ] >>= userId
;
423 if ( userId
.isEmpty() )
425 throw RuntimeException(
426 "expected a user-id as first argument!", static_cast<OWeakObject
*>(this) );
428 // assured that no sync is necessary: no check happens at this forking time
429 m_singleUserId
= userId
;
430 m_singleUser_init
= false;
434 Reference
< security::XPolicy
> const & AccessController::getPolicy()
436 // get policy singleton
437 if (! m_xPolicy
.is())
439 Reference
< security::XPolicy
> xPolicy
;
440 m_xComponentContext
->getValueByName(
441 "/singletons/com.sun.star.security.thePolicy" ) >>= xPolicy
;
444 MutexGuard
guard( m_mutex
);
445 if (! m_xPolicy
.is())
452 throw SecurityException(
453 "cannot get policy singleton!", static_cast<OWeakObject
*>(this) );
460 static void dumpPermissions(
461 PermissionCollection
const & collection
, OUString
const & userId
= OUString() )
463 OUStringBuffer
buf( 48 );
464 if (!userId
.isEmpty())
466 buf
.append( "> dumping permissions of user \"" );
467 buf
.append( userId
);
472 buf
.append( "> dumping default permissions:" );
474 SAL_INFO("stoc", buf
.makeStringAndClear() );
475 Sequence
< OUString
> permissions( collection
.toStrings() );
476 OUString
const * p
= permissions
.getConstArray();
477 for ( sal_Int32 nPos
= 0; nPos
< permissions
.getLength(); ++nPos
)
479 SAL_INFO("stoc", p
[ nPos
] );
481 SAL_INFO("stoc", "> permission dump done" );
486 inline void AccessController::clearPostPoned()
488 delete static_cast< t_rec_vec
* >( m_rec
.getData() );
489 m_rec
.setData( nullptr );
492 void AccessController::checkAndClearPostPoned()
494 // check postponed permissions
495 std::unique_ptr
< t_rec_vec
> rec( static_cast< t_rec_vec
* >( m_rec
.getData() ) );
496 m_rec
.setData( nullptr ); // takeover ownership
497 OSL_ASSERT( rec
.get() );
500 t_rec_vec
const & vec
= *rec
.get();
505 OSL_ASSERT( m_singleUser_init
);
506 for (const auto & p
: vec
)
508 OSL_ASSERT( m_singleUserId
.equals( p
.first
) );
509 m_singleUserPermissions
.checkPermission( p
.second
);
513 case SINGLE_DEFAULT_USER
:
515 OSL_ASSERT( m_defaultPerm_init
);
516 for (const auto & p
: vec
)
518 OSL_ASSERT( p
.first
.isEmpty() ); // default-user
519 m_defaultPermissions
.checkPermission( p
.second
);
525 for (const auto & p
: vec
)
527 PermissionCollection
const * pPermissions
;
528 // lookup policy for user
530 MutexGuard
guard( m_mutex
);
531 pPermissions
= m_user2permissions
.lookup( p
.first
);
533 OSL_ASSERT( pPermissions
);
536 pPermissions
->checkPermission( p
.second
);
542 OSL_FAIL( "### this should never be called in this ac mode!" );
548 /** this is the only function calling the policy singleton and thus has to take care
551 @param demanded_perm (if not empty) is the demanded permission of a checkPermission() call
552 which will be postponed for recurring calls
554 PermissionCollection
AccessController::getEffectivePermissions(
555 Reference
< XCurrentContext
> const & xContext
,
556 Any
const & demanded_perm
)
564 if (m_singleUser_init
)
565 return m_singleUserPermissions
;
566 userId
= m_singleUserId
;
569 case SINGLE_DEFAULT_USER
:
571 if (m_defaultPerm_init
)
572 return m_defaultPermissions
;
579 xContext
->getValueByName( USER_CREDS
".id" ) >>= userId
;
581 if ( userId
.isEmpty() )
583 throw SecurityException(
584 "cannot determine current user in multi-user ac!", static_cast<OWeakObject
*>(this) );
587 // lookup policy for user
588 MutexGuard
guard( m_mutex
);
589 PermissionCollection
const * pPermissions
= m_user2permissions
.lookup( userId
);
591 return *pPermissions
;
595 OSL_FAIL( "### this should never be called in this ac mode!" );
596 return PermissionCollection();
600 // iff this is a recurring call for the default user, then grant all permissions
601 t_rec_vec
* rec
= static_cast< t_rec_vec
* >( m_rec
.getData() );
602 if (rec
) // tls entry exists => this is recursive call
604 if (demanded_perm
.hasValue())
607 rec
->push_back( pair
< OUString
, Any
>( userId
, demanded_perm
) );
610 OUStringBuffer
buf( 48 );
611 buf
.append( "> info: recurring call of user \"" );
612 buf
.append( userId
);
615 OUStringToOString( buf
.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US
) );
616 SAL_INFO("stoc",( "%s", str
.getStr() );
618 return PermissionCollection( new AllPermission() );
623 m_rec
.setData( rec
);
628 // init default permissions
629 if (! m_defaultPerm_init
)
631 PermissionCollection
defaultPermissions(
632 getPolicy()->getDefaultPermissions() );
634 MutexGuard
guard( m_mutex
);
635 if (! m_defaultPerm_init
)
637 m_defaultPermissions
= defaultPermissions
;
638 m_defaultPerm_init
= true;
641 dumpPermissions( m_defaultPermissions
);
645 PermissionCollection ret
;
647 // init user permissions
652 ret
= PermissionCollection(
653 getPolicy()->getPermissions( userId
), m_defaultPermissions
);
656 MutexGuard
guard( m_mutex
);
657 if (m_singleUser_init
)
659 ret
= m_singleUserPermissions
;
663 m_singleUserPermissions
= ret
;
664 m_singleUser_init
= true;
668 dumpPermissions( ret
, userId
);
672 case SINGLE_DEFAULT_USER
:
674 ret
= m_defaultPermissions
;
679 ret
= PermissionCollection(
680 getPolicy()->getPermissions( userId
), m_defaultPermissions
);
683 MutexGuard
guard( m_mutex
);
684 m_user2permissions
.set( userId
, ret
);
687 dumpPermissions( ret
, userId
);
696 checkAndClearPostPoned();
699 catch (const security::AccessControlException
& exc
) // wrapped into DeploymentException
701 clearPostPoned(); // safety: exception could have happened before checking postponed?
702 throw DeploymentException( "deployment error (AccessControlException occurred): " + exc
.Message
, exc
.Context
);
704 catch (RuntimeException
&)
706 // don't check postponed, just cleanup
708 delete static_cast< t_rec_vec
* >( m_rec
.getData() );
709 m_rec
.setData( nullptr );
714 // check postponed permissions first
715 // => AccessControlExceptions are errors, user exceptions not!
716 checkAndClearPostPoned();
721 // don't check postponed, just cleanup
727 // XAccessController impl
729 void AccessController::checkPermission(
732 if (rBHelper
.bDisposed
)
734 throw lang::DisposedException(
735 "checkPermission() call on disposed AccessController!", static_cast<OWeakObject
*>(this) );
741 // first dynamic check of ac contexts
742 Reference
< XCurrentContext
> xContext
;
743 ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext
), s_envType
.pData
, nullptr );
744 Reference
< security::XAccessControlContext
> xACC( getDynamicRestriction( xContext
) );
747 xACC
->checkPermission( perm
);
750 if (DYNAMIC_ONLY
== m_mode
)
754 getEffectivePermissions( xContext
, perm
).checkPermission( perm
);
757 Any
AccessController::doRestricted(
758 Reference
< security::XAction
> const & xAction
,
759 Reference
< security::XAccessControlContext
> const & xRestriction
)
761 if (rBHelper
.bDisposed
)
763 throw lang::DisposedException(
764 "doRestricted() call on disposed AccessController!", static_cast<OWeakObject
*>(this) );
767 if (OFF
== m_mode
) // optimize this way, because no dynamic check will be performed
768 return xAction
->run();
770 if (xRestriction
.is())
772 Reference
< XCurrentContext
> xContext
;
773 ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext
), s_envType
.pData
, nullptr );
775 // override restriction
776 Reference
< XCurrentContext
> xNewContext(
777 new acc_CurrentContext( xContext
, acc_Intersection::create(
778 xRestriction
, getDynamicRestriction( xContext
) ) ) );
779 ::uno_setCurrentContext( xNewContext
.get(), s_envType
.pData
, nullptr );
780 cc_reset
reset( xContext
.get() );
781 return xAction
->run();
785 return xAction
->run();
789 Any
AccessController::doPrivileged(
790 Reference
< security::XAction
> const & xAction
,
791 Reference
< security::XAccessControlContext
> const & xRestriction
)
793 if (rBHelper
.bDisposed
)
795 throw lang::DisposedException(
796 "doPrivileged() call on disposed AccessController!", static_cast<OWeakObject
*>(this) );
799 if (OFF
== m_mode
) // no dynamic check will be performed
801 return xAction
->run();
804 Reference
< XCurrentContext
> xContext
;
805 ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext
), s_envType
.pData
, nullptr );
807 Reference
< security::XAccessControlContext
> xOldRestr(
808 getDynamicRestriction( xContext
) );
810 if (xOldRestr
.is()) // previous restriction
812 // override restriction
813 Reference
< XCurrentContext
> xNewContext(
814 new acc_CurrentContext( xContext
, acc_Union::create( xRestriction
, xOldRestr
) ) );
815 ::uno_setCurrentContext( xNewContext
.get(), s_envType
.pData
, nullptr );
816 cc_reset
reset( xContext
.get() );
817 return xAction
->run();
819 else // no previous restriction => never current restriction
821 return xAction
->run();
825 Reference
< security::XAccessControlContext
> AccessController::getContext()
827 if (rBHelper
.bDisposed
)
829 throw lang::DisposedException(
830 "getContext() call on disposed AccessController!", static_cast<OWeakObject
*>(this) );
833 if (OFF
== m_mode
) // optimize this way, because no dynamic check will be performed
835 return new acc_Policy( PermissionCollection( new AllPermission() ) );
838 Reference
< XCurrentContext
> xContext
;
839 ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext
), s_envType
.pData
, nullptr );
841 return acc_Intersection::create(
842 getDynamicRestriction( xContext
),
843 new acc_Policy( getEffectivePermissions( xContext
, Any() ) ) );
848 OUString
AccessController::getImplementationName()
850 return OUString("com.sun.star.security.comp.stoc.AccessController");
853 sal_Bool
AccessController::supportsService( OUString
const & serviceName
)
855 return cppu::supportsService(this, serviceName
);
858 Sequence
< OUString
> AccessController::getSupportedServiceNames()
860 Sequence
<OUString
> aSNS
{ SERVICE_NAME
};
866 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
* SAL_CALL
867 com_sun_star_security_comp_stoc_AccessController_get_implementation(
868 css::uno::XComponentContext
*context
,
869 css::uno::Sequence
<css::uno::Any
> const &)
871 return cppu::acquire(new AccessController(context
));
874 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */