E-MailRelay
gidentity_unix.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_unix.cpp
19///
20
21#include "gdef.h"
22#include "gidentity.h"
23#include "gprocess.h"
24#include "grange.h"
25#include "gassert.h"
26#include <array>
27#include <climits>
28#include <vector>
29#include <pwd.h> // getpwnam_r()
30#include <unistd.h> // sysconf()
31
32namespace G
33{
34 namespace IdentityImp
35 {
36 bool lookupUser( std::string & name , uid_t & uid , gid_t & gid ) ;
37 bool lookupGroup( const std::string & group , gid_t & gid ) ;
38 int sysconf_( int key ) ;
39 }
40}
41
42G::Identity::Identity( uid_t uid , gid_t gid ) :
43 m_uid(uid) ,
44 m_gid(gid)
45{
46}
47
48G::Identity::Identity() noexcept : // invalid()
49 m_uid(static_cast<uid_t>(-1)) ,
50 m_gid(static_cast<gid_t>(-1))
51{
52}
53
54G::Identity::Identity( SignalSafe ) noexcept : // invalid()
55 Identity()
56{
57}
58
59G::Identity::Identity( const std::string & name_in , const std::string & group ) :
60 Identity()
61{
62 std::string name = name_in ;
63 if( !IdentityImp::lookupUser( name , m_uid , m_gid ) )
64 throw NoSuchUser( name_in ) ;
65
66 if( !group.empty() && !IdentityImp::lookupGroup( group , m_gid ) )
67 throw NoSuchGroup( group ) ;
68}
69
71{
72 return { ::geteuid() , ::getegid() } ;
73}
74
76{
77 return { ::getuid() , ::getgid() } ;
78}
79
81{
82 return {} ;
83}
84
85#ifndef G_LIB_SMALL
87{
88 return Identity( safe ) ;
89}
90#endif
91
93{
94 return { 0 , 0 } ;
95}
96
97#ifndef G_LIB_SMALL
98std::string G::Identity::str() const
99{
100 std::ostringstream ss ;
101 ss << static_cast<int>(m_uid) << "/" << static_cast<int>(m_gid) ;
102 return ss.str() ;
103}
104#endif
105
106uid_t G::Identity::userid() const noexcept
107{
108 return m_uid ;
109}
110
111gid_t G::Identity::groupid() const noexcept
112{
113 return m_gid ;
114}
115
116bool G::Identity::isRoot() const noexcept
117{
118 return m_uid == 0 ;
119}
120
121bool G::Identity::operator==( const Identity & other ) const noexcept
122{
123 return m_uid == other.m_uid && m_gid == other.m_gid ;
124}
125
126bool G::Identity::operator!=( const Identity & other ) const noexcept
127{
128 return !operator==( other ) ;
129}
130
131#ifndef G_LIB_SMALL
132std::pair<G::Identity,std::string> G::Identity::lookup( std::string_view name_in )
133{
134 Identity result ;
135 std::string name = sv_to_string( name_in ) ;
136 if( !IdentityImp::lookupUser( name , result.m_uid , result.m_gid ) )
137 throw NoSuchUser( name_in ) ;
138 return std::make_pair( result , name ) ;
139}
140#endif
141
142std::pair<G::Identity,std::string> G::Identity::lookup( std::string_view name_in , std::nothrow_t )
143{
144 Identity result ;
145 std::string name = sv_to_string( name_in ) ;
146 IdentityImp::lookupUser( name , result.m_uid , result.m_gid ) ;
147 return std::make_pair( result , name ) ;
148}
149
150gid_t G::Identity::lookupGroup( const std::string & group )
151{
152 gid_t result = static_cast<gid_t>(-1) ;
153 IdentityImp::lookupGroup( group , result ) ;
154 return result ;
155}
156
157bool G::Identity::match( std::pair<int,int> uid_range ) const
158{
159 return G::Range::within( uid_range , static_cast<int>(m_uid) ) ;
160}
161
162// ==
163
164bool G::IdentityImp::lookupUser( std::string & name , uid_t & uid , gid_t & gid )
165{
166 using passwd_t = struct passwd ;
167 std::array<int,3U> sizes {{ 120 , 0 , 16000 }} ;
168 sizes[1] = IdentityImp::sysconf_( _SC_GETPW_R_SIZE_MAX ) ;
169 for( std::size_t i = 0U ; i < sizes.size() ; i++ )
170 {
171 int size = sizes[i] ;
172 if( size <= 0 ) continue ;
173 auto buffer_size = static_cast<std::size_t>(size) ;
174 std::vector<char> buffer( buffer_size ) ;
175 passwd_t pwd {} ;
176 passwd_t * result_p = nullptr ;
177 int rc = ::getpwnam_r( name.c_str() , &pwd , buffer.data() , buffer_size , &result_p ) ;
178 int e = Process::errno_() ;
179 if( rc != 0 && e == ERANGE && (i+1U) < sizes.size() )
180 {
181 continue ; // try again with with bigger buffer
182 }
183 else if( rc == 0 && result_p )
184 {
185 name = result_p->pw_name ;
186 uid = result_p->pw_uid ;
187 gid = result_p->pw_gid ;
188 return true ;
189 }
190 else if( rc == 0 && name == "root" ) // in case of no /etc/passwd file
191 {
192 uid = 0 ;
193 gid = 0 ;
194 return true ;
195 }
196 else if( rc == 0 )
197 {
198 break ; // no such user
199 }
200 else
201 {
202 throw Identity::Error( Process::strerror(e) ) ;
203 }
204 }
205 return false ;
206}
207
208bool G::IdentityImp::lookupGroup( const std::string & group , gid_t & gid )
209{
210 using group_t = struct group ;
211 std::array<int,3U> sizes {{ 120 , 0 , 16000 }} ;
212 sizes[1] = IdentityImp::sysconf_( _SC_GETGR_R_SIZE_MAX ) ;
213 for( auto size : sizes )
214 {
215 if( size <= 0 ) continue ;
216 auto buffer_size = static_cast<std::size_t>(size) ;
217 std::vector<char> buffer( buffer_size ) ;
218 group_t grp {} ;
219 group_t * result_p = nullptr ;
220 int rc = ::getgrnam_r( group.c_str() , &grp , buffer.data() , buffer_size , &result_p ) ;
221 if( rc == 0 && result_p )
222 {
223 gid = result_p->gr_gid ;
224 return true ;
225 }
226 else if( rc == 0 )
227 {
228 return false ;
229 }
230 }
231 return false ;
232}
233
234int G::IdentityImp::sysconf_( int key )
235{
236 long n = ::sysconf( key ) ;
237 return ( n < 0 || n > INT_MAX ) ? -1 : static_cast<int>(n) ;
238}
239
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).
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.
static std::string strerror(int errno_)
Translates an 'errno' value into a meaningful diagnostic string.
static int errno_(const SignalSafe &=G::SignalSafe()) noexcept
Returns the process's current 'errno' value.
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:37
Low-level classes.
Definition: garg.h:36