first attempt at making unix setuid/setgid code that is independent of
[samba.git] / source3 / lib / unix_sec_ctxt.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 1.9.
4    uid/user handling
5    Copyright (C) Andrew Tridgell 1992-1998
6    
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11    
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16    
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 */
21
22 #include "includes.h"
23
24 extern int DEBUGLEVEL;
25
26 static uid_t initial_uid;
27 static gid_t initial_gid;
28
29 /* what context is current */
30 struct unix_sec_ctxt curr_ctxt;
31
32 /****************************************************************************
33 initialise the security context routines
34 ****************************************************************************/
35 void init_sec_ctxt(void)
36 {
37         initial_uid = curr_ctxt.uid = geteuid();
38         initial_gid = curr_ctxt.gid = getegid();
39
40         if (initial_gid != 0 && initial_uid == 0) {
41 #ifdef HAVE_SETRESUID
42                 setresgid(0,0,0);
43 #else
44                 setgid(0);
45                 setegid(0);
46 #endif
47         }
48
49         initial_uid = geteuid();
50         initial_gid = getegid();
51 }
52
53
54 /****************************************************************************
55   become the specified uid 
56 ****************************************************************************/
57 static BOOL become_uid(uid_t uid)
58 {
59         if (initial_uid != 0)
60         {
61                 return(True);
62         }
63         
64         if (uid == (uid_t)-1 || ((sizeof(uid_t) == 2) && (uid == (uid_t)65535)))
65         {
66                 static int done;
67                 if (!done) {
68                         DEBUG(1,("WARNING: using uid %d is a security risk\n",(int)uid));
69                         done=1;
70                 }
71         }
72
73 #ifdef HAVE_TRAPDOOR_UID
74 #ifdef HAVE_SETUIDX
75         /* AIX3 has setuidx which is NOT a trapoor function (tridge) */
76         if (setuidx(ID_EFFECTIVE, uid) != 0) {
77                 if (seteuid(uid) != 0) {
78                         DEBUG(1,("Can't set uid %d (setuidx)\n", (int)uid));
79                         return False;
80                 }
81         }
82 #endif
83 #endif
84
85 #ifdef HAVE_SETRESUID
86     if (setresuid(-1,uid,-1) != 0)
87 #else
88     if ((seteuid(uid) != 0) && 
89         (setuid(uid) != 0))
90 #endif
91       {
92         DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
93                  (int)uid,(int)getuid(), (int)geteuid()));
94         if (uid > (uid_t)32000) {
95                 DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
96         }
97         return(False);
98       }
99
100     if (((uid == (uid_t)-1) || ((sizeof(uid_t) == 2) && (uid == 65535))) && (geteuid() != uid))
101         {
102             DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
103             return(False);
104     }
105
106     curr_ctxt.uid = uid;
107
108     return(True);
109 }
110
111
112 /****************************************************************************
113   become the specified gid
114 ****************************************************************************/
115 static BOOL become_gid(gid_t gid)
116 {
117   if (initial_uid != 0)
118     return(True);
119
120   if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && (gid == (gid_t)65535))) {
121     DEBUG(1,("WARNING: using gid %d is a security risk\n",(int)gid));    
122   }
123   
124 #ifdef HAVE_SETRESUID
125   if (setresgid(-1,gid,-1) != 0)
126 #else
127   if (setgid(gid) != 0)
128 #endif
129       {
130         DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
131                  (int)gid,(int)getgid(),(int)getegid()));
132         if (gid > 32000) {
133                 DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
134         }
135         return(False);
136       }
137
138   curr_ctxt.gid = gid;
139
140   return(True);
141 }
142
143
144 /****************************************************************************
145   become the user of a connection number
146 ****************************************************************************/
147 BOOL become_unix_sec_ctxt(struct unix_sec_ctxt const *ctxt)
148 {
149         if (curr_ctxt.uid == ctxt->uid)
150         {
151                 DEBUG(4,("Skipping become_unix_sec_ctxt - already user\n"));
152                 return(True);
153         }
154
155         unbecome_unix_sec_ctxt();
156
157         curr_ctxt.ngroups = ctxt->ngroups;
158         curr_ctxt.groups  = ctxt->groups;
159         curr_ctxt.name    = ctxt->name;
160
161         if (initial_uid == 0)
162         {
163                 if (!become_uid(ctxt->uid)) return(False);
164 #ifdef HAVE_SETGROUPS      
165                 if (curr_ctxt.ngroups > 0)
166                 {
167                         if (setgroups(curr_ctxt.ngroups,
168                                               curr_ctxt.groups) < 0)
169                         {
170                                 DEBUG(0,("setgroups call failed!\n"));
171                         }
172                 }
173 #endif
174                 if (!become_gid(ctxt->gid)) return(False);
175
176         }
177         
178         DEBUG(5,("become_unix_sec_ctxt uid=(%d,%d) gid=(%d,%d)\n",
179                  (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
180   
181         return(True);
182 }
183
184 /****************************************************************************
185   unbecome the user of a connection number
186 ****************************************************************************/
187 BOOL unbecome_unix_sec_ctxt(void)
188 {
189   if (initial_uid == 0)
190     {
191 #ifdef HAVE_SETRESUID
192       setresuid(-1,getuid(),-1);
193       setresgid(-1,getgid(),-1);
194 #else
195       if (seteuid(initial_uid) != 0) 
196         setuid(initial_uid);
197       setgid(initial_gid);
198 #endif
199     }
200
201 #ifdef NO_EID
202   if (initial_uid == 0)
203     DEBUG(2,("Running with no EID\n"));
204   initial_uid = getuid();
205   initial_gid = getgid();
206 #else
207   if (geteuid() != initial_uid) {
208           DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
209           initial_uid = geteuid();
210   }
211   if (getegid() != initial_gid) {
212           DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
213           initial_gid = getegid();
214   }
215 #endif
216
217   curr_ctxt.uid = initial_uid;
218   curr_ctxt.gid = initial_gid;
219   curr_ctxt.name = NULL;
220
221   curr_ctxt.ngroups = 0;
222   curr_ctxt.groups  = NULL;
223
224   DEBUG(5,("unbecome_unix_sec_ctxt now uid=(%d,%d) gid=(%d,%d)\n",
225         (int)getuid(),(int)geteuid(),(int)getgid(),(int)getegid()));
226
227   return(True);
228 }
229
230 static struct unix_sec_ctxt curr_ctxt_saved;
231 static int become_root_depth;
232
233 /****************************************************************************
234 This is used when we need to do a privileged operation (such as mucking
235 with share mode files) and temporarily need root access to do it. This
236 call should always be paired with an unbecome_root() call immediately
237 after the operation
238
239 Set save_dir if you also need to save/restore the CWD 
240 ****************************************************************************/
241 void become_unix_root_sec_ctxt(void) 
242 {
243         if (become_root_depth) {
244                 DEBUG(0,("ERROR: become root depth is non zero\n"));
245         }
246
247         curr_ctxt_saved = curr_ctxt;
248         become_root_depth = 1;
249
250         become_uid(0);
251         become_gid(0);
252 }
253
254 /****************************************************************************
255 When the privileged operation is over call this
256
257 Set save_dir if you also need to save/restore the CWD 
258 ****************************************************************************/
259 void unbecome_unix_root_sec_ctxt(void)
260 {
261         if (become_root_depth != 1)
262         {
263                 DEBUG(0,("ERROR: unbecome root depth is %d\n",
264                          become_root_depth));
265         }
266
267         /* we might have done a become_user() while running as root,
268            if we have then become root again in order to become 
269            non root! */
270         if (curr_ctxt.uid != 0)
271         {
272                 become_uid(0);
273         }
274
275         /* restore our gid first */
276         if (!become_gid(curr_ctxt_saved.gid))
277         {
278                 DEBUG(0,("ERROR: Failed to restore gid\n"));
279                 exit(-1);
280         }
281
282 #ifdef HAVE_SETGROUPS      
283         if (curr_ctxt_saved.ngroups > 0)
284         {
285                 if (setgroups(curr_ctxt_saved.ngroups,
286                                       curr_ctxt_saved.groups) < 0)
287                 {
288                         DEBUG(0,("setgroups call failed!\n"));
289                 }
290         }
291 #endif
292         /* now restore our uid */
293         if (!become_uid(curr_ctxt_saved.uid))
294         {
295                 DEBUG(0,("ERROR: Failed to restore uid\n"));
296                 exit(-1);
297         }
298
299         curr_ctxt = curr_ctxt_saved;
300
301         become_root_depth = 0;
302 }
303