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)
52 initial_uid = geteuid();
53 initial_gid = getegid();
55 current_user.cnum = -1;
56 current_user.vuid = UID_FIELD_INVALID;
62 /****************************************************************************
63 become the specified uid
64 ****************************************************************************/
65 static BOOL become_uid(int uid)
70 if (uid == -1 || uid == 65535) {
71 DEBUG(1,("WARNING: using uid %d is a security risk\n",uid));
76 /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
81 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
82 &priv, sizeof(priv_t)) < 0 ||
83 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
84 seteuid((uid_t)uid) < 0)
85 DEBUG(1,("Can't set uid (AIX3)\n"));
90 if (setresuid(-1,uid,-1) != 0)
91 #elif defined(USE_SETFS)
92 if (setfsuid(uid) != 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"));
105 if (((uid == -1) || (uid == 65535)) && geteuid() != uid) {
106 DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
110 current_user.uid = uid;
116 /****************************************************************************
117 become the specified gid
118 ****************************************************************************/
119 static BOOL become_gid(int gid)
121 if (initial_uid != 0)
124 if (gid == -1 || gid == 65535) {
125 DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));
129 if (setresgid(-1,gid,-1) != 0)
130 #elif defined(USE_SETFS)
131 if (setfsgid(gid) != 0)
133 if (setgid(gid) != 0)
136 DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
137 gid,getgid(),getegid()));
139 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);
172 ret = become_id(pass->pw_uid,pass->pw_gid);
175 DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
177 current_user.cnum = -2;
178 current_user.vuid = UID_FIELD_INVALID;
183 /*******************************************************************
184 check if a username is OK
185 ********************************************************************/
186 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
189 for (i=0;i<conn->uid_cache.entries;i++)
190 if (conn->uid_cache.list[i] == vuser->uid) return(True);
192 if (!user_ok(vuser->name,snum)) return(False);
194 i = conn->uid_cache.entries % UID_CACHE_SIZE;
195 conn->uid_cache.list[i] = vuser->uid;
197 if (conn->uid_cache.entries < UID_CACHE_SIZE)
198 conn->uid_cache.entries++;
204 /****************************************************************************
205 become the user of a connection number
206 ****************************************************************************/
207 BOOL become_user(connection_struct *conn, int cnum, uint16 vuid)
209 user_struct *vuser = get_valid_user_struct(vuid);
213 if ((current_user.cnum == cnum) && (vuser != 0) && (current_user.vuid == vuid) &&
214 (current_user.uid == vuser->uid)) {
215 DEBUG(4,("Skipping become_user - already user\n"));
221 if (!(VALID_CNUM(cnum) && conn->open)) {
222 DEBUG(2,("Connection %d not open\n",cnum));
226 snum = conn->service;
228 if((vuser != NULL) && !check_user_ok(conn, vuser, snum))
231 if (conn->force_user ||
232 lp_security() == SEC_SHARE ||
233 !(vuser) || (vuser->guest)
238 current_user.groups = conn->groups;
239 current_user.igroups = conn->igroups;
240 current_user.ngroups = conn->ngroups;
241 current_user.attrs = conn->attrs;
246 DEBUG(2,("Invalid vuid used %d\n",vuid));
250 if(!*lp_force_group(snum))
254 current_user.ngroups = vuser->n_groups;
255 current_user.groups = vuser->groups;
256 current_user.igroups = vuser->igroups;
257 current_user.attrs = vuser->attrs;
260 if (initial_uid == 0)
262 if (!become_gid(gid)) return(False);
265 if (!(VALID_CNUM(cnum) && conn->ipc)) {
266 /* groups stuff added by ih/wreu */
267 if (current_user.ngroups > 0)
268 if (setgroups(current_user.ngroups,current_user.groups)<0)
269 DEBUG(0,("setgroups call failed!\n"));
273 if (!conn->admin_user && !become_uid(uid))
277 current_user.cnum = cnum;
278 current_user.vuid = vuid;
280 DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
281 getuid(),geteuid(),getgid(),getegid()));
286 /****************************************************************************
287 unbecome the user of a connection number
288 ****************************************************************************/
289 BOOL unbecome_user(void )
291 if (current_user.cnum == -1)
296 if (initial_uid == 0)
299 setresuid(-1,getuid(),-1);
300 setresgid(-1,getgid(),-1);
301 #elif defined(USE_SETFS)
302 setfsuid(initial_uid);
303 setfsgid(initial_gid);
305 if (seteuid(initial_uid) != 0)
311 if (initial_uid == 0)
312 DEBUG(2,("Running with no EID\n"));
313 initial_uid = getuid();
314 initial_gid = getgid();
316 if (geteuid() != initial_uid)
318 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
319 initial_uid = geteuid();
321 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 */
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 */
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");
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;