Line data Source code
1 : // Copyright (c) 2016-2022 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/snapdev
4 : // contact@m2osw.com
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License along
17 : // with this program; if not, write to the Free Software Foundation, Inc.,
18 : // 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 : #pragma once
20 :
21 : // self
22 : //
23 : #include <snapdev/not_used.h>
24 :
25 :
26 : // C++
27 : //
28 : #include <algorithm>
29 : #include <string>
30 : #include <type_traits>
31 :
32 :
33 :
34 : namespace snapdev
35 : {
36 :
37 :
38 :
39 : /** \brief Safely become root and back.
40 : *
41 : * This class defines a way to become root and then come back as the
42 : * original user one your work requiring root is done.
43 : *
44 : * The class uses RAII so when an object of that type gets destroyed,
45 : * it automatically restore the user. You can also restore the user
46 : * sooner if safer.
47 : *
48 : * This class is used only when a process is given root as the owner
49 : * of the file and when the set user identifier flag is set on the
50 : * executable (a.k.a. `chmod u+s /usr/bin/my-tool`).
51 : *
52 : * The class constructor attempts the change to root. If that fails,
53 : * the valid() function returns false. It is likely important that
54 : * you verified whether the user change worked or not to make sure
55 : * that you can run the following commands as root.
56 : *
57 : * The class can be used recursively. So if function A uses as_root,
58 : * then calls function B which also uses as_root, the second time
59 : * the switch will do nothing since you are already root. Function A
60 : * will stil properly restore the user to the normal user instead of
61 : * root.
62 : *
63 : * The class is only expected to be used on the stack. If you use it
64 : * in an object, then the restore may not work as expected (i.e. it
65 : * could happen in the wrong order).
66 : */
67 : class as_root
68 : {
69 : public:
70 : /** \brief Save the current user then switch to root.
71 : *
72 : * This function switches us to the root user. If that fails,
73 : * then the valid() function will return false. You can get
74 : * the exact error using the error_number() function.
75 : *
76 : * The destructor will automatically revert back to your
77 : * original user.
78 : */
79 1 : as_root()
80 1 : : f_user_uid(getuid())
81 : {
82 1 : if(seteuid(0) != 0)
83 : {
84 1 : f_errno = errno;
85 : }
86 1 : }
87 :
88 : /** \brief Restore the user as it was on construction.
89 : *
90 : * This function restores the user as found in the construtor. If
91 : * the constructor could not switch to root, then the function fails.
92 : */
93 1 : ~as_root()
94 1 : {
95 1 : if(f_errno == 0)
96 : {
97 : NOT_USED(seteuid(f_user_uid)); // LCOV_EXCL_LINE -- our unit tests cannot switch to root user
98 : }
99 1 : }
100 :
101 : /** \brief Check whether the as_root object is considered valid.
102 : *
103 : * The constructor of the function attempts to switch to the root
104 : * user. If the function fails, then the errno value is saved.
105 : * This function checks that error, if not zero, then the switch
106 : * to the root user did not happen so the as_root object is not
107 : * considered valid.
108 : *
109 : * To get the actual error number, use the error_number() function.
110 : *
111 : * \return true if the constructor worked and thus we are root.
112 : *
113 : * \sa error_number()
114 : */
115 1 : bool valid() const
116 : {
117 1 : return f_errno == 0;
118 : }
119 :
120 : /** \brief Get the error number.
121 : *
122 : * The constructor saves the error number of the seteuid() function
123 : * fails. That error number can be retrieved using this function.
124 : * If you do not call any other system function, the errno variable
125 : * will still be set to the correct value, however, you must use
126 : * the valid() function to make sure that an error actually occurred.
127 : *
128 : * \return The errno as set by the seteuid() on an error, otherwise 0.
129 : */
130 1 : int error_number() const
131 : {
132 1 : return f_errno;
133 : }
134 :
135 : private:
136 : /** \brief The user UID on entry.
137 : *
138 : * The constructor saves the current user identifier so we can restore
139 : * it on exit. This may be 0 if you use the object multiple times in
140 : * a recursive manner.
141 : */
142 : uid_t f_user_uid = 0;
143 :
144 : /** \brief The error number.
145 : *
146 : * If the seteuid() found in the constructor fails, then this value is
147 : * set to the errno parameter as set by the seteuid() function.
148 : *
149 : * By default, the value is 0 which means that the seteuid() function
150 : * suceeded in changing the user identifier to the root user identifier.
151 : */
152 : int f_errno = 0;
153 : };
154 :
155 :
156 :
157 : } // namespace snapdev
158 : // vim: ts=4 sw=4 et
|