E-MailRelay
gidentity_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2023 Graeme Walker <graeme_walker@users.sourceforge.net>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16// ===
17///
18/// \file gidentity_win32.cpp
19///
20
21#include "gdef.h"
22#include "gidentity.h"
23#include "gscope.h"
24#include "gstr.h"
25#include "grange.h"
26#include "gbuffer.h"
27#include <sddl.h> // ConvertSidToStringSid()
28#include <sstream>
29#include <vector>
30#include <algorithm>
31
32namespace G
33{
34 namespace IdentityImp
35 {
36 struct Account
37 {
38 Account() = default ;
39 Account( SID_NAME_USE , const std::string & , const std::string & , const std::string & ) ;
40 SID_NAME_USE type {SidTypeInvalid} ;
41 std::string sid ;
42 std::string domain ;
43 std::string name ; // if requested
44 bool valid() const noexcept { return type == SidTypeUser ; }
45 } ;
46 std::string sid() ;
47 std::string rootsid() ;
48 std::string sidstr( PSID sid_p ) ;
49 std::string computername() ;
50 Account lookup( const std::string & name , bool = false ) ;
51 }
52}
53
54G::Identity::Identity( uid_t , gid_t , const std::string & sid ) :
55 m_uid(-1) ,
56 m_gid(-1) ,
57 m_sid(sid)
58{
59}
60
61G::Identity::Identity() noexcept : // invalid()
62 m_uid(-1) ,
63 m_gid(-1)
64{
65 static_assert( noexcept(std::string()) , "" ) ; // only guaranteed for c++17
66}
67
68G::Identity::Identity( SignalSafe ) noexcept : // invalid()
69 Identity()
70{
71}
72
73G::Identity::Identity( const std::string & name , const std::string & ) :
74 Identity()
75{
76 auto account = IdentityImp::lookup( name ) ;
77 if( !account.valid() )
78 throw NoSuchUser( name ) ;
79 m_sid = account.sid ;
80}
81
83{
84 return { -1 , -1 , IdentityImp::sid() } ;
85}
86
88{
89 return effective() ;
90}
91
93{
94 return {} ;
95}
96
97G::Identity G::Identity::invalid( SignalSafe ) noexcept
98{
99 return {} ;
100}
101
103{
104 Identity id( "Administrator" ) ; // hmm
105 if( id != invalid() )
106 return id ;
107 return { -1 , -1 , IdentityImp::rootsid() } ;
108}
109
110std::string G::Identity::str() const
111{
112 return sid() ;
113}
114
115uid_t G::Identity::userid() const noexcept
116{
117 if( m_sid.empty() ) return false ;
118 return G::Str::toInt( G::Str::tail( m_sid , m_sid.rfind('-') , "" ) , "-1" ) ; // "RID"
119}
120
121gid_t G::Identity::groupid() const noexcept
122{
123 return -1 ;
124}
125
126std::string G::Identity::sid() const
127{
128 return
129 m_sid.empty() ?
130 std::string("S-1-0-0") :
131 m_sid ;
132}
133
134bool G::Identity::isRoot() const noexcept
135{
136 return G::Str::headMatch(m_sid,"S-1-5-") && G::Str::tailMatch(m_sid,"-500") ;
137}
138
139bool G::Identity::operator==( const Identity & other ) const noexcept
140{
141 return m_sid == other.m_sid ;
142}
143
144bool G::Identity::operator!=( const Identity & other ) const noexcept
145{
146 return m_sid != other.m_sid ;
147}
148
149std::pair<G::Identity,std::string> G::Identity::lookup( const std::string & name )
150{
151 auto account = IdentityImp::lookup( name , true ) ;
152 if( !account.valid() )
153 throw NoSuchUser( name ) ;
154
155 Identity id ;
156 id.m_sid = account.sid ;
157 return std::make_pair( id , account.name ) ;
158}
159
160std::pair<G::Identity,std::string> G::Identity::lookup( const std::string & name , std::nothrow_t )
161{
162 auto account = IdentityImp::lookup( name , true ) ;
163 if( account.valid() )
164 {
165 Identity id ;
166 id.m_sid = account.sid ;
167 return std::make_pair( id , account.name ) ;
168 }
169 else
170 {
171 return std::make_pair( Identity() , std::string() ) ;
172 }
173}
174
175gid_t G::Identity::lookupGroup( const std::string & /*group*/ )
176{
177 return -1 ;
178}
179
180bool G::Identity::match( std::pair<int,int> range ) const
181{
182 return G::Range::within( range , userid() ) ;
183}
184
185// ==
186
187std::string G::IdentityImp::sidstr( PSID sid_p )
188{
189 char * sidstr_p = nullptr ;
190 if( !ConvertSidToStringSidA( sid_p , &sidstr_p ) || sidstr_p == nullptr )
191 return {} ;
192 std::string result( sidstr_p ) ;
193 LocalFree( sidstr_p ) ;
194 return result ;
195}
196
197std::string G::IdentityImp::sid()
198{
199 HANDLE htoken = NULL ;
200 if( !OpenProcessToken( GetCurrentProcess() , TOKEN_QUERY , &htoken ) )
201 return {} ;
202 G::ScopeExit close( [htoken](){CloseHandle(htoken);} ) ;
203 DWORD size = 0 ;
204 G::Buffer<char> buffer( sizeof(TOKEN_USER) ) ;
205 if( !GetTokenInformation( htoken , TokenUser , &buffer[0] , static_cast<DWORD>(buffer.size()) , &size ) && size )
206 buffer.resize( static_cast<std::size_t>(size) ) ;
207 if( !GetTokenInformation( htoken , TokenUser , &buffer[0] , static_cast<DWORD>(buffer.size()) , &size ) )
208 return {} ;
209 TOKEN_USER * info_p = G::buffer_cast<TOKEN_USER*>( buffer ) ;
210 return sidstr( info_p->User.Sid ) ;
211}
212
213#if 0
214std::string G::IdentityImp::username()
215{
216 std::vector<char> buffer ;
217 buffer.reserve( 100U ) ;
218 buffer.resize( 1U ) ;
219 DWORD size = static_cast<DWORD>( buffer.size() ) ;
220 if( !GetUserNameA( &buffer[0] , &size ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER && size )
221 buffer.resize( static_cast<std::size_t>(size) ) ;
222 if( size == 0 || !GetUserNameA( &buffer[0] , &size ) )
223 return {} ;
224 std::size_t n = std::min( buffer.size() , static_cast<std::size_t>(size) ) ;
225 buffer.at( n-1U ) = '\0' ;
226 return std::string( &buffer[0] ) ;
227}
228#endif
229
230std::string G::IdentityImp::computername()
231{
232 std::vector<char> buffer ;
233 buffer.reserve( 100U ) ;
234 buffer.resize( 1U ) ;
235 DWORD size = static_cast<DWORD>( buffer.size() ) ;
236 if( !GetComputerNameExA( ComputerNameNetBIOS , &buffer[0] , &size ) && GetLastError() == ERROR_MORE_DATA && size )
237 buffer.resize( static_cast<std::size_t>(size) ) ;
238 if( !GetComputerNameExA( ComputerNameNetBIOS , &buffer[0] , &size ) )
239 return {} ;
240 std::size_t n = std::min( buffer.size()-1U , static_cast<std::size_t>(size) ) ;
241 buffer.at( n ) = '\0' ;
242 return std::string( &buffer[0] ) ;
243}
244
245G::IdentityImp::Account G::IdentityImp::lookup( const std::string & name , bool with_canonical_name )
246{
247 const Account error ;
248 if( name.empty() || name.find('\\') != std::string::npos )
249 return error ;
250 std::string domain = computername() ; // => local accounts
251 if( domain.empty() )
252 return error ;
253 std::string full_name = domain.append(1U,'\\').append(name) ;
254 DWORD sidsize = 0 ;
255 DWORD domainsize = 0 ;
256 SID_NAME_USE type = SidTypeInvalid ;
257 if( LookupAccountNameA( NULL , full_name.c_str() , NULL , &sidsize , NULL , &domainsize , &type ) )
258 return error ;
259 G::Buffer<char> sidbuffer( std::max(DWORD(1),sidsize) ) ;
260 std::vector<char> domainbuffer( std::max(DWORD(1),domainsize) ) ;
261 if( !LookupAccountNameA( NULL , full_name.c_str() , &sidbuffer[0] , &sidsize , &domainbuffer[0] , &domainsize , &type ) )
262 return error ;
263 SID * sid_p = G::buffer_cast<SID*>(sidbuffer) ;
264
265 std::string canonical_name ;
266 if( with_canonical_name )
267 {
268 DWORD namebuffersize = 0 ;
269 DWORD domainbuffersize = 0 ;
270 if( LookupAccountSidA( NULL , sid_p , NULL , &namebuffersize , NULL , &domainbuffersize , &type ) )
271 return error ;
272 std::vector<char> namebuffer( std::max(DWORD(1),namebuffersize) ) ;
273 std::vector<char> domainbuffer2( std::max(DWORD(1),domainbuffersize) ) ; // not used
274 if( !LookupAccountSidA( NULL , sid_p , &namebuffer[0] , &namebuffersize , &domainbuffer2[0] , &domainbuffersize , &type ) )
275 return error ;
276 namebuffer[namebuffer.size()-1U] = '\0' ;
277 canonical_name = std::string( &namebuffer[0] ) ;
278 if( canonical_name.empty() )
279 return error ;
280 }
281
282 return { type , sidstr(sid_p) , &domain[0] , canonical_name } ;
283}
284
285std::string G::IdentityImp::rootsid()
286{
287 DWORD size = 0 ;
288 G::Buffer<char> buffer( 1U ) ;
289 WELL_KNOWN_SID_TYPE type = WinLocalAccountAndAdministratorSid ;
290 if( !CreateWellKnownSid( type , NULL , &buffer[0] , &size ) && size )
291 buffer.resize( static_cast<std::size_t>(size) ) ;
292 if( !CreateWellKnownSid( type , NULL , &buffer[0] , &size ) )
293 return {} ;
294 SID * sid_p = G::buffer_cast<SID*>( buffer ) ;
295 return sidstr( sid_p ) ;
296}
297
298G::IdentityImp::Account::Account( SID_NAME_USE type_ , const std::string & sid_ ,
299 const std::string & domain_ , const std::string & name_ ) :
300 type(type_) ,
301 sid(sid_) ,
302 domain(domain_) ,
303 name(name_)
304{
305}
306
A combination of user-id and group-id, with a very low-level interface to the get/set/e/uid/gid funct...
Definition: gidentity.h:44
gid_t groupid() const noexcept
Returns the group part (Unix).
std::string sid() const
Returns the sid (Windows).
bool isRoot() const noexcept
Returns true if the userid is zero.
uid_t userid() const noexcept
Returns the user part (Unix).
static Identity invalid() noexcept
Returns an invalid identity.
bool operator==(const Identity &) const noexcept
Comparison operator.
bool match(std::pair< int, int > uid_range) const
Returns true if the user-id is in the given range or if not implemented.
static Identity root() noexcept
Returns the superuser identity.
static gid_t lookupGroup(const std::string &group)
Does a groupname lookup.
std::string str() const
Returns a string representation.
static Identity effective() noexcept
Returns the current effective identity.
bool operator!=(const Identity &) const noexcept
Comparison operator.
static std::pair< Identity, std::string > lookup(const std::string &user)
Does a username lookup returning the identity and the canonical name.
Identity(const std::string &username, const std::string &group_name_override={})
Constructor for the named identity.
static Identity real() noexcept
Returns the calling process's real identity.
A class that calls an exit function at the end of its scope.
Definition: gscope.h:46
static int toInt(string_view s)
Converts string 's' to an int.
Definition: gstr.cpp:541
static bool tailMatch(const std::string &in, string_view ending) noexcept
Returns true if the string has the given ending (or the given ending is empty).
Definition: gstr.cpp:1355
static std::string tail(string_view in, std::size_t pos, string_view default_={})
Returns the last part of the string after the given position.
Definition: gstr.cpp:1325
static bool headMatch(const std::string &in, string_view head) noexcept
Returns true if the string has the given start (or head is empty).
Definition: gstr.cpp:1362
Low-level classes.
Definition: garg.h:30
A substitute for std::vector<char> that has more useful alignment guarantees and explicitly avoids de...
Definition: gbuffer.h:63