2 Unix SMB/Netbios implementation.
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.
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 ****************************************************************************/
41 initial_uid = current_user.uid = geteuid();
42 initial_gid = current_user.gid = getegid();
44 if (initial_gid != 0 && initial_uid == 0)
54 initial_uid = geteuid();
55 initial_gid = getegid();
57 current_user.cnum = -1;
63 /****************************************************************************
64 become the specified uid
65 ****************************************************************************/
66 static BOOL become_uid(int uid)
71 if (uid == -1 || uid == 65535) {
72 DEBUG(1,("WARNING: using uid %d is a security risk\n",uid));
77 /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
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)\n"));
91 if (setresuid(-1,uid,-1) != 0)
92 #elif defined(USE_SETFS)
93 if (setfsuid(uid) != 0)
95 if ((seteuid(uid) != 0) &&
99 DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
100 uid,getuid(), geteuid()));
102 DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
106 if (((uid == -1) || (uid == 65535)) && geteuid() != uid) {
107 DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
111 current_user.uid = uid;
117 /****************************************************************************
118 become the specified gid
119 ****************************************************************************/
120 static BOOL become_gid(int gid)
122 if (initial_uid != 0)
125 if (gid == -1 || gid == 65535) {
126 DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));
130 if (setresgid(-1,gid,-1) != 0)
131 #elif defined(USE_SETFS)
132 if (setfsgid(gid) != 0)
134 if (setgid(gid) != 0)
137 DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
138 gid,getgid(),getegid()));
140 DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
144 current_user.gid = gid;
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)
164 static struct passwd *pass=NULL;
166 if (initial_uid != 0)
170 pass = Get_Pwnam(lp_guestaccount(-1),True);
171 if (!pass) return(False);
173 ret = become_id(pass->pw_uid,pass->pw_gid);
176 DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
178 current_user.cnum = -2;
183 /*******************************************************************
184 check if a username is OK
185 ********************************************************************/
186 static BOOL check_user_ok(int cnum,user_struct *vuser,int snum)
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++;
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);
213 if (current_user.cnum == cnum && vuser != 0 && current_user.id == vuser->uid) {
214 DEBUG(4,("Skipping become_user - already user\n"));
220 if (!OPEN_CNUM(cnum)) {
221 DEBUG(2,("Connection %d not open\n",cnum));
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))
232 uid = Connections[cnum].uid;
233 gid = Connections[cnum].gid;
234 current_user.groups = Connections[cnum].groups;
235 current_user.igroups = Connections[cnum].igroups;
236 current_user.ngroups = Connections[cnum].ngroups;
237 current_user.attrs = vuser->attrs;
242 DEBUG(2,("Invalid vuid used %d\n",vuid));
246 if(!*lp_force_group(snum))
249 gid = Connections[cnum].gid;
250 current_user.ngroups = vuser->n_groups;
251 current_user.groups = vuser->groups;
252 current_user.igroups = vuser->igroups;
253 current_user.attrs = vuser->attrs;
256 if (initial_uid == 0)
258 if (!become_gid(gid)) return(False);
262 /* groups stuff added by ih/wreu */
263 if (current_user.ngroups > 0)
264 if (setgroups(current_user.ngroups,current_user.groups)<0)
265 DEBUG(0,("setgroups call failed!\n"));
269 if (!Connections[cnum].admin_user && !become_uid(uid))
273 current_user.cnum = cnum;
274 current_user.id = uid;
276 DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
277 getuid(),geteuid(),getgid(),getegid()));
282 /****************************************************************************
283 unbecome the user of a connection number
284 ****************************************************************************/
285 BOOL unbecome_user(void )
287 if (current_user.cnum == -1)
292 if (initial_uid == 0)
295 setresuid(-1,getuid(),-1);
296 setresgid(-1,getgid(),-1);
297 #elif defined(USE_SETFS)
298 setfsuid(initial_uid);
299 setfsgid(initial_gid);
301 if (seteuid(initial_uid) != 0)
307 if (initial_uid == 0)
308 DEBUG(2,("Running with no EID\n"));
309 initial_uid = getuid();
310 initial_gid = getgid();
312 if (geteuid() != initial_uid)
314 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
315 initial_uid = geteuid();
317 if (getegid() != initial_gid)
319 DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
320 initial_gid = getegid();
324 current_user.uid = initial_uid;
325 current_user.gid = initial_gid;
327 if (ChDir(OriginalDir) != 0)
328 DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
329 timestring(),OriginalDir));
331 DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
332 getuid(),geteuid(),getgid(),getegid()));
334 current_user.cnum = -1;
340 /****************************************************************************
341 This is a utility function of smbrun(). It must be called only from
342 the child as it may leave the caller in a privilaged state.
343 ****************************************************************************/
344 static BOOL setup_stdout_file(char *outfile,BOOL shared)
348 mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
349 int flags = O_RDWR|O_CREAT|O_TRUNC|O_EXCL;
354 /* become root - unprivilaged users can't delete these files */
364 if(stat(outfile, &st) == 0) {
365 /* Check we're not deleting a device file. */
366 if(st.st_mode & S_IFREG)
371 /* now create the file */
372 fd = open(outfile,flags,mode);
374 if (fd == -1) return False;
377 if (dup2(fd,1) != 0) {
378 DEBUG(2,("Failed to create stdout file descriptor\n"));
388 /****************************************************************************
389 run a command being careful about uid/gid handling and putting the output in
390 outfile (or discard it if outfile is NULL).
392 if shared is True then ensure the file will be writeable by all users
393 but created such that its owned by root. This overcomes a security hole.
395 if shared is not set then open the file with O_EXCL set
396 ****************************************************************************/
397 int smbrun(char *cmd,char *outfile,BOOL shared)
400 int uid = current_user.uid;
401 int gid = current_user.gid;
406 char *path = lp_smbrun();
408 /* in the old method we use system() to execute smbrun which then
409 executes the command (using system() again!). This involves lots
410 of shell launches and is very slow. It also suffers from a
411 potential security hole */
412 if (!file_exist(path,NULL))
414 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
418 sprintf(syscmd,"%s %d %d \"(%s 2>&1) > %s\"",
420 outfile?outfile:"/dev/null");
422 DEBUG(5,("smbrun - running %s ",syscmd));
423 ret = system(syscmd);
424 DEBUG(5,("gave %d\n",ret));
427 /* in this newer method we will exec /bin/sh with the correct
428 arguments, after first setting stdout to point at the file */
432 /* the parent just waits for the child to exit */
433 if (sys_waitpid(pid,&status,0) != pid) {
434 DEBUG(2,("waitpid(%d) : %s\n",pid,strerror(errno)));
441 /* we are in the child. we exec /bin/sh to do the work for us. we
442 don't directly exec the command we want because it may be a
443 pipeline or anything else the config file specifies */
445 /* point our stdout at the file we want output to go into */
446 if (outfile && !setup_stdout_file(outfile,shared)) {
450 /* now completely lose our privilages. This is a fairly paranoid
451 way of doing it, but it does work on all systems that I know of */
455 setresgid(gid,gid,gid);
456 setresuid(uid,uid,uid);
466 if (getuid() != uid || geteuid() != uid ||
467 getgid() != gid || getegid() != gid) {
468 /* we failed to lose our privilages - do not execute the command */
469 exit(81); /* we can't print stuff at this stage, instead use exit codes
473 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
474 2 point to /dev/null from the startup code */
475 for (fd=3;fd<256;fd++) close(fd);
477 execl("/bin/sh","sh","-c",cmd,NULL);