2 Unix SMB/Netbios implementation.
5 Copyright (C) Andrew Tridgell 1992-1998
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 static int initial_uid;
27 static int initial_gid;
29 /* what user is current? */
30 struct current_user current_user;
34 /****************************************************************************
35 initialise the uid routines
36 ****************************************************************************/
39 initial_uid = current_user.uid = geteuid();
40 initial_gid = current_user.gid = getegid();
42 if (initial_gid != 0 && initial_uid == 0) {
51 initial_uid = geteuid();
52 initial_gid = getegid();
54 current_user.cnum = -1;
55 current_user.vuid = UID_FIELD_INVALID;
61 /****************************************************************************
62 become the specified uid
63 ****************************************************************************/
64 static BOOL become_uid(int uid)
66 if (initial_uid != 0) {
70 if (uid == -1 || uid == 65535) {
73 DEBUG(1,("WARNING: using uid %d is a security risk\n",
79 #ifdef HAVE_TRAPDOOR_UID
81 /* AIX3 has setuidx which is NOT a trapoor function (tridge) */
82 if (setuidx(ID_EFFECTIVE, (uid_t)uid) != 0) {
83 if (seteuid((uid_t)uid) != 0) {
84 DEBUG(1,("Can't set uid (setuidx)\n"));
92 if (setresuid(-1,uid,-1) != 0)
94 if ((seteuid(uid) != 0) &&
98 DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
99 uid,getuid(), geteuid()));
101 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));
129 #ifdef HAVE_SETRESUID
130 if (setresgid(-1,gid,-1) != 0)
132 if (setgid(gid) != 0)
135 DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
136 gid,getgid(),getegid()));
138 DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
143 current_user.gid = gid;
149 /****************************************************************************
150 become the specified uid and gid
151 ****************************************************************************/
152 static BOOL become_id(int uid,int gid)
154 return(become_gid(gid) && become_uid(uid));
157 /****************************************************************************
158 become the guest user
159 ****************************************************************************/
160 BOOL become_guest(void)
163 static struct passwd *pass=NULL;
165 if (initial_uid != 0)
169 pass = Get_Pwnam(lp_guestaccount(-1),True);
170 if (!pass) return(False);
173 /* MWW: From AIX FAQ patch to WU-ftpd: call initgroups before setting IDs */
174 initgroups(pass->pw_name, (gid_t)pass->pw_gid);
177 ret = become_id(pass->pw_uid,pass->pw_gid);
180 DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
183 current_user.cnum = -2;
184 current_user.vuid = UID_FIELD_INVALID;
189 /*******************************************************************
190 check if a username is OK
191 ********************************************************************/
192 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
195 for (i=0;i<conn->uid_cache.entries;i++)
196 if (conn->uid_cache.list[i] == vuser->uid) return(True);
198 if (!user_ok(vuser->name,snum)) return(False);
200 i = conn->uid_cache.entries % UID_CACHE_SIZE;
201 conn->uid_cache.list[i] = vuser->uid;
203 if (conn->uid_cache.entries < UID_CACHE_SIZE)
204 conn->uid_cache.entries++;
210 /****************************************************************************
211 become the user of a connection number
212 ****************************************************************************/
213 BOOL become_user(connection_struct *conn, int cnum, uint16 vuid)
215 user_struct *vuser = get_valid_user_struct(vuid);
219 if ((current_user.cnum == cnum) && (vuser != 0) && (current_user.vuid == vuid) &&
220 (current_user.uid == vuser->uid)) {
221 DEBUG(4,("Skipping become_user - already user\n"));
227 if (!(VALID_CNUM(cnum) && conn->open)) {
228 DEBUG(2,("Connection %d not open\n",cnum));
232 snum = conn->service;
234 if((vuser != NULL) && !check_user_ok(conn, vuser, snum))
237 if (conn->force_user ||
238 lp_security() == SEC_SHARE ||
239 !(vuser) || (vuser->guest)
244 current_user.groups = conn->groups;
245 current_user.igroups = conn->igroups;
246 current_user.ngroups = conn->ngroups;
251 DEBUG(2,("Invalid vuid used %d\n",vuid));
255 if(!*lp_force_group(snum))
259 current_user.ngroups = vuser->n_groups;
260 current_user.groups = vuser->groups;
261 current_user.igroups = vuser->igroups;
264 if (initial_uid == 0)
266 if (!become_gid(gid)) return(False);
268 #ifdef HAVE_SETGROUPS
269 if (!(VALID_CNUM(cnum) && conn->ipc)) {
270 /* groups stuff added by ih/wreu */
271 if (current_user.ngroups > 0)
272 if (setgroups(current_user.ngroups,current_user.groups)<0)
273 DEBUG(0,("setgroups call failed!\n"));
277 if (!conn->admin_user && !become_uid(uid))
281 current_user.cnum = cnum;
282 current_user.vuid = vuid;
284 DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
285 getuid(),geteuid(),getgid(),getegid()));
290 /****************************************************************************
291 unbecome the user of a connection number
292 ****************************************************************************/
293 BOOL unbecome_user(void )
295 if (current_user.cnum == -1)
300 if (initial_uid == 0)
302 #ifdef HAVE_SETRESUID
303 setresuid(-1,getuid(),-1);
304 setresgid(-1,getgid(),-1);
306 if (seteuid(initial_uid) != 0)
313 if (initial_uid == 0)
314 DEBUG(2,("Running with no EID\n"));
315 initial_uid = getuid();
316 initial_gid = getgid();
318 if (geteuid() != initial_uid) {
319 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
320 initial_uid = geteuid();
322 if (getegid() != initial_gid) {
323 DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
324 initial_gid = getegid();
328 current_user.uid = initial_uid;
329 current_user.gid = initial_gid;
331 if (ChDir(OriginalDir) != 0)
332 DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
333 timestring(),OriginalDir));
335 DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
336 getuid(),geteuid(),getgid(),getegid()));
338 current_user.cnum = -1;
339 current_user.vuid = UID_FIELD_INVALID;
345 /****************************************************************************
346 This is a utility function of smbrun(). It must be called only from
347 the child as it may leave the caller in a privilaged state.
348 ****************************************************************************/
349 static BOOL setup_stdout_file(char *outfile,BOOL shared)
353 mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
354 int flags = O_RDWR|O_CREAT|O_TRUNC|O_EXCL;
359 /* become root - unprivilaged users can't delete these files */
360 #ifdef HAVE_SETRESUID
369 if(stat(outfile, &st) == 0) {
370 /* Check we're not deleting a device file. */
371 if(st.st_mode & S_IFREG)
376 /* now create the file */
377 fd = open(outfile,flags,mode);
379 if (fd == -1) return False;
382 if (dup2(fd,1) != 0) {
383 DEBUG(2,("Failed to create stdout file descriptor\n"));
393 /****************************************************************************
394 run a command being careful about uid/gid handling and putting the output in
395 outfile (or discard it if outfile is NULL).
397 if shared is True then ensure the file will be writeable by all users
398 but created such that its owned by root. This overcomes a security hole.
400 if shared is not set then open the file with O_EXCL set
401 ****************************************************************************/
402 int smbrun(char *cmd,char *outfile,BOOL shared)
405 int uid = current_user.uid;
406 int gid = current_user.gid;
411 char *path = lp_smbrun();
413 /* in the old method we use system() to execute smbrun which then
414 executes the command (using system() again!). This involves lots
415 of shell launches and is very slow. It also suffers from a
416 potential security hole */
417 if (!file_exist(path,NULL))
419 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
423 slprintf(syscmd,sizeof(syscmd)-1,"%s %d %d \"(%s 2>&1) > %s\"",
425 outfile?outfile:"/dev/null");
427 DEBUG(5,("smbrun - running %s ",syscmd));
428 ret = system(syscmd);
429 DEBUG(5,("gave %d\n",ret));
432 /* in this newer method we will exec /bin/sh with the correct
433 arguments, after first setting stdout to point at the file */
437 /* the parent just waits for the child to exit */
438 if (sys_waitpid(pid,&status,0) != pid) {
439 DEBUG(2,("waitpid(%d) : %s\n",pid,strerror(errno)));
446 /* we are in the child. we exec /bin/sh to do the work for us. we
447 don't directly exec the command we want because it may be a
448 pipeline or anything else the config file specifies */
450 /* point our stdout at the file we want output to go into */
451 if (outfile && !setup_stdout_file(outfile,shared)) {
455 /* now completely lose our privilages. This is a fairly paranoid
456 way of doing it, but it does work on all systems that I know of */
457 #ifdef HAVE_SETRESUID
460 setresgid(gid,gid,gid);
461 setresuid(uid,uid,uid);
471 if (getuid() != uid || geteuid() != uid ||
472 getgid() != gid || getegid() != gid) {
473 /* we failed to lose our privilages - do not execute the command */
474 exit(81); /* we can't print stuff at this stage, instead use exit codes
478 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
479 2 point to /dev/null from the startup code */
480 for (fd=3;fd<256;fd++) close(fd);
482 execl("/bin/sh","sh","-c",cmd,NULL);
490 static struct current_user current_user_saved;
491 static int become_root_depth;
492 static pstring become_root_dir;
494 /****************************************************************************
495 This is used when we need to do a privilaged operation (such as mucking
496 with share mode files) and temporarily need root access to do it. This
497 call should always be paired with an unbecome_root() call immediately
500 Set save_dir if you also need to save/restore the CWD
501 ****************************************************************************/
502 void become_root(BOOL save_dir)
504 if (become_root_depth) {
505 DEBUG(0,("ERROR: become root depth is non zero\n"));
508 GetWd(become_root_dir);
510 current_user_saved = current_user;
511 become_root_depth = 1;
517 /****************************************************************************
518 When the privilaged operation is over call this
520 Set save_dir if you also need to save/restore the CWD
521 ****************************************************************************/
522 void unbecome_root(BOOL restore_dir)
524 if (become_root_depth != 1) {
525 DEBUG(0,("ERROR: unbecome root depth is %d\n",
529 /* we might have done a become_user() while running as root,
530 if we have then become root again in order to become
532 if (current_user.uid != 0) {
536 /* restore our gid first */
537 if (!become_gid(current_user_saved.gid)) {
538 DEBUG(0,("ERROR: Failed to restore gid\n"));
539 exit_server("Failed to restore gid");
542 #ifdef HAVE_SETGROUPS
543 if (current_user_saved.ngroups > 0) {
544 if (setgroups(current_user_saved.ngroups,
545 current_user_saved.groups)<0)
546 DEBUG(0,("ERROR: setgroups call failed!\n"));
550 /* now restore our uid */
551 if (!become_uid(current_user_saved.uid)) {
552 DEBUG(0,("ERROR: Failed to restore uid\n"));
553 exit_server("Failed to restore uid");
557 ChDir(become_root_dir);
559 current_user = current_user_saved;
561 become_root_depth = 0;