E-MailRelay
gidentity_unix.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2021 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 "gassert.h"
25#include <array>
26#include <climits>
27#include <vector>
28#include <pwd.h> // getpwnam_r()
29#include <unistd.h> // sysconf()
30
31namespace G
32{
33 namespace IdentityImp
34 {
35 int sysconf_value( int key )
36 {
37 long n = ::sysconf( key ) ;
38 return ( n < 0 || n > INT_MAX ) ? -1 : static_cast<int>(n) ;
39 }
40 }
41}
42
43G::Identity::Identity() noexcept :
44 m_uid(static_cast<uid_t>(-1)) ,
45 m_gid(static_cast<gid_t>(-1))
46{
47}
48
49G::Identity::Identity( SignalSafe ) noexcept :
50 m_uid(static_cast<uid_t>(-1)) ,
51 m_gid(static_cast<gid_t>(-1))
52{
53}
54
55G::Identity::Identity( const std::string & name , const std::string & group ) :
56 m_uid(static_cast<uid_t>(-1)) ,
57 m_gid(static_cast<gid_t>(-1))
58{
59 auto pair = lookupUser( name ) ;
60 m_uid = pair.first ;
61 m_gid = pair.second ;
62
63 if( !group.empty() )
64 m_gid = lookupGroup( group ) ;
65}
66
67std::pair<uid_t,gid_t> G::Identity::lookupUser( const std::string & name )
68{
69 using passwd_t = struct passwd ;
70 std::pair<uid_t,gid_t> result( 0 , 0 ) ;
71 std::array<int,3U> sizes {{ 120 , 0 , 16000 }} ;
72 sizes[1] = IdentityImp::sysconf_value( _SC_GETPW_R_SIZE_MAX ) ;
73 for( auto size : sizes )
74 {
75 if( size <= 0 ) continue ;
76 auto buffer_size = static_cast<std::size_t>(size) ;
77 std::vector<char> buffer( buffer_size ) ;
78 passwd_t pwd {} ;
79 passwd_t * result_p = nullptr ;
80 int rc = ::getpwnam_r( name.c_str() , &pwd , &buffer[0] , buffer_size , &result_p ) ;
81 int e = Process::errno_() ;
82 if( rc == 0 && result_p )
83 {
84 result.first = result_p->pw_uid ;
85 result.second = result_p->pw_gid ;
86 break ;
87 }
88 else if( rc == 0 && name == "root" ) // in case of no /etc/passwd file
89 {
90 result.first = 0 ;
91 result.second = 0 ;
92 break ;
93 }
94 else if( rc == 0 )
95 {
96 throw NoSuchUser( name ) ;
97 }
98 else if( e != ERANGE )
99 {
100 throw Error( Process::strerror(e) ) ;
101 }
102 }
103 return result ;
104}
105
106gid_t G::Identity::lookupGroup( const std::string & group )
107{
108 using group_t = struct group ;
109 gid_t result = 0 ;
110 std::array<int,3U> sizes {{ 120 , 0 , 16000 }} ;
111 sizes[1] = IdentityImp::sysconf_value( _SC_GETGR_R_SIZE_MAX ) ;
112 for( auto size : sizes )
113 {
114 if( size <= 0 ) continue ;
115 auto buffer_size = static_cast<std::size_t>(size) ;
116 std::vector<char> buffer( buffer_size ) ;
117 group_t grp {} ;
118 group_t * result_p = nullptr ;
119 int rc = ::getgrnam_r( group.c_str() , &grp , &buffer[0] , buffer_size , &result_p ) ;
120 if( rc == 0 && result_p )
121 {
122 result = result_p->gr_gid ;
123 break ;
124 }
125 else if( rc == 0 )
126 {
127 throw NoSuchGroup( group ) ;
128 }
129 }
130 return result ;
131}
132
134{
135 Identity id ;
136 id.m_uid = ::geteuid() ;
137 id.m_gid = ::getegid() ;
138 id.m_h = 0 ; // pacifies -Wunused-private-field
139 return id ;
140}
141
142G::Identity G::Identity::real( bool with_cache ) noexcept
143{
144 static bool first = true ;
145 static uid_t u = 0 ;
146 static gid_t g = 0 ;
147 if( first )
148 {
149 first = false ;
150 u = ::getuid() ;
151 g = ::getgid() ;
152 }
153 Identity id ;
154 id.m_uid = (first||with_cache) ? u : ::getuid() ;
155 id.m_gid = (first||with_cache) ? g : ::getgid() ;
156 return id ;
157}
158
160{
161 return {} ;
162}
163
165{
166 return Identity(safe) ;
167}
168
170{
171 Identity id ;
172 id.m_uid = 0 ;
173 id.m_gid = 0 ;
174 return id ;
175}
176
177std::string G::Identity::str() const
178{
179 std::ostringstream ss ;
180 ss << m_uid << "/" << m_gid ;
181 return ss.str() ;
182}
183
184uid_t G::Identity::userid() const noexcept
185{
186 return m_uid ;
187}
188
189gid_t G::Identity::groupid() const noexcept
190{
191 return m_gid ;
192}
193
194bool G::Identity::isRoot() const noexcept
195{
196 return m_uid == 0 ;
197}
198
199bool G::Identity::operator==( const Identity & other ) const noexcept
200{
201 return m_uid == other.m_uid && m_gid == other.m_gid ;
202}
203
204bool G::Identity::operator!=( const Identity & other ) const noexcept
205{
206 return !operator==( other ) ;
207}
208
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:43
static std::pair< uid_t, gid_t > lookupUser(const std::string &user)
Does a username lookup. Throws on error.
gid_t groupid() const noexcept
Returns the group part.
bool isRoot() const noexcept
Returns true if the userid is zero.
uid_t userid() const noexcept
Returns the user part.
static Identity invalid() noexcept
Returns an invalid identity.
bool operator==(const Identity &) const noexcept
Comparison operator.
static Identity root() noexcept
Returns the superuser identity.
static gid_t lookupGroup(const std::string &group)
Does a groupname lookup. Throws on error.
std::string str() const
Returns a string representation.
static Identity effective() noexcept
Returns the current effective identity.
Identity(const std::string &username, const std::string &group_name_override=std::string())
Constructor for the named identity.
bool operator!=(const Identity &) const noexcept
Comparison operator.
static Identity real(bool with_cache=true) 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: galign.h:28