E-MailRelay
gidentity_win32.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 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 "gnowide.h"
23#include "gidentity.h"
24#include "gscope.h"
25#include "gstr.h"
26#include "grange.h"
27#include "gbuffer.h"
28#include "gconvert.h"
29#include <sstream>
30#include <vector>
31#include <algorithm>
32
33namespace G
34{
35 namespace IdentityImp
36 {
37 struct Account
38 {
39 Account() = default ;
40 Account( SID_NAME_USE , const std::string & , const std::string & , const std::string & ) ;
41 SID_NAME_USE type {SidTypeInvalid} ;
42 std::string sid ;
43 std::string domain ;
44 std::string name ; // if requested
45 bool valid() const noexcept { return type == SidTypeUser ; }
46 } ;
47 std::string sid() ;
48 std::string rootsid() ;
49 std::string sidstr( PSID sid_p ) ;
50 std::string computername() ;
51 Account lookup( std::string_view , bool = false ) ;
52 }
53}
54
55G::Identity::Identity( uid_t , gid_t , const std::string & sid ) :
56 m_uid(-1) ,
57 m_gid(-1) ,
58 m_sid(sid)
59{
60}
61
62G::Identity::Identity() noexcept : // invalid()
63 m_uid(-1) ,
64 m_gid(-1)
65{
66 static_assert( noexcept(std::string()) , "" ) ; // only guaranteed for c++17
67}
68
69G::Identity::Identity( SignalSafe ) noexcept : // invalid()
70 Identity()
71{
72}
73
74G::Identity::Identity( const std::string & name , const std::string & ) :
75 Identity()
76{
77 auto account = IdentityImp::lookup( name ) ;
78 if( !account.valid() )
79 throw NoSuchUser( name ) ;
80 m_sid = account.sid ;
81}
82
84{
85 return { -1 , -1 , IdentityImp::sid() } ;
86}
87
89{
90 return effective() ;
91}
92
94{
95 return {} ;
96}
97
98G::Identity G::Identity::invalid( SignalSafe ) noexcept
99{
100 return {} ;
101}
102
104{
105 Identity id( "Administrator" ) ; // hmm
106 if( id != invalid() )
107 return id ;
108 return { -1 , -1 , IdentityImp::rootsid() } ;
109}
110
111std::string G::Identity::str() const
112{
113 return sid() ;
114}
115
116uid_t G::Identity::userid() const noexcept
117{
118 if( m_sid.empty() ) return false ;
119 return G::Str::toInt( G::Str::tail( m_sid , m_sid.rfind('-') , "" ) , "-1" ) ; // "RID"
120}
121
122gid_t G::Identity::groupid() const noexcept
123{
124 return -1 ;
125}
126
127std::string G::Identity::sid() const
128{
129 return
130 m_sid.empty() ?
131 std::string("S-1-0-0") :
132 m_sid ;
133}
134
135bool G::Identity::isRoot() const noexcept
136{
137 return G::Str::headMatch(m_sid,"S-1-5-") && G::Str::tailMatch(m_sid,"-500") ;
138}
139
140bool G::Identity::operator==( const Identity & other ) const noexcept
141{
142 return m_sid == other.m_sid ;
143}
144
145bool G::Identity::operator!=( const Identity & other ) const noexcept
146{
147 return m_sid != other.m_sid ;
148}
149
150std::pair<G::Identity,std::string> G::Identity::lookup( std::string_view name )
151{
152 auto account = IdentityImp::lookup( name , true ) ;
153 if( !account.valid() )
154 throw NoSuchUser( name ) ;
155
156 Identity id ;
157 id.m_sid = account.sid ;
158 return std::make_pair( id , account.name ) ;
159}
160
161std::pair<G::Identity,std::string> G::Identity::lookup( std::string_view name , std::nothrow_t )
162{
163 auto account = IdentityImp::lookup( name , true ) ;
164 if( account.valid() )
165 {
166 Identity id ;
167 id.m_sid = account.sid ;
168 return std::make_pair( id , account.name ) ;
169 }
170 else
171 {
172 return std::make_pair( Identity() , std::string() ) ;
173 }
174}
175
176gid_t G::Identity::lookupGroup( const std::string & /*group*/ )
177{
178 return -1 ;
179}
180
181bool G::Identity::match( std::pair<int,int> range ) const
182{
183 return G::Range::within( range , userid() ) ;
184}
185
186// ==
187
188std::string G::IdentityImp::sidstr( PSID sid_p )
189{
190 return G::nowide::convertSidToStringSid( sid_p ) ;
191}
192
193std::string G::IdentityImp::sid()
194{
195 HANDLE htoken = NULL ;
196 if( !OpenProcessToken( GetCurrentProcess() , TOKEN_QUERY , &htoken ) )
197 return {} ;
198 G::ScopeExit close( [htoken](){CloseHandle(htoken);} ) ;
199 DWORD size = 0 ;
200 G::Buffer<char> buffer( sizeof(TOKEN_USER) ) ;
201 if( !GetTokenInformation( htoken , TokenUser , &buffer[0] , static_cast<DWORD>(buffer.size()) , &size ) && size )
202 buffer.resize( static_cast<std::size_t>(size) ) ;
203 if( !GetTokenInformation( htoken , TokenUser , &buffer[0] , static_cast<DWORD>(buffer.size()) , &size ) )
204 return {} ;
205 TOKEN_USER * info_p = G::buffer_cast<TOKEN_USER*>( buffer ) ;
206 return sidstr( info_p->User.Sid ) ;
207}
208
209std::string G::IdentityImp::computername()
210{
211 return nowide::getComputerNameEx() ;
212}
213
214G::IdentityImp::Account G::IdentityImp::lookup( std::string_view name , bool with_canonical_name )
215{
216 const Account error ;
217 if( name.empty() || name.find('\\') != std::string::npos )
218 return error ;
219 std::string domain = computername() ; // => local accounts
220 if( domain.empty() )
221 return error ;
222 std::string full_name = domain.append(1U,'\\').append(name.data(),name.size()) ;
223
224 DWORD sidsize = 0 ;
225 DWORD domainsize = 0 ;
226 SID_NAME_USE type = SidTypeInvalid ;
227 if( nowide::lookupAccountName( full_name , NULL , &sidsize , false , &domainsize , &type ) )
228 return error ;
229 G::Buffer<char> sidbuffer( std::max(DWORD(1),sidsize) ) ;
230 if( !nowide::lookupAccountName( full_name , sidbuffer.data() , &sidsize , true , &domainsize , &type ) )
231 return error ;
232 SID * sid_p = G::buffer_cast<SID*>(sidbuffer) ;
233
234 std::string canonical_name ;
235 if( with_canonical_name )
236 {
237 DWORD namebuffersize = 0 ;
238 DWORD domainbuffersize = 0 ;
239 if( nowide::lookupAccountSid( sid_p , nullptr , false , &namebuffersize , false , &domainbuffersize , &type ) )
240 return error ;
241 if( !nowide::lookupAccountSid( sid_p , &canonical_name , true , &namebuffersize , true , &domainbuffersize , &type ) )
242 return error ;
243 if( canonical_name.empty() )
244 return error ;
245 }
246
247 return { type , sidstr(sid_p) , domain , canonical_name } ;
248}
249
250std::string G::IdentityImp::rootsid()
251{
252 DWORD size = 0 ;
253 G::Buffer<char> buffer( 1U ) ;
254 WELL_KNOWN_SID_TYPE type = WinLocalAccountAndAdministratorSid ;
255 if( !CreateWellKnownSid( type , NULL , &buffer[0] , &size ) && size )
256 buffer.resize( static_cast<std::size_t>(size) ) ;
257 if( !CreateWellKnownSid( type , NULL , &buffer[0] , &size ) )
258 return {} ;
259 SID * sid_p = G::buffer_cast<SID*>( buffer ) ;
260 return sidstr( sid_p ) ;
261}
262
263G::IdentityImp::Account::Account( SID_NAME_USE type_ , const std::string & sid_ ,
264 const std::string & domain_ , const std::string & name_ ) :
265 type(type_) ,
266 sid(sid_) ,
267 domain(domain_) ,
268 name(name_)
269{
270}
271
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:45
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.
static std::pair< Identity, std::string > lookup(std::string_view user)
Does a username lookup returning the identity and the canonical name.
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.
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:47
static bool tailMatch(std::string_view in, std::string_view ending) noexcept
Returns true if the string has the given ending (or the given ending is empty).
Definition: gstr.cpp:1352
static int toInt(std::string_view s)
Converts string 's' to an int.
Definition: gstr.cpp:538
static bool headMatch(std::string_view in, std::string_view head) noexcept
Returns true if the string has the given start (or head is empty).
Definition: gstr.cpp:1359
static std::string tail(std::string_view in, std::size_t pos, std::string_view default_={})
Returns the last part of the string after the given position.
Definition: gstr.cpp:1322
Contains inline functions that convert to and from UTF-8 strings in order to call wide-character "W()...
Low-level classes.
Definition: garg.h:36
A substitute for std::vector<char> that has more useful alignment guarantees and explicitly avoids de...
Definition: gbuffer.h:63