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 <osl/diagnose.h>
22 #include <osl/file.h>
23 #include <rtl/byteseq.hxx>
24 #include <rtl/ustrbuf.hxx>
25 
26 #include <cppuhelper/access_control.hxx>
27 #include <cppuhelper/compbase.hxx>
28 #include <cppuhelper/supportsservice.hxx>
29 
30 #include <com/sun/star/lang/XServiceInfo.hpp>
31 #include <com/sun/star/security/XPolicy.hpp>
32 #include <com/sun/star/security/AllPermission.hpp>
33 #include <com/sun/star/security/RuntimePermission.hpp>
34 #include <com/sun/star/io/FilePermission.hpp>
35 #include <com/sun/star/connection/SocketPermission.hpp>
36 #include <com/sun/star/uno/XComponentContext.hpp>
37 
38 #include <unordered_map>
39 
40 #define IMPL_NAME "com.sun.star.security.comp.stoc.FilePolicy"
41 
42 using namespace ::osl;
43 using namespace ::cppu;
44 using namespace ::com::sun::star;
45 using namespace css::uno;
46 
47 namespace {
48 
49 struct MutexHolder
50 {
51     Mutex m_mutex;
52 };
53 typedef WeakComponentImplHelper< security::XPolicy, lang::XServiceInfo > t_helper;
54 
55 
56 class FilePolicy
57     : public MutexHolder
58     , public t_helper
59 {
60     Reference< XComponentContext > m_xComponentContext;
61     AccessControl m_ac;
62 
63     Sequence< Any > m_defaultPermissions;
64     typedef std::unordered_map< OUString, Sequence< Any > > t_permissions;
65     t_permissions m_userPermissions;
66     bool m_init;
67 
68 protected:
69     virtual void SAL_CALL disposing() override;
70 
71 public:
72     explicit FilePolicy( Reference< XComponentContext > const & xComponentContext );
73 
74     // XPolicy impl
75     virtual Sequence< Any > SAL_CALL getPermissions(
76         OUString const & userId ) override;
77     virtual Sequence< Any > SAL_CALL getDefaultPermissions() override;
78     virtual void SAL_CALL refresh() override;
79 
80     // XServiceInfo impl
81     virtual OUString SAL_CALL getImplementationName() override;
82     virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override;
83     virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
84 };
85 
FilePolicy(Reference<XComponentContext> const & xComponentContext)86 FilePolicy::FilePolicy( Reference< XComponentContext > const & xComponentContext )
87     : t_helper( m_mutex )
88     , m_xComponentContext( xComponentContext )
89     , m_ac( xComponentContext )
90     , m_init( false )
91 {}
92 
disposing()93 void FilePolicy::disposing()
94 {
95     m_userPermissions.clear();
96     m_defaultPermissions = Sequence< Any >();
97     m_xComponentContext.clear();
98 }
99 
100 
getPermissions(OUString const & userId)101 Sequence< Any > FilePolicy::getPermissions(
102     OUString const & userId )
103 {
104     if (! m_init)
105     {
106         refresh();
107         m_init = true;
108     }
109 
110     MutexGuard guard( m_mutex );
111     t_permissions::iterator iFind( m_userPermissions.find( userId ) );
112     if (m_userPermissions.end() == iFind)
113     {
114         return Sequence< Any >();
115     }
116     else
117     {
118         return iFind->second;
119     }
120 }
121 
getDefaultPermissions()122 Sequence< Any > FilePolicy::getDefaultPermissions()
123 {
124     if (! m_init)
125     {
126         refresh();
127         m_init = true;
128     }
129 
130     MutexGuard guard( m_mutex );
131     return m_defaultPermissions;
132 }
133 
134 
135 class PolicyReader
136 {
137     OUString const m_fileName;
138     oslFileHandle m_file;
139 
140     sal_Int32 m_linepos;
141     rtl::ByteSequence m_line;
142     sal_Int32 m_pos;
143     sal_Unicode m_back;
144 
145     sal_Unicode get();
back(sal_Unicode c)146     void back( sal_Unicode c )
147         { m_back = c; }
148 
isWhiteSpace(sal_Unicode c)149     static bool isWhiteSpace( sal_Unicode c )
150         { return (' ' == c || '\t' == c || '\n' == c || '\r' == c); }
151     void skipWhiteSpace();
152 
isCharToken(sal_Unicode c)153     static bool isCharToken( sal_Unicode c )
154         { return (';' == c || ',' == c || '{' == c || '}' == c); }
155 
156 public:
157     PolicyReader( OUString const & file, AccessControl & ac );
158     ~PolicyReader();
159 
160     void error( OUString const & msg );
161 
162     OUString getToken();
163     OUString assureToken();
164     OUString getQuotedToken();
165     OUString assureQuotedToken();
166     void assureToken( sal_Unicode token );
167 };
168 
assureToken(sal_Unicode token)169 void PolicyReader::assureToken( sal_Unicode token )
170 {
171     skipWhiteSpace();
172     sal_Unicode c = get();
173     if (c == token)
174         return;
175     OUString msg = "expected >" + OUStringChar(c) + "<!";
176     error( msg );
177 }
178 
assureQuotedToken()179 OUString PolicyReader::assureQuotedToken()
180 {
181     OUString token( getQuotedToken() );
182     if (token.isEmpty())
183         error( "unexpected end of file!" );
184     return token;
185 }
186 
getQuotedToken()187 OUString PolicyReader::getQuotedToken()
188 {
189     skipWhiteSpace();
190     OUStringBuffer buf( 32 );
191     sal_Unicode c = get();
192     if ('\"' != c)
193         error( "expected quoting >\"< character!" );
194     c = get();
195     while ('\0' != c && '\"' != c)
196     {
197         buf.append( c );
198         c = get();
199     }
200     return buf.makeStringAndClear();
201 }
202 
assureToken()203 OUString PolicyReader::assureToken()
204 {
205     OUString token( getToken() );
206     if ( token.isEmpty())
207         error( "unexpected end of file!" );
208     return token;
209 }
210 
getToken()211 OUString PolicyReader::getToken()
212 {
213     skipWhiteSpace();
214     sal_Unicode c = get();
215     if (isCharToken( c ))
216         return OUString( &c, 1 );
217     OUStringBuffer buf( 32 );
218     while ('\0' != c && !isCharToken( c ) && !isWhiteSpace( c ))
219     {
220         buf.append( c );
221         c = get();
222     }
223     back( c );
224     return buf.makeStringAndClear();
225 }
226 
skipWhiteSpace()227 void PolicyReader::skipWhiteSpace()
228 {
229     sal_Unicode c;
230     do
231     {
232         c = get();
233     }
234     while (isWhiteSpace( c )); // seeking next non-whitespace char
235 
236     if ('/' == c) // C/C++ like comment
237     {
238         c = get();
239         if ('/' == c) // C++ like comment
240         {
241             do
242             {
243                 c = get();
244             }
245             while ('\n' != c && '\0' != c); // seek eol/eof
246             skipWhiteSpace(); // cont skip on next line
247         }
248         else if ('*' == c) // C like comment
249         {
250             bool fini = true;
251             do
252             {
253                 c = get();
254                 if ('*' == c)
255                 {
256                     c = get();
257                     fini = ('/' == c || '\0' == c);
258                 }
259                 else
260                 {
261                     fini = ('\0' == c);
262                 }
263             }
264             while (! fini);
265             skipWhiteSpace(); // cont skip on next line
266         }
267         else
268         {
269             error( "expected C/C++ like comment!" );
270         }
271     }
272     else if ('#' == c) // script like comment
273     {
274         do
275         {
276             c = get();
277         }
278         while ('\n' != c && '\0' != c); // seek eol/eof
279         skipWhiteSpace(); // cont skip on next line
280     }
281 
282     else // is token char
283     {
284         back( c );
285     }
286 }
287 
get()288 sal_Unicode PolicyReader::get()
289 {
290     if ('\0' != m_back) // one char push back possible
291     {
292         sal_Unicode c = m_back;
293         m_back = '\0';
294         return c;
295     }
296     else if (m_pos == m_line.getLength()) // provide newline as whitespace
297     {
298         ++m_pos;
299         return '\n';
300     }
301     else if (m_pos > m_line.getLength()) // read new line
302     {
303         sal_Bool eof;
304         oslFileError rc = ::osl_isEndOfFile( m_file, &eof );
305         if (osl_File_E_None != rc)
306             error( "checking eof failed!" );
307         if (eof)
308             return '\0';
309 
310         rc = ::osl_readLine( m_file, reinterpret_cast< sal_Sequence ** >( &m_line ) );
311         if (osl_File_E_None != rc)
312             error( "read line failed!" );
313         ++m_linepos;
314         if (! m_line.getLength()) // empty line read
315         {
316             m_pos = 1; // read new line next time
317             return '\n';
318         }
319         m_pos = 0;
320     }
321     return (m_line.getConstArray()[ m_pos++ ]);
322 }
323 
error(OUString const & msg)324 void PolicyReader::error( OUString const & msg )
325 {
326     throw RuntimeException(
327         "error processing file \"" + m_fileName +
328         "\" [line " + OUString::number(m_linepos) +
329         ", column " + OUString::number(m_pos) +
330         "] " + msg);
331 }
332 
PolicyReader(OUString const & fileName,AccessControl & ac)333 PolicyReader::PolicyReader( OUString const & fileName, AccessControl & ac )
334     : m_fileName( fileName )
335     , m_linepos( 0 )
336     , m_pos( 1 ) // force readline
337     , m_back( '\0' )
338 {
339     ac.checkFilePermission( m_fileName, "read" );
340     if (osl_File_E_None != ::osl_openFile( m_fileName.pData, &m_file, osl_File_OpenFlag_Read ))
341     {
342         throw RuntimeException( "cannot open file \"" + m_fileName + "\"!" );
343     }
344 }
345 
~PolicyReader()346 PolicyReader::~PolicyReader()
347 {
348     if ( ::osl_closeFile( m_file ) != osl_File_E_None ) {
349         OSL_ASSERT( false );
350     }
351 }
352 
353 #define s_grant "grant"
354 #define s_user "user"
355 #define s_permission "permission"
356 #define s_openBrace "{"
357 #define s_closingBrace "}"
358 
359 #define s_filePermission "com.sun.star.io.FilePermission"
360 #define s_socketPermission "com.sun.star.connection.SocketPermission"
361 #define s_runtimePermission "com.sun.star.security.RuntimePermission"
362 #define s_allPermission "com.sun.star.security.AllPermission"
363 
364 
refresh()365 void FilePolicy::refresh()
366 {
367     // read out file (the .../file-name value had originally been set in
368     // cppu::add_access_control_entries (cppuhelper/source/servicefactory.cxx)
369     // depending on various UNO_AC* bootstrap variables that are no longer
370     // supported, so this is effectively dead code):
371     OUString fileName;
372     m_xComponentContext->getValueByName(
373         "/implementations/" IMPL_NAME "/file-name" ) >>= fileName;
374     if ( fileName.isEmpty() )
375     {
376         throw RuntimeException(
377             "name of policy file unknown!",
378             static_cast<OWeakObject *>(this) );
379     }
380 
381     PolicyReader reader( fileName, m_ac );
382 
383     // fill these two
384     Sequence< Any > defaultPermissions;
385     t_permissions userPermissions;
386 
387     OUString token( reader.getToken() );
388     while (!token.isEmpty())
389     {
390         if ( token != s_grant )
391             reader.error( "expected >grant< token!" );
392         OUString userId;
393         token = reader.assureToken();
394         if ( token == s_user ) // next token is user-id
395         {
396             userId = reader.assureQuotedToken();
397             token = reader.assureToken();
398         }
399         if ( token != s_openBrace )
400             reader.error( "expected opening brace >{<!" );
401         token = reader.assureToken();
402         // permissions list
403         while ( token != s_closingBrace )
404         {
405             if ( token != s_permission )
406                 reader.error( "expected >permission< or closing brace >}<!" );
407 
408             token = reader.assureToken(); // permission type
409             Any perm;
410             if ( token == s_filePermission ) // FilePermission
411             {
412                 OUString url( reader.assureQuotedToken() );
413                 reader.assureToken( ',' );
414                 OUString actions( reader.assureQuotedToken() );
415                 perm <<= io::FilePermission( url, actions );
416             }
417             else if ( token == s_socketPermission ) // SocketPermission
418             {
419                 OUString host( reader.assureQuotedToken() );
420                 reader.assureToken( ',' );
421                 OUString actions( reader.assureQuotedToken() );
422                 perm <<= connection::SocketPermission( host, actions );
423             }
424             else if ( token == s_runtimePermission ) // RuntimePermission
425             {
426                 OUString name( reader.assureQuotedToken() );
427                 perm <<= security::RuntimePermission( name );
428             }
429             else if ( token == s_allPermission ) // AllPermission
430             {
431                 perm <<= security::AllPermission();
432             }
433             else
434             {
435                 reader.error( "expected permission type!" );
436             }
437 
438             reader.assureToken( ';' );
439 
440             // insert
441             if (!userId.isEmpty())
442             {
443                 Sequence< Any > perms( userPermissions[ userId ] );
444                 sal_Int32 len = perms.getLength();
445                 perms.realloc( len +1 );
446                 perms[ len ] = perm;
447                 userPermissions[ userId ] = perms;
448             }
449             else
450             {
451                 sal_Int32 len = defaultPermissions.getLength();
452                 defaultPermissions.realloc( len +1 );
453                 defaultPermissions[ len ] = perm;
454             }
455 
456             token = reader.assureToken(); // next permissions token
457         }
458 
459         reader.assureToken( ';' ); // semi
460         token = reader.getToken(); // next grant token
461     }
462 
463     // assign new ones
464     MutexGuard guard( m_mutex );
465     m_defaultPermissions = defaultPermissions;
466     m_userPermissions = userPermissions;
467 }
468 
469 
getImplementationName()470 OUString FilePolicy::getImplementationName()
471 {
472     return IMPL_NAME;
473 }
474 
supportsService(OUString const & serviceName)475 sal_Bool FilePolicy::supportsService( OUString const & serviceName )
476 {
477     return cppu::supportsService(this, serviceName);
478 }
479 
getSupportedServiceNames()480 Sequence< OUString > FilePolicy::getSupportedServiceNames()
481 {
482     Sequence<OUString> aSNS { "com.sun.star.security.Policy" };
483     return aSNS;
484 }
485 
486 } // namespace
487 
488 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
com_sun_star_security_comp_stoc_FilePolicy_get_implementation(css::uno::XComponentContext * context,css::uno::Sequence<css::uno::Any> const &)489 com_sun_star_security_comp_stoc_FilePolicy_get_implementation(
490     css::uno::XComponentContext *context,
491     css::uno::Sequence<css::uno::Any> const &)
492 {
493     return cppu::acquire(new FilePolicy(context));
494 }
495 
496 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
497