Flow-IPC 1.0.1
Flow-IPC project: Full implementation reference.
app.cpp
Go to the documentation of this file.
1/* Flow-IPC: Sessions
2 * Copyright 2023 Akamai Technologies, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the
5 * "License"); you may not use this file except in
6 * compliance with the License. You may obtain a copy
7 * of the License at
8 *
9 * https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in
12 * writing, software distributed under the License is
13 * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
14 * CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing
16 * permissions and limitations under the License. */
17
18/// @file
19#include "ipc/session/app.hpp"
21#include "ipc/session/error.hpp"
23#include <flow/error/error.hpp>
24#include <sys/stat.h>
25
26namespace ipc::session
27{
28
29// Implementations.
30
31void ensure_resource_owner_is_app(flow::log::Logger* logger_ptr, const fs::path& path, const App& app,
32 Error_code* err_code)
33{
35 using boost::system::system_category;
36 using ::open;
37 using ::close;
38 // using ::O_PATH; // It's a macro apparently.
39 // using ::errno; // It's a macro apparently.
40
41 if (flow::error::exec_void_and_throw_on_error
42 ([&](Error_code* actual_err_code)
43 { ensure_resource_owner_is_app(logger_ptr, path, app, actual_err_code); },
44 err_code, "session::ensure_resource_owner_is_app(1)"))
45 {
46 return;
47 }
48 // else
49
50 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_SESSION);
51
52 /* We can either reuse the handle-based overload, or we can do stat() instead of fstat(). The former seems nicer;
53 * though we'll have to open a handle temporarily. I've seen boost.interprocess do it fairly casually for
54 * similar purposes, so why not. The O_PATH flag (which opens the resource just for this purpose -- not for
55 * I/O) is perfect for it, particularly since the resource might not be a file but a SHM pool, etc. */
56
57 int native_handle = open(path.c_str(), O_PATH);
58 if (native_handle == -1)
59 {
60 *err_code = Error_code(errno, system_category());
61 FLOW_LOG_WARNING("Tried to check ownership of resource at [" << path << "] but while obtaining info-only handle "
62 "encountered error [" << *err_code << "] [" << err_code->message() << "]; unable to check.");
63 return;
64 }
65 // else
66 Native_handle handle(native_handle);
67
68 // For nicer messaging add some more logging on error. A little code duplication, but it's OK.
69 ensure_resource_owner_is_app(logger_ptr, handle, app, err_code);
70 if (*err_code)
71 {
72 FLOW_LOG_WARNING("Check of ownership of resource at [" << path << "], upon successfully opening probe-only "
73 "descriptor/handle, resulted in error in checking or "
74 "unexpected ownership; see preceding WARNING referencing all other details.");
75 }
76
77 close(native_handle);
78} // ensure_resource_owner_is_app(1)
79
80void ensure_resource_owner_is_app(flow::log::Logger* logger_ptr, util::Native_handle handle, const App& app,
81 Error_code* err_code)
82{
83 using boost::system::system_category;
84 using ::fstat;
85 /* using Stats = struct ::stat; // This worked at coliru.com but not here; not sure why.
86 * // The crux of this whole thing is that struct ::stat() and ::stat()
87 * // have the same name. Blech. Just specify it explicitly below, C-style. */
88 // using ::errno; // It's a macro apparently.
89
90 if (flow::error::exec_void_and_throw_on_error
91 ([&](Error_code* actual_err_code)
92 { ensure_resource_owner_is_app(logger_ptr, handle, app, actual_err_code); },
93 err_code, "session::ensure_resource_owner_is_app(2)"))
94 {
95 return;
96 }
97 // else
98
99 assert((!handle.null()) && "Disallowed per contract.");
100
101 FLOW_LOG_SET_CONTEXT(logger_ptr, Log_component::S_SESSION);
102
103 // There's no Boost.filesystem or STL way to get UID/GID, that I know of (even via path); so use POSIX-y thing.
104 struct ::stat stats;
105 const auto rc = fstat(handle.m_native_handle, &stats);
106 if (rc == -1)
107 {
108 *err_code = Error_code(errno, system_category());
109 FLOW_LOG_WARNING("Tried to check ownership via descriptor/handle [" << handle << "] but encountered "
110 "error [" << *err_code << "] [" << err_code->message() << "]; unable to check.");
111 }
112 else if ((stats.st_uid != app.m_user_id) || (stats.st_gid != app.m_group_id))
113 {
115 FLOW_LOG_WARNING("Checked ownership via descriptor/handle [" << handle << "] but encountered "
116 "error [" << *err_code << "] [" << err_code->message() << "]; unable to check.");
117 }
118 else
119 {
120 err_code->clear();
121 }
122} // ensure_resource_owner_is_app(2)
123
124std::ostream& operator<<(std::ostream& os, const App& val)
125{
126 return os << '[' << val.m_name << "] "
127 "exec[" << val.m_exec_path << "] user[" << val.m_user_id << ':' << val.m_group_id << ']';
128}
129std::ostream& operator<<(std::ostream& os, const Client_app& val)
130{
131 return os << static_cast<const App&>(val);
132}
133
134std::ostream& operator<<(std::ostream& os, const Server_app& val)
135{
136 using boost::algorithm::join;
137
138 return os << static_cast<const App&>(val) << " allowed_cli_apps[" << join(val.m_allowed_client_apps, " ") << ']';
139}
140
141} // namespace ipc::session
@ S_RESOURCE_OWNER_UNEXPECTED
A resource in the file system (file, SHM pool, MQ, etc.) has or could have unexpected owner; ipc::ses...
Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...
Definition: app.cpp:27
void ensure_resource_owner_is_app(flow::log::Logger *logger_ptr, const fs::path &path, const App &app, Error_code *err_code)
Utility, used internally but exposed in public API in case it is of general use, that checks that the...
Definition: app.cpp:31
std::ostream & operator<<(std::ostream &os, const App &val)
Prints string representation of the given App to the given ostream.
Definition: app.cpp:124
util::Native_handle Native_handle
Convenience alias for the commonly used type util::Native_handle.
flow::Error_code Error_code
Short-hand for flow::Error_code which is very common.
Definition: common.hpp:297
A description of an application in this ipc::session inter-process communication universe.
Definition: app.hpp:78
util::group_id_t m_group_id
The application must run as this group ID (GID). Files and other shared resources shall have this own...
Definition: app.hpp:171
std::string m_name
Brief application name, readable to humans and unique across all other applications' names; used both...
Definition: app.hpp:144
util::user_id_t m_user_id
The application must run as this user ID (UID). Files and other shared resources shall have this owne...
Definition: app.hpp:168
fs::path m_exec_path
Absolute, lexically normalized canonical path to the executable entity (which is not a directory),...
Definition: app.hpp:165
An App that is used as a client in at least one client-server IPC split.
Definition: app.hpp:185
An App that is used as a server in at least one client-server IPC split.
Definition: app.hpp:206
Client_app_set m_allowed_client_apps
A given Client_app (as identified by its distinct App::m_name) may request to establish an IPC sessio...
Definition: app.hpp:222
A monolayer-thin wrapper around a native handle, a/k/a descriptor a/k/a FD.
bool null() const
Returns true if and only if m_native_handle equals S_NULL_HANDLE.
handle_t m_native_handle
The native handle (possibly equal to S_NULL_HANDLE), the exact payload of this Native_handle.