1 /* b-excwho.c --- exclusivity / identity
2
3 Copyright (C) 2010-2020 Thien-Thi Nguyen
4 Copyright (C) 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5 Copyright (C) 1982, 1988, 1989 Walter Tichy
6
7 This file is part of GNU RCS.
8
9 GNU RCS is free software: you can redistribute it and/or modify it
10 under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 GNU RCS is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty
16 of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 See the GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #include "base.h"
24 #include <string.h>
25 #include <stdlib.h>
26 #include <unistd.h>
27 #ifdef HAVE_PWD_H
28 #include <pwd.h>
29 #endif
30 #include "b-complain.h"
31 #include "b-divvy.h"
32 #include "b-esds.h"
33
34 #if defined HAVE_SETUID && !defined HAVE_SETEUID
35 #undef seteuid
36 #define seteuid setuid
37 #endif
38
39 /* Programmer error: We used to conditionally define the
40 func (only when ‘enable’ is defined), so it makes no sense
41 to call it otherwise. */
42 #ifdef DEBUG
43 #define PEBKAC(enable) PFATAL ("%s:%d: PEBKAC (%s, %s)", \
44 __FILE__, __LINE__, \
45 __func__, #enable)
46 #else
47 #define PEBKAC(enable) abort ()
48 #endif
49
50 #define cacheid(V,E) \
51 if (!BE (V ## _cached)) \
52 { \
53 BE (V) = E; \
54 BE (V ## _cached) = true; \
55 } \
56 return BE (V)
57
58 static uid_t
ruid(void)59 ruid (void)
60 {
61 #ifndef HAVE_GETUID
62 PEBKAC (HAVE_GETUID);
63 #endif
64 cacheid (ruid, getuid ());
65 }
66
67 bool
stat_mine_p(struct stat * st)68 stat_mine_p (struct stat *st)
69 {
70 #ifndef HAVE_GETUID
71 return true;
72 #else
73 return ruid () == st->st_uid;
74 #endif
75 }
76
77 #if defined HAVE_SETUID
78 static uid_t
euid(void)79 euid (void)
80 {
81 #ifndef HAVE_GETUID
82 PEBKAC (HAVE_GETUID);
83 #endif
84 cacheid (euid, geteuid ());
85 }
86 #endif /* defined HAVE_SETUID */
87
88 bool
currently_setuid_p(void)89 currently_setuid_p (void)
90 {
91 #if defined HAVE_SETUID && defined HAVE_GETUID
92 return euid () != ruid ();
93 #else
94 return false;
95 #endif
96 }
97
98 #if defined HAVE_SETUID
99 static void
set_uid_to(uid_t u)100 set_uid_to (uid_t u)
101 /* Become user ‘u’. */
102 {
103 /* Setuid execution really works only with POSIX 1003.1a Draft 5
104 ‘seteuid’, because it lets us switch back and forth between
105 arbitrary users. If ‘seteuid’ doesn't work, we fall back on
106 ‘setuid’, which works if saved setuid is supported, unless
107 the real or effective user is root. This area is such a mess
108 that we always check switches at runtime. */
109
110 if (! currently_setuid_p ())
111 return;
112 #if defined HAVE_WORKING_FORK
113 #if defined HAVE_SETREUID
114 if (PROB (setreuid (u == euid () ? ruid () : euid (), u)))
115 fatal_sys ("setuid");
116 #else /* !defined HAVE_SETREUID */
117 if (PROB (seteuid (u)))
118 fatal_sys ("setuid");
119 #endif /* !defined HAVE_SETREUID */
120 #endif /* defined HAVE_WORKING_FORK */
121 if (geteuid () != u)
122 {
123 if (BE (already_setuid))
124 return;
125 BE (already_setuid) = true;
126 PFATAL ("root setuid not supported" + (u ? 5 : 0));
127 }
128 }
129 #endif /* defined HAVE_SETUID */
130
131 void
nosetid(void)132 nosetid (void)
133 /* Ignore all calls to ‘seteid’ and ‘setrid’. */
134 {
135 #ifdef HAVE_SETUID
136 BE (stick_with_euid) = true;
137 #endif
138 }
139
140 void
seteid(void)141 seteid (void)
142 /* Become effective user. */
143 {
144 #ifdef HAVE_SETUID
145 if (!BE (stick_with_euid))
146 set_uid_to (euid ());
147 #endif
148 }
149
150 void
setrid(void)151 setrid (void)
152 /* Become real user. */
153 {
154 #ifdef HAVE_SETUID
155 if (!BE (stick_with_euid))
156 set_uid_to (ruid ());
157 #endif
158 }
159
160 #if USER_OVER_LOGNAME
161 #define CONSULT_FIRST "USER"
162 #define CONSULT_SECOND "LOGNAME"
163 #else
164 #define CONSULT_FIRST "LOGNAME"
165 #define CONSULT_SECOND "USER"
166 #endif
167
168 char const *
getusername(bool suspicious)169 getusername (bool suspicious)
170 /* Get and return the caller's login name.
171 Trust only ‘getwpuid_r’ if ‘suspicious’. */
172 {
173 if (!BE (username))
174 {
175 #define JAM(x) (BE (username) = x)
176 char buf[BUFSIZ];
177
178 /* Prefer ‘getenv’ unless ‘suspicious’; it's much faster. */
179 if (suspicious
180 || (!JAM (cgetenv (CONSULT_FIRST))
181 && !JAM (cgetenv (CONSULT_SECOND))
182 && !(0 == getlogin_r (buf, BUFSIZ)
183 && JAM (str_save (buf)))))
184 {
185 #if !defined HAVE_GETPWUID_R
186 #if defined HAVE_SETUID
187 PFATAL ("setuid not supported");
188 #else
189 PFATAL ("Who are you? Please setenv LOGNAME.");
190 #endif
191 #else /* defined HAVE_GETPWUID_R */
192 struct passwd pwbuf, *pw = NULL;
193
194 if (getpwuid_r (ruid (), &pwbuf, buf, BUFSIZ, &pw)
195 || &pwbuf != pw
196 || !pw->pw_name)
197 PFATAL ("no password entry for userid %d", ruid ());
198
199 JAM (str_save (pw->pw_name));
200 #endif /* defined HAVE_GETPWUID_R */
201 }
202 checksid (BE (username));
203 #undef JAM
204 }
205 return BE (username);
206 }
207
208 char const *
getcaller(void)209 getcaller (void)
210 /* Get the caller's login name. */
211 {
212 return getusername (currently_setuid_p ());
213 }
214
215 bool
caller_login_p(char const * login)216 caller_login_p (char const *login)
217 {
218 return STR_SAME (getcaller (), login);
219 }
220
221 struct link *
lock_memq(struct link * ls,bool login,void const * x)222 lock_memq (struct link *ls, bool login, void const *x)
223 /* Search ‘ls’, which should be initialized by caller to have its ‘.next’
224 pointing to ‘GROK (locks)’, for a lock that matches ‘x’ and return the
225 link whose cadr is the match, else NULL. If ‘login’, ‘x’ is a login
226 (string), else it is a delta. */
227 {
228 struct rcslock const *rl;
229
230 for (; ls->next; ls = ls->next)
231 {
232 rl = ls->next->entry;
233 if (login
234 ? STR_SAME (x, rl->login)
235 : x == rl->delta)
236 return ls;
237 }
238 return NULL;
239 }
240
241 struct rcslock const *
lock_on(struct delta const * delta)242 lock_on (struct delta const *delta)
243 /* Return the first lock found on ‘delta’, or NULL if no such lock exists. */
244 {
245 for (struct link *ls = GROK (locks); ls; ls = ls->next)
246 {
247 struct rcslock const *rl = ls->entry;
248
249 if (delta == rl->delta)
250 return rl;
251 }
252 return NULL;
253 }
254
255 void
lock_drop(struct link * box,struct link * tp)256 lock_drop (struct link *box, struct link *tp)
257 {
258 struct rcslock const *rl = tp->next->entry;
259
260 rl->delta->lockedby = NULL;
261 tp->next = tp->next->next;
262 GROK (locks) = box->next;
263 }
264
265 int
addlock_maybe(struct delta * delta,bool selfsame,bool verbose)266 addlock_maybe (struct delta *delta, bool selfsame, bool verbose)
267 /* Add a lock held by caller to ‘delta’ and return 1 if successful.
268 Print an error message if ‘verbose’ and return -1 if no lock is
269 added because ‘delta’ is locked by somebody other than caller.
270 (If ‘selfsame’, do this regardless of the caller.)
271 Return 0 if the caller already holds the lock. */
272 {
273 register struct rcslock *rl;
274 struct rcslock const *was = lock_on (delta);
275
276 if (was)
277 {
278 if (!selfsame && caller_login_p (was->login))
279 return 0;
280 if (verbose)
281 RERR ("Revision %s is already locked by %s.", delta->num, was->login);
282 return -1;
283 }
284 rl = FALLOC (struct rcslock);
285 rl->login = delta->lockedby = getcaller ();
286 rl->delta = delta;
287 GROK (locks) = prepend (rl, GROK (locks), SINGLE);
288 return 1;
289 }
290
291 /* b-excwho.c ends here */
292