• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *             Copyright Andrey Semashev 2016.
3  * Distributed under the Boost Software License, Version 1.0.
4  *    (See accompanying file LICENSE_1_0.txt or copy at
5  *          http://www.boost.org/LICENSE_1_0.txt)
6  */
7 /*!
8  * \file   windows/object_name.cpp
9  * \author Andrey Semashev
10  * \date   06.03.2016
11  *
12  * \brief  This header is the Boost.Log library implementation, see the library documentation
13  *         at http://www.boost.org/doc/libs/release/libs/log/doc/html/index.html.
14  */
15 
16 #include <boost/log/detail/config.hpp>
17 #include <cstddef>
18 #include <cstdlib>
19 #include <string>
20 #include <vector>
21 #include <algorithm>
22 #include <boost/memory_order.hpp>
23 #include <boost/atomic/atomic.hpp>
24 #include <boost/move/utility_core.hpp>
25 #include <boost/log/exceptions.hpp>
26 #include <boost/log/utility/ipc/object_name.hpp>
27 #include <boost/winapi/get_last_error.hpp>
28 #include <windows.h>
29 #include <lmcons.h>
30 #include <security.h>
31 #include "windows/auto_handle.hpp"
32 #include "windows/utf_code_conversion.hpp"
33 #include <boost/log/detail/header.hpp>
34 
35 namespace boost {
36 
37 BOOST_LOG_OPEN_NAMESPACE
38 
39 namespace ipc {
40 
41 BOOST_LOG_ANONYMOUS_NAMESPACE {
42 
43 #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
44 
45 class auto_boundary_descriptor
46 {
47 private:
48     HANDLE m_handle;
49 
50 public:
51     explicit auto_boundary_descriptor(HANDLE h = NULL) BOOST_NOEXCEPT : m_handle(h)
52     {
53     }
54 
55     ~auto_boundary_descriptor() BOOST_NOEXCEPT
56     {
57         if (m_handle)
58             DeleteBoundaryDescriptor(m_handle);
59     }
60 
61     void init(HANDLE h) BOOST_NOEXCEPT
62     {
63         BOOST_ASSERT(m_handle == NULL);
64         m_handle = h;
65     }
66 
67     HANDLE get() const BOOST_NOEXCEPT { return m_handle; }
68     HANDLE* get_ptr() BOOST_NOEXCEPT { return &m_handle; }
69 
70     void swap(auto_boundary_descriptor& that) BOOST_NOEXCEPT
71     {
72         HANDLE h = m_handle;
73         m_handle = that.m_handle;
74         that.m_handle = h;
75     }
76 
77     BOOST_DELETED_FUNCTION(auto_boundary_descriptor(auto_boundary_descriptor const&))
78     BOOST_DELETED_FUNCTION(auto_boundary_descriptor& operator=(auto_boundary_descriptor const&))
79 };
80 
81 //! Handle for the private namespace for \c user scope
82 static boost::atomic< HANDLE > g_user_private_namespace;
83 
84 //! Closes the private namespace on process exit
85 void close_user_namespace()
86 {
87     HANDLE h = g_user_private_namespace.load(boost::memory_order_acquire);
88     if (h)
89     {
90         ClosePrivateNamespace(h, 0);
91         g_user_private_namespace.store((HANDLE)NULL, boost::memory_order_release);
92     }
93 }
94 
95 //! Attempts to create or open the private namespace
96 bool init_user_namespace()
97 {
98     HANDLE h = g_user_private_namespace.load(boost::memory_order_acquire);
99     if (BOOST_UNLIKELY(!h))
100     {
101         // Obtain the current user SID
102         boost::log::ipc::aux::auto_handle h_process_token;
103         if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, h_process_token.get_ptr()))
104             return false;
105 
106         DWORD token_user_size = 0;
107         GetTokenInformation(h_process_token.get(), TokenUser, NULL, 0u, &token_user_size);
108         if (GetLastError() != ERROR_INSUFFICIENT_BUFFER || token_user_size < sizeof(TOKEN_USER))
109             return false;
110         std::vector< unsigned char > token_user_storage(static_cast< std::size_t >(token_user_size), static_cast< unsigned char >(0u));
111         if (!GetTokenInformation(h_process_token.get(), TokenUser, &token_user_storage[0], token_user_size, &token_user_size))
112             return false;
113 
114         TOKEN_USER const& token_user = *reinterpret_cast< const TOKEN_USER* >(&token_user_storage[0]);
115         if (!token_user.User.Sid)
116             return false;
117 
118         // Create a boundary descriptor with the user's SID
119         auto_boundary_descriptor h_boundary(CreateBoundaryDescriptorW(L"User", 0));
120         if (!h_boundary.get())
121             return false;
122 
123         if (!AddSIDToBoundaryDescriptor(h_boundary.get_ptr(), token_user.User.Sid))
124             return false;
125 
126         // Create or open a namespace for kernel objects
127         h = CreatePrivateNamespaceW(NULL, h_boundary.get(), L"User");
128         if (!h)
129             h = OpenPrivateNamespaceW(h_boundary.get(), L"User");
130 
131         if (h)
132         {
133             HANDLE expected = NULL;
134             if (g_user_private_namespace.compare_exchange_strong(expected, h, boost::memory_order_acq_rel, boost::memory_order_acquire))
135             {
136                 std::atexit(&close_user_namespace);
137             }
138             else
139             {
140                 // Another thread must have opened the namespace
141                 ClosePrivateNamespace(h, 0);
142                 h = expected;
143             }
144         }
145     }
146 
147     return !!h;
148 }
149 
150 #endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
151 
152 //! Returns a prefix string for a shared resource according to the scope
153 std::string get_scope_prefix(object_name::scope ns)
154 {
155     std::string prefix;
156     switch (ns)
157     {
158     case object_name::process_group:
159         {
160             // For now consider all processes as members of the common process group. It may change if there is found
161             // a way to get a process group id (i.e. id of the closest parent process that was created with the CREATE_NEW_PROCESS_GROUP flag).
162             prefix = "Local\\boost.log.process_group";
163         }
164         break;
165 
166     case object_name::session:
167         {
168             prefix = "Local\\boost.log.session";
169         }
170         break;
171 
172     case object_name::user:
173         {
174 #if BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
175             if (init_user_namespace())
176             {
177                 prefix = "User\\boost.log.user";
178             }
179             else
180 #endif // BOOST_USE_WINAPI_VERSION >= BOOST_WINAPI_VERSION_WIN6
181             {
182                 wchar_t buf[UNLEN + 1u];
183                 ULONG len = sizeof(buf) / sizeof(*buf);
184                 if (BOOST_UNLIKELY(!GetUserNameExW(NameSamCompatible, buf, &len)))
185                 {
186                     const boost::winapi::DWORD_ err = boost::winapi::GetLastError();
187                     BOOST_LOG_THROW_DESCR_PARAMS(boost::log::system_error, "Failed to obtain the current user name", (err));
188                 }
189 
190                 std::replace(buf, buf + len, L'\\', L'.');
191 
192                 prefix = "Local\\boost.log.user." + boost::log::aux::utf16_to_utf8(buf);
193             }
194         }
195         break;
196 
197     default:
198         prefix = "Global\\boost.log.global";
199         break;
200     }
201 
202     prefix.push_back('.');
203 
204     return BOOST_LOG_NRVO_RESULT(prefix);
205 }
206 
207 } // namespace
208 
209 //! Constructor from the object name
210 BOOST_LOG_API object_name::object_name(scope ns, const char* str) :
211     m_name(get_scope_prefix(ns) + str)
212 {
213 }
214 
215 //! Constructor from the object name
object_name(scope ns,std::string const & str)216 BOOST_LOG_API object_name::object_name(scope ns, std::string const& str) :
217     m_name(get_scope_prefix(ns) + str)
218 {
219 }
220 
221 } // namespace ipc
222 
223 BOOST_LOG_CLOSE_NAMESPACE // namespace log
224 
225 } // namespace boost
226 
227 #include <boost/log/detail/footer.hpp>
228