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