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