1/*
2 * Copyright (C) 2013 Intel Corporation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "OpenSyscall.h"
28
29#if ENABLE(SECCOMP_FILTERS)
30
31#include "ArgumentCoders.h"
32#include "SyscallPolicy.h"
33#include <errno.h>
34#include <fcntl.h>
35#include <seccomp.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <unistd.h>
39#include <wtf/text/WTFString.h>
40
41namespace WebKit {
42
43COMPILE_ASSERT(!O_RDONLY, O_RDONLY);
44COMPILE_ASSERT(O_WRONLY == 1, O_WRONLY);
45COMPILE_ASSERT(O_RDWR == 2, O_RDWR);
46
47std::unique_ptr<Syscall> OpenSyscall::createFromOpenatContext(mcontext_t* context)
48{
49    auto open = std::make_unique<OpenSyscall>(nullptr);
50
51    open->setFlags(context->gregs[REG_ARG2]);
52    open->setMode(context->gregs[REG_ARG3]);
53    open->setContext(context);
54
55    int fd = context->gregs[REG_ARG0];
56    char* path = reinterpret_cast<char*>(context->gregs[REG_ARG1]);
57
58    if (path[0] == '/') {
59        open->setPath(path);
60        return WTF::move(open);
61    }
62
63    struct stat pathStat;
64    if (fstat(fd, &pathStat) == -1) {
65        context->gregs[REG_SYSCALL] = -errno;
66        return nullptr;
67    }
68
69    if (!S_ISDIR(pathStat.st_mode)) {
70        context->gregs[REG_SYSCALL] = -ENOTDIR;
71        return nullptr;
72    }
73
74    char fdLinkPath[32];
75    snprintf(fdLinkPath, sizeof(fdLinkPath), "/proc/self/fd/%d", fd);
76
77    char fdPath[PATH_MAX];
78    ssize_t size = readlink(fdLinkPath, fdPath, sizeof(fdPath) - 1);
79    if (size == -1) {
80        context->gregs[REG_SYSCALL] = -errno;
81        return nullptr;
82    }
83
84    // The "+ 2" here stands for the '/' and null terminator.
85    if (size + strlen(path) + 2 > PATH_MAX) {
86        context->gregs[REG_SYSCALL] = -ENAMETOOLONG;
87        return nullptr;
88    }
89
90    sprintf(&fdPath[size], "/%s", path);
91    open->setPath(fdPath);
92
93    return WTF::move(open);
94}
95
96std::unique_ptr<Syscall> OpenSyscall::createFromCreatContext(mcontext_t* context)
97{
98    auto open = std::make_unique<OpenSyscall>(nullptr);
99
100    open->setPath(CString(reinterpret_cast<char*>(context->gregs[REG_ARG0])));
101    open->setFlags(O_CREAT | O_WRONLY | O_TRUNC);
102    open->setMode(context->gregs[REG_ARG1]);
103    open->setContext(context);
104
105    return WTF::move(open);
106}
107
108OpenSyscall::OpenSyscall(mcontext_t* context)
109    : Syscall(__NR_open, context)
110    , m_flags(0)
111    , m_mode(0)
112{
113    if (!context)
114        return;
115
116    m_path = CString(reinterpret_cast<char*>(context->gregs[REG_ARG0]));
117    m_flags = context->gregs[REG_ARG1];
118    m_mode = context->gregs[REG_ARG2];
119}
120
121void OpenSyscall::setResult(const SyscallResult* result)
122{
123    ASSERT(context() && result->type() == type());
124
125    const OpenSyscallResult* openResult = static_cast<const OpenSyscallResult*>(result);
126
127    if (openResult->fd() >= 0)
128        context()->gregs[REG_SYSCALL] = dup(openResult->fd());
129    else
130        context()->gregs[REG_SYSCALL] = -openResult->errorNumber();
131}
132
133std::unique_ptr<SyscallResult> OpenSyscall::execute(const SyscallPolicy& policy)
134{
135    if (!strncmp("/proc/self/", m_path.data(), 11)) {
136        String resolvedSelfPath = ASCIILiteral("/proc/") + String::number(getppid()) + &m_path.data()[10];
137        m_path = resolvedSelfPath.utf8().data();
138    }
139
140    SyscallPolicy::Permission permission = SyscallPolicy::NotAllowed;
141    if (m_flags & O_RDWR)
142        permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::ReadAndWrite);
143    else if (m_flags & O_WRONLY)
144        permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::Write);
145    else
146        permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::Read);
147
148    // Create a file implies write permission on the directory.
149    if (m_flags & O_CREAT || m_flags & O_EXCL)
150        permission = static_cast<SyscallPolicy::Permission>(permission | SyscallPolicy::Write);
151
152    if (!policy.hasPermissionForPath(m_path.data(), permission))
153        return std::make_unique<OpenSyscallResult>(-1, EACCES);
154
155    // Permission granted, execute the syscall. The syscall might still
156    // fail because of hard permissions enforced by the filesystem and
157    // things like if the entry does not exist.
158    int fd = open(m_path.data(), m_flags, m_mode);
159    int errorNumber = fd == -1 ? errno : 0;
160
161    return std::make_unique<OpenSyscallResult>(fd, errorNumber);
162}
163
164void OpenSyscall::encode(IPC::ArgumentEncoder& encoder) const
165{
166    encoder << type();
167    encoder << m_path;
168    encoder << m_flags;
169    encoder << m_mode;
170}
171
172bool OpenSyscall::decode(IPC::ArgumentDecoder* decoder)
173{
174    // m_type already decoded by the parent class.
175
176    if (!decoder->decode(m_path))
177        return false;
178    if (!decoder->decode(m_flags))
179        return false;
180
181    return decoder->decode(m_mode);
182}
183
184OpenSyscallResult::OpenSyscallResult(int fd, int errorNumber)
185    : SyscallResult(__NR_open)
186    , m_fd(fd)
187    , m_errorNumber(errorNumber)
188{
189}
190
191OpenSyscallResult::~OpenSyscallResult()
192{
193    if (m_fd >= 0)
194        close(m_fd);
195}
196
197void OpenSyscallResult::encode(IPC::ArgumentEncoder& encoder) const
198{
199    encoder << type();
200
201    if (m_fd >= 0) {
202        IPC::Attachment attachment(m_fd);
203        encoder.addAttachment(attachment);
204    }
205
206    encoder << m_errorNumber;
207}
208
209bool OpenSyscallResult::decode(IPC::ArgumentDecoder* decoder, int fd)
210{
211    if (fd >= 0)
212        m_fd = fd;
213
214    return decoder->decode(m_errorNumber);
215}
216
217} // namespace WebKit
218
219#endif // ENABLE(SECCOMP_FILTERS)
220