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 #include <sal/config.h>
21
22 #include <cassert>
23 #include <cstddef>
24 #include <cstring>
25 #include <limits>
26
27 #ifdef IOS
28 #include <premac.h>
29 #import <Foundation/Foundation.h>
30 #include <postmac.h>
31 #endif
32
33 #include "system.hxx"
34
35 #include <o3tl/safeint.hxx>
36 #include <osl/security.h>
37 #include <rtl/bootstrap.hxx>
38 #include <sal/log.hxx>
39
40 #include <osl/thread.h>
41 #include <osl/file.h>
42
43 #if defined LINUX || defined __sun
44 #include <crypt.h>
45 #endif
46
47 #if defined HAIKU
48 #include <fs_info.h>
49 #include <FindDirectory.h>
50 #endif
51
52 #include "secimpl.hxx"
53
54 #ifdef ANDROID
55 #define getpwuid_r(uid, pwd, buf, buflen, result) (*(result) = getpwuid(uid), (*(result) ? (memcpy (buf, *(result), sizeof (struct passwd)), 0) : errno))
56 #endif
57
58 static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory);
59 static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory);
60
sysconf_SC_GETPW_R_SIZE_MAX(std::size_t * value)61 static bool sysconf_SC_GETPW_R_SIZE_MAX(std::size_t * value) {
62 #if defined _SC_GETPW_R_SIZE_MAX
63 long m;
64 errno = 0;
65 m = sysconf(_SC_GETPW_R_SIZE_MAX);
66 if (m == -1) {
67 /* _SC_GETPW_R_SIZE_MAX has no limit; some platforms like certain
68 FreeBSD versions support sysconf(_SC_GETPW_R_SIZE_MAX) in a broken
69 way and always set EINVAL, so be resilient here: */
70 return false;
71 }
72 SAL_WARN_IF( m < 0 || o3tl::make_unsigned(m) >= std::numeric_limits<std::size_t>::max(), "sal.osl",
73 "m < 0 || (unsigned long) m >= std::numeric_limits<std::size_t>::max()");
74 *value = static_cast<std::size_t>(m);
75 return true;
76 #else
77 /* some platforms like macOS 1.3 do not define _SC_GETPW_R_SIZE_MAX: */
78 return false;
79 #endif
80 }
81
growSecurityImpl(oslSecurityImpl * impl,std::size_t * bufSize)82 static oslSecurityImpl * growSecurityImpl(
83 oslSecurityImpl * impl, std::size_t * bufSize)
84 {
85 std::size_t n = 0;
86 oslSecurityImpl * p = nullptr;
87 if (impl == nullptr) {
88 if (!sysconf_SC_GETPW_R_SIZE_MAX(&n)) {
89 /* choose something sensible (the callers of growSecurityImpl will
90 detect it if the allocated buffer is too small: */
91 n = 1024;
92 }
93 } else if (*bufSize <= std::numeric_limits<std::size_t>::max() / 2) {
94 n = 2 * *bufSize;
95 }
96 if (n != 0) {
97 if (n <= std::numeric_limits<std::size_t>::max()
98 - offsetof(oslSecurityImpl, m_buffer))
99 {
100 *bufSize = n;
101 n += offsetof(oslSecurityImpl, m_buffer);
102 } else {
103 *bufSize = std::numeric_limits<std::size_t>::max()
104 - offsetof(oslSecurityImpl, m_buffer);
105 n = std::numeric_limits<std::size_t>::max();
106 }
107 p = static_cast<oslSecurityImpl *>(realloc(impl, n));
108 memset (p, 0, n);
109 }
110 if (p == nullptr) {
111 free(impl);
112 }
113 return p;
114 }
115
deleteSecurityImpl(oslSecurityImpl * impl)116 static void deleteSecurityImpl(oslSecurityImpl * impl) {
117 free(impl);
118 }
119
osl_getCurrentSecurity()120 oslSecurity SAL_CALL osl_getCurrentSecurity()
121 {
122 std::size_t n = 0;
123 oslSecurityImpl * p = nullptr;
124 for (;;) {
125 struct passwd * found;
126 p = growSecurityImpl(p, &n);
127 if (p == nullptr) {
128 return nullptr;
129 }
130 #if defined(IOS) && defined(X86_64)
131 // getpwuid_r() does not work in the iOS simulator
132 (void) found;
133 char * buffer = p->m_buffer;
134 assert(n >= 100);
135 strcpy(buffer, "mobile");
136 p->m_pPasswd.pw_name = buffer;
137 buffer += strlen(buffer) + 1;
138 strcpy(buffer, "*");
139 p->m_pPasswd.pw_passwd = buffer;
140 buffer += strlen(buffer) + 1;
141 p->m_pPasswd.pw_uid = geteuid();
142 p->m_pPasswd.pw_gid = getegid();
143 p->m_pPasswd.pw_change = 0;
144 strcpy(buffer, "");
145 p->m_pPasswd.pw_class = buffer;
146 buffer += strlen(buffer) + 1;
147 strcpy(buffer, "Mobile User");
148 p->m_pPasswd.pw_gecos = buffer;
149 buffer += strlen(buffer) + 1;
150 strcpy(buffer, "/var/mobile"); // ???
151 p->m_pPasswd.pw_dir = buffer;
152 buffer += strlen(buffer) + 1;
153 strcpy(buffer, "");
154 p->m_pPasswd.pw_shell = buffer;
155 buffer += strlen(buffer) + 1;
156 p->m_pPasswd.pw_expire = 0;
157 return p;
158 #else
159 switch (getpwuid_r(getuid(), &p->m_pPasswd, p->m_buffer, n, &found)) {
160 case ERANGE:
161 break;
162 case 0:
163 if (found != nullptr) {
164 return p;
165 }
166 [[fallthrough]];
167 default:
168 deleteSecurityImpl(p);
169 return nullptr;
170 }
171 #endif
172 }
173 }
174
osl_loginUser(SAL_UNUSED_PARAMETER rtl_uString *,SAL_UNUSED_PARAMETER rtl_uString *,SAL_UNUSED_PARAMETER oslSecurity *)175 oslSecurityError SAL_CALL osl_loginUser(
176 SAL_UNUSED_PARAMETER rtl_uString *,
177 SAL_UNUSED_PARAMETER rtl_uString *,
178 SAL_UNUSED_PARAMETER oslSecurity *
179 )
180 {
181 return osl_Security_E_None;
182 }
183
osl_loginUserOnFileServer(SAL_UNUSED_PARAMETER rtl_uString *,SAL_UNUSED_PARAMETER rtl_uString *,SAL_UNUSED_PARAMETER rtl_uString *,SAL_UNUSED_PARAMETER oslSecurity *)184 oslSecurityError SAL_CALL osl_loginUserOnFileServer(
185 SAL_UNUSED_PARAMETER rtl_uString *,
186 SAL_UNUSED_PARAMETER rtl_uString *,
187 SAL_UNUSED_PARAMETER rtl_uString *,
188 SAL_UNUSED_PARAMETER oslSecurity *
189 )
190 {
191 return osl_Security_E_UserUnknown;
192 }
193
osl_getUserIdent(oslSecurity Security,rtl_uString ** ustrIdent)194 sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **ustrIdent)
195 {
196 bool bRet = false;
197 char pszIdent[1024];
198
199 pszIdent[0] = '\0';
200
201 bRet = osl_psz_getUserIdent(Security,pszIdent,sizeof(pszIdent));
202
203 rtl_string2UString( ustrIdent, pszIdent, rtl_str_getLength( pszIdent ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
204 SAL_WARN_IF(*ustrIdent == nullptr, "sal.osl", "*ustrIdent == NULL");
205
206 return bRet;
207 }
208
osl_psz_getUserIdent(oslSecurity Security,char * pszIdent,sal_uInt32 nMax)209 bool osl_psz_getUserIdent(oslSecurity Security, char *pszIdent, sal_uInt32 nMax)
210 {
211 char buffer[32];
212 sal_Int32 nChr;
213
214 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
215
216 if (pSecImpl == nullptr)
217 return false;
218
219 nChr = snprintf(buffer, sizeof(buffer), "%u", pSecImpl->m_pPasswd.pw_uid);
220 if ( nChr < 0 || sal::static_int_cast<sal_uInt32>(nChr) >= sizeof(buffer)
221 || sal::static_int_cast<sal_uInt32>(nChr) >= nMax )
222 return false; /* leave *pszIdent unmodified in case of failure */
223
224 memcpy(pszIdent, buffer, nChr+1);
225 return true;
226 }
227
osl_getUserName(oslSecurity Security,rtl_uString ** ustrName)228 sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **ustrName)
229 {
230 bool bRet = false;
231 char * pszName;
232 sal_Int32 len;
233
234 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
235
236 if (pSecImpl != nullptr && pSecImpl->m_pPasswd.pw_name != nullptr) {
237 pszName = pSecImpl->m_pPasswd.pw_name;
238 auto const n = std::strlen(pszName);
239 if (n <= o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
240 len = n;
241 bRet = true;
242 }
243 }
244
245 if (!bRet) {
246 pszName = nullptr;
247 len = 0;
248 }
249
250 rtl_string2UString( ustrName, pszName, len, osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
251 SAL_WARN_IF(*ustrName == nullptr, "sal.osl", "ustrName == NULL");
252
253 return bRet;
254 }
255
osl_getShortUserName(oslSecurity Security,rtl_uString ** ustrName)256 sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **ustrName)
257 {
258 return osl_getUserName(Security, ustrName); // No domain name on unix
259 }
260
osl_getHomeDir(oslSecurity Security,rtl_uString ** pustrDirectory)261 sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory)
262 {
263 bool bRet = false;
264 OString pszDirectory;
265
266 bRet = osl_psz_getHomeDir(Security,&pszDirectory);
267
268 if ( bRet )
269 {
270 rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
271 SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
272 osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
273 }
274
275 return bRet;
276 }
277
osl_psz_getHomeDir(oslSecurity Security,OString * pszDirectory)278 static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory)
279 {
280 assert(pszDirectory != nullptr);
281
282 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
283
284 if (pSecImpl == nullptr)
285 return false;
286
287 #ifdef HAIKU
288 dev_t volume = dev_for_path("/boot");
289 char homeDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
290 status_t result = find_directory(B_USER_DIRECTORY, volume, false, homeDir,
291 sizeof(homeDir));
292 if (result == B_OK) {
293 static_assert(
294 B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH <= std::numeric_limits<sal_Int32>::max());
295 *pszDirectory = OString(homeDir, std::strlen(homeDir));
296 return true;
297 }
298 return false;
299 #endif
300
301 #ifdef ANDROID
302 {
303 OUString pValue;
304
305 if (rtl::Bootstrap::get("HOME", pValue))
306 {
307 auto const pStrValue = OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
308 if (!pStrValue.isEmpty())
309 {
310 *pszDirectory = pStrValue;
311 return true;
312 }
313 }
314 }
315 #endif
316
317 #ifdef IOS
318 {
319 // Let's pretend the app-specific "Documents" directory is the home directory for now
320 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
321 NSString *userDirectory = [paths objectAtIndex:0];
322 auto const len = [userDirectory length];
323 if (len <= std::numeric_limits<sal_Int32>::max())
324 {
325 *pszDirectory = OString([userDirectory UTF8String], len);
326 return sal_True;
327 }
328 }
329 #endif
330
331 /* if current user, check also environment for HOME */
332 if (getuid() == pSecImpl->m_pPasswd.pw_uid)
333 {
334 char *pStr = nullptr;
335 #ifdef __sun
336 char buffer[8192];
337
338 struct passwd pwd;
339 struct passwd *ppwd;
340
341 #ifdef _POSIX_PTHREAD_SEMANTICS
342 if ( 0 != getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &ppwd ) )
343 ppwd = NULL;
344 #else
345 ppwd = getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer) );
346 #endif
347
348 if ( ppwd )
349 pStr = ppwd->pw_dir;
350 #else
351 pStr = getenv("HOME");
352 #endif
353
354 if (pStr != nullptr && pStr[0] != '\0' && access(pStr, 0) == 0)
355 {
356 auto const len = std::strlen(pStr);
357 if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
358 return false;
359 }
360 *pszDirectory = OString(pStr, len);
361 return true;
362 }
363 }
364 if (pSecImpl->m_pPasswd.pw_dir != nullptr)
365 {
366 auto const len = std::strlen(pSecImpl->m_pPasswd.pw_dir);
367 if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
368 return false;
369 }
370 *pszDirectory = OString(pSecImpl->m_pPasswd.pw_dir, len);
371 }
372 else
373 return false;
374
375 return true;
376 }
377
osl_getConfigDir(oslSecurity Security,rtl_uString ** pustrDirectory)378 sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory)
379 {
380 bool bRet = false;
381 OString pszDirectory;
382
383 bRet = osl_psz_getConfigDir(Security,&pszDirectory);
384
385 if ( bRet )
386 {
387 rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
388 SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
389 osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
390 }
391
392 return bRet;
393 }
394
395 #if defined HAIKU
396
osl_psz_getConfigDir(oslSecurity Security,OString * pszDirectory)397 static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
398 {
399 assert(pszDirectory != nullptr);
400 (void) Security;
401 dev_t volume = dev_for_path("/boot");
402 char configDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
403 status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, volume, false,
404 configDir, sizeof(configDir));
405 if (result == B_OK) {
406 auto const len = strlen(configDir);
407 if (len <= sal_uInt32(std::numeric_limits<sal_Int32>::max())) {
408 *pszDirectory = OString(configDir, len);
409 return true;
410 }
411 }
412 return false;
413 }
414
415 #elif !defined(MACOSX) && !defined(IOS)
416
osl_psz_getConfigDir(oslSecurity Security,OString * pszDirectory)417 static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
418 {
419 assert(pszDirectory != nullptr);
420
421 char *pStr = getenv("XDG_CONFIG_HOME");
422
423 if (pStr == nullptr || pStr[0] == '\0' || access(pStr, 0) != 0)
424 {
425 // a default equal to $HOME/.config should be used.
426 OString home;
427 if (!osl_psz_getHomeDir(Security, &home))
428 return false;
429 auto const config = OString(home + "/.config");
430
431 // try to create dir if not present
432 bool dirOK = true;
433 if (mkdir(config.getStr(), S_IRWXU) != 0)
434 {
435 int e = errno;
436 if (e != EEXIST)
437 {
438 SAL_WARN(
439 "sal.osl",
440 "mkdir(" << config << "): errno=" << e);
441 dirOK = false;
442 }
443 }
444 if (dirOK)
445 {
446 // check file type and permissions
447 struct stat st;
448 if (stat(config.getStr(), &st) != 0)
449 {
450 SAL_INFO("sal.osl","Could not stat $HOME/.config");
451 dirOK = false;
452 }
453 else
454 {
455 if (!S_ISDIR(st.st_mode))
456 {
457 SAL_INFO("sal.osl", "$HOME/.config is not a directory");
458 dirOK = false;
459 }
460 if (!(st.st_mode & S_IRUSR && st.st_mode & S_IWUSR && st.st_mode & S_IXUSR))
461 {
462 SAL_INFO("sal.osl", "$HOME/.config has bad permissions");
463 dirOK = false;
464 }
465 }
466 }
467
468 // if !dirOK, resort to HOME
469 if (dirOK)
470 home = config;
471 *pszDirectory = home;
472 }
473 else
474 {
475 auto const len = std::strlen(pStr);
476 if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
477 return false;
478 }
479 *pszDirectory = OString(pStr, len);
480 }
481
482 return true;
483 }
484
485 #else
486
487 /*
488 * FIXME: rewrite to use more flexible
489 * NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
490 * as soon as we can bump the baseline to Tiger (for NSApplicationSupportDirectory) and have
491 * support for Objective-C in the build environment
492 */
493
osl_psz_getConfigDir(oslSecurity Security,OString * pszDirectory)494 static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
495 {
496 assert(pszDirectory != nullptr);
497
498 OString home;
499 if( osl_psz_getHomeDir(Security, &home) )
500 {
501 *pszDirectory = home + "/Library/Application Support"; /* Used on iOS, too */
502 return true;
503 }
504
505 return false;
506 }
507
508 #endif
509
osl_isAdministrator(oslSecurity Security)510 sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security)
511 {
512 oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
513
514 if (pSecImpl == nullptr)
515 return false;
516
517 if (pSecImpl->m_pPasswd.pw_uid != 0)
518 return false;
519
520 return true;
521 }
522
osl_freeSecurityHandle(oslSecurity Security)523 void SAL_CALL osl_freeSecurityHandle(oslSecurity Security)
524 {
525 deleteSecurityImpl(static_cast<oslSecurityImpl *>(Security));
526 }
527
osl_loadUserProfile(SAL_UNUSED_PARAMETER oslSecurity)528 sal_Bool SAL_CALL osl_loadUserProfile(SAL_UNUSED_PARAMETER oslSecurity)
529 {
530 return false;
531 }
532
osl_unloadUserProfile(SAL_UNUSED_PARAMETER oslSecurity)533 void SAL_CALL osl_unloadUserProfile(SAL_UNUSED_PARAMETER oslSecurity) {}
534
535 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
536