charcnv.c: Fixed silly bugs detected on IRIX.
[Samba.git] / source / smbd / uid.c
blob7a903e05511336d72eaf18dd7af424538f295893
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 uid/user handling
5 Copyright (C) Andrew Tridgell 1992-1997
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.
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.
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.
22 #include "includes.h"
24 extern int DEBUGLEVEL;
26 extern connection_struct Connections[];
28 static int initial_uid;
29 static int initial_gid;
31 /* what user is current? */
32 struct current_user current_user;
34 extern pstring OriginalDir;
36 /****************************************************************************
37 initialise the uid routines
38 ****************************************************************************/
39 void init_uid(void)
41 initial_uid = current_user.uid = geteuid();
42 initial_gid = current_user.gid = getegid();
44 if (initial_gid != 0 && initial_uid == 0)
46 #ifdef HPUX
47 setresgid(0,0,0);
48 #else
49 setgid(0);
50 setegid(0);
51 #endif
54 initial_uid = geteuid();
55 initial_gid = getegid();
57 current_user.cnum = -1;
59 ChDir(OriginalDir);
63 /****************************************************************************
64 become the specified uid
65 ****************************************************************************/
66 static BOOL become_uid(int uid)
68 if (initial_uid != 0)
69 return(True);
71 if (uid == -1 || uid == 65535) {
72 DEBUG(1,("WARNING: using uid %d is a security risk\n",uid));
75 #ifdef AIX
77 /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
78 priv_t priv;
80 priv.pv_priv[0] = 0;
81 priv.pv_priv[1] = 0;
82 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
83 &priv, sizeof(priv_t)) < 0 ||
84 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
85 seteuid((uid_t)uid) < 0)
86 DEBUG(1,("Can't set uid (AIX3)"));
88 #endif
90 #ifdef USE_SETRES
91 if (setresuid(-1,uid,-1) != 0)
92 #elif defined(USE_SETFS)
93 if (setfsuid(uid) != 0)
94 #else
95 if ((seteuid(uid) != 0) &&
96 (setuid(uid) != 0))
97 #endif
99 DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
100 uid,getuid(), geteuid()));
101 if (uid > 32000)
102 DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
103 return(False);
106 if (((uid == -1) || (uid == 65535)) && geteuid() != uid) {
107 DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
108 return(False);
111 current_user.uid = uid;
113 return(True);
117 /****************************************************************************
118 become the specified gid
119 ****************************************************************************/
120 static BOOL become_gid(int gid)
122 if (initial_uid != 0)
123 return(True);
125 if (gid == -1 || gid == 65535) {
126 DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));
129 #ifdef USE_SETRES
130 if (setresgid(-1,gid,-1) != 0)
131 #elif defined(USE_SETFS)
132 if (setfsgid(gid) != 0)
133 #else
134 if (setgid(gid) != 0)
135 #endif
137 DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
138 gid,getgid(),getegid()));
139 if (gid > 32000)
140 DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
141 return(False);
144 current_user.gid = gid;
146 return(True);
150 /****************************************************************************
151 become the specified uid and gid
152 ****************************************************************************/
153 static BOOL become_id(int uid,int gid)
155 return(become_gid(gid) && become_uid(uid));
158 /****************************************************************************
159 become the guest user
160 ****************************************************************************/
161 BOOL become_guest(void)
163 BOOL ret;
164 static struct passwd *pass=NULL;
166 if (initial_uid != 0)
167 return(True);
169 if (!pass)
170 pass = Get_Pwnam(lp_guestaccount(-1),True);
171 if (!pass) return(False);
173 ret = become_id(pass->pw_uid,pass->pw_gid);
175 if (!ret)
176 DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
178 current_user.cnum = -2;
180 return(ret);
183 /*******************************************************************
184 check if a username is OK
185 ********************************************************************/
186 static BOOL check_user_ok(int cnum,user_struct *vuser,int snum)
188 int i;
189 for (i=0;i<Connections[cnum].uid_cache.entries;i++)
190 if (Connections[cnum].uid_cache.list[i] == vuser->uid) return(True);
192 if (!user_ok(vuser->name,snum)) return(False);
194 i = Connections[cnum].uid_cache.entries % UID_CACHE_SIZE;
195 Connections[cnum].uid_cache.list[i] = vuser->uid;
197 if (Connections[cnum].uid_cache.entries < UID_CACHE_SIZE)
198 Connections[cnum].uid_cache.entries++;
200 return(True);
204 /****************************************************************************
205 become the user of a connection number
206 ****************************************************************************/
207 BOOL become_user(int cnum, uint16 vuid)
209 user_struct *vuser = get_valid_user_struct(vuid);
210 int snum,gid;
211 int uid;
213 if (current_user.cnum == cnum && vuser != 0 && current_user.id == vuser->uid) {
214 DEBUG(4,("Skipping become_user - already user\n"));
215 return(True);
218 unbecome_user();
220 if (!OPEN_CNUM(cnum)) {
221 DEBUG(2,("Connection %d not open\n",cnum));
222 return(False);
225 snum = Connections[cnum].service;
227 if (Connections[cnum].force_user ||
228 lp_security() == SEC_SHARE ||
229 !(vuser) || (vuser->guest) ||
230 !check_user_ok(cnum,vuser,snum)) {
231 uid = Connections[cnum].uid;
232 gid = Connections[cnum].gid;
233 current_user.groups = Connections[cnum].groups;
234 current_user.igroups = Connections[cnum].igroups;
235 current_user.ngroups = Connections[cnum].ngroups;
236 } else {
237 if (!vuser) {
238 DEBUG(2,("Invalid vuid used %d\n",vuid));
239 return(False);
241 uid = vuser->uid;
242 if(!*lp_force_group(snum))
243 gid = vuser->gid;
244 else
245 gid = Connections[cnum].gid;
246 current_user.groups = vuser->user_groups;
247 current_user.igroups = vuser->user_igroups;
248 current_user.ngroups = vuser->user_ngroups;
251 if (initial_uid == 0)
253 if (!become_gid(gid)) return(False);
255 #ifndef NO_SETGROUPS
256 if (!IS_IPC(cnum)) {
257 /* groups stuff added by ih/wreu */
258 if (current_user.ngroups > 0)
259 if (setgroups(current_user.ngroups,current_user.groups)<0)
260 DEBUG(0,("setgroups call failed!\n"));
262 #endif
264 if (!Connections[cnum].admin_user && !become_uid(uid))
265 return(False);
268 current_user.cnum = cnum;
269 current_user.id = uid;
271 DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
272 getuid(),geteuid(),getgid(),getegid()));
274 return(True);
277 /****************************************************************************
278 unbecome the user of a connection number
279 ****************************************************************************/
280 BOOL unbecome_user(void )
282 if (current_user.cnum == -1)
283 return(False);
285 ChDir(OriginalDir);
287 if (initial_uid == 0)
289 #ifdef USE_SETRES
290 setresuid(-1,getuid(),-1);
291 setresgid(-1,getgid(),-1);
292 #elif defined(USE_SETFS)
293 setfsuid(initial_uid);
294 setfsgid(initial_gid);
295 #else
296 if (seteuid(initial_uid) != 0)
297 setuid(initial_uid);
298 setgid(initial_gid);
299 #endif
301 #ifdef NO_EID
302 if (initial_uid == 0)
303 DEBUG(2,("Running with no EID\n"));
304 initial_uid = getuid();
305 initial_gid = getgid();
306 #else
307 if (geteuid() != initial_uid)
309 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
310 initial_uid = geteuid();
312 if (getegid() != initial_gid)
314 DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
315 initial_gid = getegid();
317 #endif
319 current_user.uid = initial_uid;
320 current_user.gid = initial_gid;
322 if (ChDir(OriginalDir) != 0)
323 DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
324 timestring(),OriginalDir));
326 DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
327 getuid(),geteuid(),getgid(),getegid()));
329 current_user.cnum = -1;
331 return(True);
335 /****************************************************************************
336 This is a utility function of smbrun(). It must be called only from
337 the child as it may leave the caller in a privilaged state.
338 ****************************************************************************/
339 static BOOL setup_stdout_file(char *outfile,BOOL shared)
341 int fd;
342 mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
344 close(1);
346 if (shared) {
347 /* become root - unprivilaged users can't delete these files */
348 #ifdef USE_SETRES
349 setresgid(0,0,0);
350 setresuid(0,0,0);
351 #else
352 setuid(0);
353 seteuid(0);
354 #endif
357 /* now create the file with O_EXCL set */
358 unlink(outfile);
359 fd = open(outfile,O_RDWR|O_CREAT|O_TRUNC|O_EXCL,mode);
361 if (fd == -1) return False;
363 if (fd != 1) {
364 if (dup2(fd,1) != 0) {
365 DEBUG(2,("Failed to create stdout file descriptor\n"));
366 close(fd);
367 return False;
369 close(fd);
371 return True;
375 /****************************************************************************
376 run a command being careful about uid/gid handling and putting the output in
377 outfile (or discard it if outfile is NULL).
379 if shared is True then ensure the file will be writeable by all users
380 but created such that its owned by root. This overcomes a security hole.
382 if shared is not set then open the file with O_EXCL set
383 ****************************************************************************/
384 int smbrun(char *cmd,char *outfile,BOOL shared)
386 int fd,pid;
387 int uid = current_user.uid;
388 int gid = current_user.gid;
390 #if USE_SYSTEM
391 int ret;
392 pstring syscmd;
393 char *path = lp_smbrun();
395 /* in the old method we use system() to execute smbrun which then
396 executes the command (using system() again!). This involves lots
397 of shell launches and is very slow. It also suffers from a
398 potential security hole */
399 if (!file_exist(path,NULL))
401 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
402 return(1);
405 sprintf(syscmd,"%s %d %d \"(%s 2>&1) > %s\"",
406 path,uid,gid,cmd,
407 outfile?outfile:"/dev/null");
409 DEBUG(5,("smbrun - running %s ",syscmd));
410 ret = system(syscmd);
411 DEBUG(5,("gave %d\n",ret));
412 return(ret);
413 #else
414 /* in this newer method we will exec /bin/sh with the correct
415 arguments, after first setting stdout to point at the file */
417 if ((pid=fork())) {
418 int status=0;
419 /* the parent just waits for the child to exit */
420 if (sys_waitpid(pid,&status,0) != pid) {
421 DEBUG(2,("waitpid(%d) : %s\n",pid,strerror(errno)));
422 return -1;
424 return status;
428 /* we are in the child. we exec /bin/sh to do the work for us. we
429 don't directly exec the command we want because it may be a
430 pipeline or anything else the config file specifies */
432 /* point our stdout at the file we want output to go into */
433 if (outfile && !setup_stdout_file(outfile,shared)) {
434 exit(80);
437 /* now completely lose our privilages. This is a fairly paranoid
438 way of doing it, but it does work on all systems that I know of */
439 #ifdef USE_SETRES
440 setresgid(0,0,0);
441 setresuid(0,0,0);
442 setresgid(gid,gid,gid);
443 setresuid(uid,uid,uid);
444 #else
445 setuid(0);
446 seteuid(0);
447 setgid(gid);
448 setegid(gid);
449 setuid(uid);
450 seteuid(uid);
451 #endif
453 if (getuid() != uid || geteuid() != uid ||
454 getgid() != gid || getegid() != gid) {
455 /* we failed to lose our privilages - do not execute the command */
456 exit(81); /* we can't print stuff at this stage, instead use exit codes
457 for debugging */
460 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
461 2 point to /dev/null from the startup code */
462 for (fd=3;fd<256;fd++) close(fd);
464 execl("/bin/sh","sh","-c",cmd,NULL);
466 /* not reached */
467 exit(82);
468 #endif
469 return 1;