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