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 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;
61 /****************************************************************************
62 become the specified uid
63 ****************************************************************************/
64 static BOOL become_uid(int uid)
69 if (uid == -1 || uid == 65535) {
70 DEBUG(1,("WARNING: using uid %d is a security risk\n",uid));
75 /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
80 if (setpriv(PRIV_SET|PRIV_INHERITED|PRIV_EFFECTIVE|PRIV_BEQUEATH,
81 &priv, sizeof(priv_t)) < 0 ||
82 setuidx(ID_REAL|ID_EFFECTIVE, (uid_t)uid) < 0 ||
83 seteuid((uid_t)uid) < 0)
84 DEBUG(1,("Can't set uid (AIX3)\n"));
89 if (setresuid(-1,uid,-1) != 0)
90 #elif defined(USE_SETFS)
91 if (setfsuid(uid) != 0)
93 if ((seteuid(uid) != 0) &&
97 DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
98 uid,getuid(), geteuid()));
100 DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
104 if (((uid == -1) || (uid == 65535)) && geteuid() != uid) {
105 DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
109 current_user.uid = uid;
115 /****************************************************************************
116 become the specified gid
117 ****************************************************************************/
118 static BOOL become_gid(int gid)
120 if (initial_uid != 0)
123 if (gid == -1 || gid == 65535) {
124 DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));
128 if (setresgid(-1,gid,-1) != 0)
129 #elif defined(USE_SETFS)
130 if (setfsgid(gid) != 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"));
142 current_user.gid = gid;
148 /****************************************************************************
149 become the specified uid and gid
150 ****************************************************************************/
151 static BOOL become_id(int uid,int gid)
153 return(become_gid(gid) && become_uid(uid));
156 /****************************************************************************
157 become the guest user
158 ****************************************************************************/
159 BOOL become_guest(void)
162 static struct passwd *pass=NULL;
164 if (initial_uid != 0)
168 pass = Get_Pwnam(lp_guestaccount(-1),True);
169 if (!pass) return(False);
171 ret = become_id(pass->pw_uid,pass->pw_gid);
174 DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
176 current_user.cnum = -2;
181 /*******************************************************************
182 check if a username is OK
183 ********************************************************************/
184 static BOOL check_user_ok(connection_struct *conn, user_struct *vuser,int snum)
187 for (i=0;i<conn->uid_cache.entries;i++)
188 if (conn->uid_cache.list[i] == vuser->uid) return(True);
190 if (!user_ok(vuser->name,snum)) return(False);
192 i = conn->uid_cache.entries % UID_CACHE_SIZE;
193 conn->uid_cache.list[i] = vuser->uid;
195 if (conn->uid_cache.entries < UID_CACHE_SIZE)
196 conn->uid_cache.entries++;
202 /****************************************************************************
203 become the user of a connection number
204 ****************************************************************************/
205 BOOL become_user(connection_struct *conn, int cnum, uint16 vuid)
207 user_struct *vuser = get_valid_user_struct(vuid);
211 if (current_user.cnum == cnum && vuser != 0 && current_user.id == vuser->uid) {
212 DEBUG(4,("Skipping become_user - already user\n"));
218 if (!(VALID_CNUM(cnum) && conn->open)) {
219 DEBUG(2,("Connection %d not open\n",cnum));
223 snum = conn->service;
225 if (conn->force_user ||
226 lp_security() == SEC_SHARE ||
227 !(vuser) || (vuser->guest) ||
228 !check_user_ok(conn, vuser, snum))
232 current_user.groups = conn->groups;
233 current_user.igroups = conn->igroups;
234 current_user.ngroups = conn->ngroups;
235 current_user.attrs = vuser->attrs;
240 DEBUG(2,("Invalid vuid used %d\n",vuid));
244 if(!*lp_force_group(snum))
248 current_user.ngroups = vuser->n_groups;
249 current_user.groups = vuser->groups;
250 current_user.igroups = vuser->igroups;
251 current_user.attrs = vuser->attrs;
254 if (initial_uid == 0)
256 if (!become_gid(gid)) return(False);
259 if (!(VALID_CNUM(cnum) && conn->ipc)) {
260 /* groups stuff added by ih/wreu */
261 if (current_user.ngroups > 0)
262 if (setgroups(current_user.ngroups,current_user.groups)<0)
263 DEBUG(0,("setgroups call failed!\n"));
267 if (!conn->admin_user && !become_uid(uid))
271 current_user.cnum = cnum;
272 current_user.id = uid;
274 DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
275 getuid(),geteuid(),getgid(),getegid()));
280 /****************************************************************************
281 unbecome the user of a connection number
282 ****************************************************************************/
283 BOOL unbecome_user(void )
285 if (current_user.cnum == -1)
290 if (initial_uid == 0)
293 setresuid(-1,getuid(),-1);
294 setresgid(-1,getgid(),-1);
295 #elif defined(USE_SETFS)
296 setfsuid(initial_uid);
297 setfsgid(initial_gid);
299 if (seteuid(initial_uid) != 0)
305 if (initial_uid == 0)
306 DEBUG(2,("Running with no EID\n"));
307 initial_uid = getuid();
308 initial_gid = getgid();
310 if (geteuid() != initial_uid)
312 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
313 initial_uid = geteuid();
315 if (getegid() != initial_gid)
317 DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
318 initial_gid = getegid();
322 current_user.uid = initial_uid;
323 current_user.gid = initial_gid;
325 if (ChDir(OriginalDir) != 0)
326 DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
327 timestring(),OriginalDir));
329 DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
330 getuid(),geteuid(),getgid(),getegid()));
332 current_user.cnum = -1;
338 /****************************************************************************
339 This is a utility function of smbrun(). It must be called only from
340 the child as it may leave the caller in a privilaged state.
341 ****************************************************************************/
342 static BOOL setup_stdout_file(char *outfile,BOOL shared)
346 mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
347 int flags = O_RDWR|O_CREAT|O_TRUNC|O_EXCL;
352 /* become root - unprivilaged users can't delete these files */
362 if(stat(outfile, &st) == 0) {
363 /* Check we're not deleting a device file. */
364 if(st.st_mode & S_IFREG)
369 /* now create the file */
370 fd = open(outfile,flags,mode);
372 if (fd == -1) return False;
375 if (dup2(fd,1) != 0) {
376 DEBUG(2,("Failed to create stdout file descriptor\n"));
386 /****************************************************************************
387 run a command being careful about uid/gid handling and putting the output in
388 outfile (or discard it if outfile is NULL).
390 if shared is True then ensure the file will be writeable by all users
391 but created such that its owned by root. This overcomes a security hole.
393 if shared is not set then open the file with O_EXCL set
394 ****************************************************************************/
395 int smbrun(char *cmd,char *outfile,BOOL shared)
398 int uid = current_user.uid;
399 int gid = current_user.gid;
404 char *path = lp_smbrun();
406 /* in the old method we use system() to execute smbrun which then
407 executes the command (using system() again!). This involves lots
408 of shell launches and is very slow. It also suffers from a
409 potential security hole */
410 if (!file_exist(path,NULL))
412 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
416 sprintf(syscmd,"%s %d %d \"(%s 2>&1) > %s\"",
418 outfile?outfile:"/dev/null");
420 DEBUG(5,("smbrun - running %s ",syscmd));
421 ret = system(syscmd);
422 DEBUG(5,("gave %d\n",ret));
425 /* in this newer method we will exec /bin/sh with the correct
426 arguments, after first setting stdout to point at the file */
430 /* the parent just waits for the child to exit */
431 if (sys_waitpid(pid,&status,0) != pid) {
432 DEBUG(2,("waitpid(%d) : %s\n",pid,strerror(errno)));
439 /* we are in the child. we exec /bin/sh to do the work for us. we
440 don't directly exec the command we want because it may be a
441 pipeline or anything else the config file specifies */
443 /* point our stdout at the file we want output to go into */
444 if (outfile && !setup_stdout_file(outfile,shared)) {
448 /* now completely lose our privilages. This is a fairly paranoid
449 way of doing it, but it does work on all systems that I know of */
453 setresgid(gid,gid,gid);
454 setresuid(uid,uid,uid);
464 if (getuid() != uid || geteuid() != uid ||
465 getgid() != gid || getegid() != gid) {
466 /* we failed to lose our privilages - do not execute the command */
467 exit(81); /* we can't print stuff at this stage, instead use exit codes
471 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
472 2 point to /dev/null from the startup code */
473 for (fd=3;fd<256;fd++) close(fd);
475 execl("/bin/sh","sh","-c",cmd,NULL);
483 static struct current_user current_user_saved;
484 static int become_root_depth;
485 static pstring become_root_dir;
487 /****************************************************************************
488 This is used when we need to do a privilaged operation (such as mucking
489 with share mode files) and temporarily need root access to do it. This
490 call should always be paired with an unbecome_root() call immediately
493 Set save_dir if you also need to save/restore the CWD
494 ****************************************************************************/
495 void become_root(BOOL save_dir)
497 if (become_root_depth) {
498 DEBUG(0,("ERROR: become root depth is non zero\n"));
501 GetWd(become_root_dir);
503 current_user_saved = current_user;
504 become_root_depth = 1;
510 /****************************************************************************
511 When the privilaged operation is over call this
513 Set save_dir if you also need to save/restore the CWD
514 ****************************************************************************/
515 void unbecome_root(BOOL restore_dir)
517 if (become_root_depth != 1) {
518 DEBUG(0,("ERROR: unbecome root depth is %d\n",
522 /* we might have done a become_user() while running as root,
523 if we have then become root again in order to become
525 if (current_user.uid != 0) {
529 /* restore our gid first */
530 if (!become_gid(current_user_saved.gid)) {
531 DEBUG(0,("ERROR: Failed to restore gid\n"));
532 exit_server("Failed to restore gid");
536 if (current_user_saved.ngroups > 0) {
537 if (setgroups(current_user_saved.ngroups,
538 current_user_saved.groups)<0)
539 DEBUG(0,("ERROR: setgroups call failed!\n"));
543 /* now restore our uid */
544 if (!become_uid(current_user_saved.uid)) {
545 DEBUG(0,("ERROR: Failed to restore uid\n"));
546 exit_server("Failed to restore uid");
550 ChDir(become_root_dir);
552 current_user = current_user_saved;
554 become_root_depth = 0;