Coverage Report

Created: 2025-11-16 09:57

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