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