office-gobmx/sal/osl/unx/security.cxx
Jan-Marek Glogowski ad5563b4f2 WASM setup as non-multiuser, desktop build
Change-Id: I2ee6e006554cf1e5e5e42c2f4f73d8788bff8f4b
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/128585
Tested-by: Jenkins
Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
2022-01-19 10:50:35 +01:00

537 lines
15 KiB
C++

/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <sal/config.h>
#include <cassert>
#include <cstddef>
#include <cstring>
#include <limits>
#ifdef IOS
#include <premac.h>
#import <Foundation/Foundation.h>
#include <postmac.h>
#endif
#include "system.hxx"
#include <o3tl/safeint.hxx>
#include <osl/security.h>
#include <rtl/bootstrap.hxx>
#include <sal/log.hxx>
#include <osl/thread.h>
#include <osl/file.h>
#if defined LINUX || defined __sun
#include <crypt.h>
#endif
#if defined HAIKU
#include <fs_info.h>
#include <FindDirectory.h>
#endif
#include "secimpl.hxx"
#ifdef ANDROID
#define getpwuid_r(uid, pwd, buf, buflen, result) (*(result) = getpwuid(uid), (*(result) ? (memcpy (buf, *(result), sizeof (struct passwd)), 0) : errno))
#endif
static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory);
static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory);
static bool sysconf_SC_GETPW_R_SIZE_MAX(std::size_t * value) {
#if defined _SC_GETPW_R_SIZE_MAX
long m;
errno = 0;
m = sysconf(_SC_GETPW_R_SIZE_MAX);
if (m == -1) {
/* _SC_GETPW_R_SIZE_MAX has no limit; some platforms like certain
FreeBSD versions support sysconf(_SC_GETPW_R_SIZE_MAX) in a broken
way and always set EINVAL, so be resilient here: */
return false;
}
SAL_WARN_IF( m < 0 || o3tl::make_unsigned(m) >= std::numeric_limits<std::size_t>::max(), "sal.osl",
"m < 0 || (unsigned long) m >= std::numeric_limits<std::size_t>::max()");
*value = static_cast<std::size_t>(m);
return true;
#else
/* some platforms like macOS 1.3 do not define _SC_GETPW_R_SIZE_MAX: */
return false;
#endif
}
static oslSecurityImpl * growSecurityImpl(
oslSecurityImpl * impl, std::size_t * bufSize)
{
std::size_t n = 0;
oslSecurityImpl * p = nullptr;
if (impl == nullptr) {
if (!sysconf_SC_GETPW_R_SIZE_MAX(&n)) {
/* choose something sensible (the callers of growSecurityImpl will
detect it if the allocated buffer is too small: */
n = 1024;
}
} else if (*bufSize <= std::numeric_limits<std::size_t>::max() / 2) {
n = 2 * *bufSize;
}
if (n != 0) {
if (n <= std::numeric_limits<std::size_t>::max()
- offsetof(oslSecurityImpl, m_buffer))
{
*bufSize = n;
n += offsetof(oslSecurityImpl, m_buffer);
} else {
*bufSize = std::numeric_limits<std::size_t>::max()
- offsetof(oslSecurityImpl, m_buffer);
n = std::numeric_limits<std::size_t>::max();
}
p = static_cast<oslSecurityImpl *>(realloc(impl, n));
memset (p, 0, n);
}
if (p == nullptr) {
free(impl);
}
return p;
}
static void deleteSecurityImpl(oslSecurityImpl * impl) {
free(impl);
}
oslSecurity SAL_CALL osl_getCurrentSecurity()
{
std::size_t n = 0;
oslSecurityImpl * p = nullptr;
for (;;) {
struct passwd * found;
p = growSecurityImpl(p, &n);
if (p == nullptr) {
return nullptr;
}
#if (defined(IOS) && defined(X86_64)) || defined(EMSCRIPTEN)
// getpwuid_r() does not work in the iOS simulator
(void) found;
char * buffer = p->m_buffer;
assert(n >= 100);
strcpy(buffer, "mobile");
p->m_pPasswd.pw_name = buffer;
buffer += strlen(buffer) + 1;
strcpy(buffer, "*");
p->m_pPasswd.pw_passwd = buffer;
buffer += strlen(buffer) + 1;
p->m_pPasswd.pw_uid = geteuid();
p->m_pPasswd.pw_gid = getegid();
#if !defined(EMSCRIPTEN)
p->m_pPasswd.pw_change = 0;
strcpy(buffer, "");
p->m_pPasswd.pw_class = buffer;
buffer += strlen(buffer) + 1;
p->m_pPasswd.pw_expire = 0;
#endif
strcpy(buffer, "Mobile User");
p->m_pPasswd.pw_gecos = buffer;
buffer += strlen(buffer) + 1;
strcpy(buffer, "/var/mobile"); // ???
p->m_pPasswd.pw_dir = buffer;
buffer += strlen(buffer) + 1;
strcpy(buffer, "");
p->m_pPasswd.pw_shell = buffer;
buffer += strlen(buffer) + 1;
return p;
#else
switch (getpwuid_r(getuid(), &p->m_pPasswd, p->m_buffer, n, &found)) {
case ERANGE:
break;
case 0:
if (found != nullptr) {
return p;
}
[[fallthrough]];
default:
deleteSecurityImpl(p);
return nullptr;
}
#endif
}
}
oslSecurityError SAL_CALL osl_loginUser(
SAL_UNUSED_PARAMETER rtl_uString *,
SAL_UNUSED_PARAMETER rtl_uString *,
SAL_UNUSED_PARAMETER oslSecurity *
)
{
return osl_Security_E_None;
}
oslSecurityError SAL_CALL osl_loginUserOnFileServer(
SAL_UNUSED_PARAMETER rtl_uString *,
SAL_UNUSED_PARAMETER rtl_uString *,
SAL_UNUSED_PARAMETER rtl_uString *,
SAL_UNUSED_PARAMETER oslSecurity *
)
{
return osl_Security_E_UserUnknown;
}
sal_Bool SAL_CALL osl_getUserIdent(oslSecurity Security, rtl_uString **ustrIdent)
{
bool bRet = false;
char pszIdent[1024];
pszIdent[0] = '\0';
bRet = osl_psz_getUserIdent(Security,pszIdent,sizeof(pszIdent));
rtl_string2UString( ustrIdent, pszIdent, rtl_str_getLength( pszIdent ), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
SAL_WARN_IF(*ustrIdent == nullptr, "sal.osl", "*ustrIdent == NULL");
return bRet;
}
bool osl_psz_getUserIdent(oslSecurity Security, char *pszIdent, sal_uInt32 nMax)
{
char buffer[32];
sal_Int32 nChr;
oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
if (pSecImpl == nullptr)
return false;
nChr = snprintf(buffer, sizeof(buffer), "%u", pSecImpl->m_pPasswd.pw_uid);
if ( nChr < 0 || sal::static_int_cast<sal_uInt32>(nChr) >= sizeof(buffer)
|| sal::static_int_cast<sal_uInt32>(nChr) >= nMax )
return false; /* leave *pszIdent unmodified in case of failure */
memcpy(pszIdent, buffer, nChr+1);
return true;
}
sal_Bool SAL_CALL osl_getUserName(oslSecurity Security, rtl_uString **ustrName)
{
bool bRet = false;
char * pszName;
sal_Int32 len;
oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
if (pSecImpl != nullptr && pSecImpl->m_pPasswd.pw_name != nullptr) {
pszName = pSecImpl->m_pPasswd.pw_name;
auto const n = std::strlen(pszName);
if (n <= o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
len = n;
bRet = true;
}
}
if (!bRet) {
pszName = nullptr;
len = 0;
}
rtl_string2UString( ustrName, pszName, len, osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
SAL_WARN_IF(*ustrName == nullptr, "sal.osl", "ustrName == NULL");
return bRet;
}
sal_Bool SAL_CALL osl_getShortUserName(oslSecurity Security, rtl_uString **ustrName)
{
return osl_getUserName(Security, ustrName); // No domain name on unix
}
sal_Bool SAL_CALL osl_getHomeDir(oslSecurity Security, rtl_uString **pustrDirectory)
{
bool bRet = false;
OString pszDirectory;
bRet = osl_psz_getHomeDir(Security,&pszDirectory);
if ( bRet )
{
rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
}
return bRet;
}
static bool osl_psz_getHomeDir(oslSecurity Security, OString* pszDirectory)
{
assert(pszDirectory != nullptr);
oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
if (pSecImpl == nullptr)
return false;
#ifdef HAIKU
dev_t volume = dev_for_path("/boot");
char homeDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
status_t result = find_directory(B_USER_DIRECTORY, volume, false, homeDir,
sizeof(homeDir));
if (result == B_OK) {
static_assert(
B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH <= std::numeric_limits<sal_Int32>::max());
*pszDirectory = OString(homeDir, std::strlen(homeDir));
return true;
}
return false;
#endif
#ifdef ANDROID
{
OUString pValue;
if (rtl::Bootstrap::get("HOME", pValue))
{
auto const pStrValue = OUStringToOString(pValue, RTL_TEXTENCODING_UTF8);
if (!pStrValue.isEmpty())
{
*pszDirectory = pStrValue;
return true;
}
}
}
#endif
#ifdef IOS
{
// Let's pretend the app-specific "Documents" directory is the home directory for now
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *userDirectory = [paths objectAtIndex:0];
auto const len = [userDirectory length];
if (len <= std::numeric_limits<sal_Int32>::max())
{
*pszDirectory = OString([userDirectory UTF8String], len);
return sal_True;
}
}
#endif
/* if current user, check also environment for HOME */
if (getuid() == pSecImpl->m_pPasswd.pw_uid)
{
char *pStr = nullptr;
#ifdef __sun
char buffer[8192];
struct passwd pwd;
struct passwd *ppwd;
#ifdef _POSIX_PTHREAD_SEMANTICS
if ( 0 != getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer), &ppwd ) )
ppwd = NULL;
#else
ppwd = getpwuid_r(getuid(), &pwd, buffer, sizeof(buffer) );
#endif
if ( ppwd )
pStr = ppwd->pw_dir;
#else
pStr = getenv("HOME");
#endif
if (pStr != nullptr && pStr[0] != '\0' && access(pStr, 0) == 0)
{
auto const len = std::strlen(pStr);
if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
return false;
}
*pszDirectory = OString(pStr, len);
return true;
}
}
if (pSecImpl->m_pPasswd.pw_dir != nullptr)
{
auto const len = std::strlen(pSecImpl->m_pPasswd.pw_dir);
if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
return false;
}
*pszDirectory = OString(pSecImpl->m_pPasswd.pw_dir, len);
}
else
return false;
return true;
}
sal_Bool SAL_CALL osl_getConfigDir(oslSecurity Security, rtl_uString **pustrDirectory)
{
bool bRet = false;
OString pszDirectory;
bRet = osl_psz_getConfigDir(Security,&pszDirectory);
if ( bRet )
{
rtl_string2UString( pustrDirectory, pszDirectory.getStr(), pszDirectory.getLength(), osl_getThreadTextEncoding(), OSTRING_TO_OUSTRING_CVTFLAGS );
SAL_WARN_IF(*pustrDirectory == nullptr, "sal.osl", "*pustrDirectory == NULL");
osl_getFileURLFromSystemPath( *pustrDirectory, pustrDirectory );
}
return bRet;
}
#if defined HAIKU
static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
{
assert(pszDirectory != nullptr);
(void) Security;
dev_t volume = dev_for_path("/boot");
char configDir[B_PATH_NAME_LENGTH + B_FILE_NAME_LENGTH];
status_t result = find_directory(B_USER_SETTINGS_DIRECTORY, volume, false,
configDir, sizeof(configDir));
if (result == B_OK) {
auto const len = strlen(configDir);
if (len <= sal_uInt32(std::numeric_limits<sal_Int32>::max())) {
*pszDirectory = OString(configDir, len);
return true;
}
}
return false;
}
#elif !defined(MACOSX) && !defined(IOS)
static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
{
assert(pszDirectory != nullptr);
char *pStr = getenv("XDG_CONFIG_HOME");
if (pStr == nullptr || pStr[0] == '\0' || access(pStr, 0) != 0)
{
// a default equal to $HOME/.config should be used.
OString home;
if (!osl_psz_getHomeDir(Security, &home))
return false;
auto const config = OString(home + "/.config");
// try to create dir if not present
bool dirOK = true;
if (mkdir(config.getStr(), S_IRWXU) != 0)
{
int e = errno;
if (e != EEXIST)
{
SAL_WARN(
"sal.osl",
"mkdir(" << config << "): errno=" << e);
dirOK = false;
}
}
if (dirOK)
{
// check file type and permissions
struct stat st;
if (stat(config.getStr(), &st) != 0)
{
SAL_INFO("sal.osl","Could not stat $HOME/.config");
dirOK = false;
}
else
{
if (!S_ISDIR(st.st_mode))
{
SAL_INFO("sal.osl", "$HOME/.config is not a directory");
dirOK = false;
}
if (!(st.st_mode & S_IRUSR && st.st_mode & S_IWUSR && st.st_mode & S_IXUSR))
{
SAL_INFO("sal.osl", "$HOME/.config has bad permissions");
dirOK = false;
}
}
}
// if !dirOK, resort to HOME
if (dirOK)
home = config;
*pszDirectory = home;
}
else
{
auto const len = std::strlen(pStr);
if (len > o3tl::make_unsigned(std::numeric_limits<sal_Int32>::max())) {
return false;
}
*pszDirectory = OString(pStr, len);
}
return true;
}
#else
/*
* FIXME: rewrite to use more flexible
* NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES)
* as soon as we can bump the baseline to Tiger (for NSApplicationSupportDirectory) and have
* support for Objective-C in the build environment
*/
static bool osl_psz_getConfigDir(oslSecurity Security, OString* pszDirectory)
{
assert(pszDirectory != nullptr);
OString home;
if( osl_psz_getHomeDir(Security, &home) )
{
*pszDirectory = home + "/Library/Application Support"; /* Used on iOS, too */
return true;
}
return false;
}
#endif
sal_Bool SAL_CALL osl_isAdministrator(oslSecurity Security)
{
oslSecurityImpl *pSecImpl = static_cast<oslSecurityImpl *>(Security);
if (pSecImpl == nullptr)
return false;
if (pSecImpl->m_pPasswd.pw_uid != 0)
return false;
return true;
}
void SAL_CALL osl_freeSecurityHandle(oslSecurity Security)
{
deleteSecurityImpl(static_cast<oslSecurityImpl *>(Security));
}
sal_Bool SAL_CALL osl_loadUserProfile(SAL_UNUSED_PARAMETER oslSecurity)
{
return false;
}
void SAL_CALL osl_unloadUserProfile(SAL_UNUSED_PARAMETER oslSecurity) {}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */