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
.ngroups
= conn
->ngroups
;
250 DEBUG(2,("Invalid vuid used %d\n",vuid
));
254 if(!*lp_force_group(snum
))
258 current_user
.ngroups
= vuser
->n_groups
;
259 current_user
.groups
= vuser
->groups
;
262 if (initial_uid
== 0)
264 if (!become_gid(gid
)) return(False
);
266 #ifdef HAVE_SETGROUPS
267 if (!(VALID_CNUM(cnum
) && conn
->ipc
)) {
268 /* groups stuff added by ih/wreu */
269 if (current_user
.ngroups
> 0)
270 if (setgroups(current_user
.ngroups
,current_user
.groups
)<0)
271 DEBUG(0,("setgroups call failed!\n"));
275 if (!conn
->admin_user
&& !become_uid(uid
))
279 current_user
.cnum
= cnum
;
280 current_user
.vuid
= vuid
;
282 DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
283 getuid(),geteuid(),getgid(),getegid()));
288 /****************************************************************************
289 unbecome the user of a connection number
290 ****************************************************************************/
291 BOOL
unbecome_user(void )
293 if (current_user
.cnum
== -1)
298 if (initial_uid
== 0)
300 #ifdef HAVE_SETRESUID
301 setresuid(-1,getuid(),-1);
302 setresgid(-1,getgid(),-1);
304 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
) {
317 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
318 initial_uid
= geteuid();
320 if (getegid() != initial_gid
) {
321 DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
322 initial_gid
= getegid();
326 current_user
.uid
= initial_uid
;
327 current_user
.gid
= initial_gid
;
329 if (ChDir(OriginalDir
) != 0)
330 DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
331 timestring(),OriginalDir
));
333 DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
334 getuid(),geteuid(),getgid(),getegid()));
336 current_user
.cnum
= -1;
337 current_user
.vuid
= UID_FIELD_INVALID
;
343 /****************************************************************************
344 This is a utility function of smbrun(). It must be called only from
345 the child as it may leave the caller in a privilaged state.
346 ****************************************************************************/
347 static BOOL
setup_stdout_file(char *outfile
,BOOL shared
)
351 mode_t mode
= S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
;
352 int flags
= O_RDWR
|O_CREAT
|O_TRUNC
|O_EXCL
;
357 /* become root - unprivilaged users can't delete these files */
358 #ifdef HAVE_SETRESUID
367 if(stat(outfile
, &st
) == 0) {
368 /* Check we're not deleting a device file. */
369 if(st
.st_mode
& S_IFREG
)
374 /* now create the file */
375 fd
= open(outfile
,flags
,mode
);
377 if (fd
== -1) return False
;
380 if (dup2(fd
,1) != 0) {
381 DEBUG(2,("Failed to create stdout file descriptor\n"));
391 /****************************************************************************
392 run a command being careful about uid/gid handling and putting the output in
393 outfile (or discard it if outfile is NULL).
395 if shared is True then ensure the file will be writeable by all users
396 but created such that its owned by root. This overcomes a security hole.
398 if shared is not set then open the file with O_EXCL set
399 ****************************************************************************/
400 int smbrun(char *cmd
,char *outfile
,BOOL shared
)
403 int uid
= current_user
.uid
;
404 int gid
= current_user
.gid
;
409 char *path
= lp_smbrun();
411 /* in the old method we use system() to execute smbrun which then
412 executes the command (using system() again!). This involves lots
413 of shell launches and is very slow. It also suffers from a
414 potential security hole */
415 if (!file_exist(path
,NULL
))
417 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path
));
421 slprintf(syscmd
,sizeof(syscmd
)-1,"%s %d %d \"(%s 2>&1) > %s\"",
423 outfile
?outfile
:"/dev/null");
425 DEBUG(5,("smbrun - running %s ",syscmd
));
426 ret
= system(syscmd
);
427 DEBUG(5,("gave %d\n",ret
));
430 /* in this newer method we will exec /bin/sh with the correct
431 arguments, after first setting stdout to point at the file */
435 /* the parent just waits for the child to exit */
436 if (sys_waitpid(pid
,&status
,0) != pid
) {
437 DEBUG(2,("waitpid(%d) : %s\n",pid
,strerror(errno
)));
444 /* we are in the child. we exec /bin/sh to do the work for us. we
445 don't directly exec the command we want because it may be a
446 pipeline or anything else the config file specifies */
448 /* point our stdout at the file we want output to go into */
449 if (outfile
&& !setup_stdout_file(outfile
,shared
)) {
453 /* now completely lose our privilages. This is a fairly paranoid
454 way of doing it, but it does work on all systems that I know of */
455 #ifdef HAVE_SETRESUID
458 setresgid(gid
,gid
,gid
);
459 setresuid(uid
,uid
,uid
);
469 if (getuid() != uid
|| geteuid() != uid
||
470 getgid() != gid
|| getegid() != gid
) {
471 /* we failed to lose our privilages - do not execute the command */
472 exit(81); /* we can't print stuff at this stage, instead use exit codes
476 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
477 2 point to /dev/null from the startup code */
478 for (fd
=3;fd
<256;fd
++) close(fd
);
480 execl("/bin/sh","sh","-c",cmd
,NULL
);
488 static struct current_user current_user_saved
;
489 static int become_root_depth
;
490 static pstring become_root_dir
;
492 /****************************************************************************
493 This is used when we need to do a privilaged operation (such as mucking
494 with share mode files) and temporarily need root access to do it. This
495 call should always be paired with an unbecome_root() call immediately
498 Set save_dir if you also need to save/restore the CWD
499 ****************************************************************************/
500 void become_root(BOOL save_dir
)
502 if (become_root_depth
) {
503 DEBUG(0,("ERROR: become root depth is non zero\n"));
506 GetWd(become_root_dir
);
508 current_user_saved
= current_user
;
509 become_root_depth
= 1;
515 /****************************************************************************
516 When the privilaged operation is over call this
518 Set save_dir if you also need to save/restore the CWD
519 ****************************************************************************/
520 void unbecome_root(BOOL restore_dir
)
522 if (become_root_depth
!= 1) {
523 DEBUG(0,("ERROR: unbecome root depth is %d\n",
527 /* we might have done a become_user() while running as root,
528 if we have then become root again in order to become
530 if (current_user
.uid
!= 0) {
534 /* restore our gid first */
535 if (!become_gid(current_user_saved
.gid
)) {
536 DEBUG(0,("ERROR: Failed to restore gid\n"));
537 exit_server("Failed to restore gid");
540 #ifdef HAVE_SETGROUPS
541 if (current_user_saved
.ngroups
> 0) {
542 if (setgroups(current_user_saved
.ngroups
,
543 current_user_saved
.groups
)<0)
544 DEBUG(0,("ERROR: setgroups call failed!\n"));
548 /* now restore our uid */
549 if (!become_uid(current_user_saved
.uid
)) {
550 DEBUG(0,("ERROR: Failed to restore uid\n"));
551 exit_server("Failed to restore uid");
555 ChDir(become_root_dir
);
557 current_user
= current_user_saved
;
559 become_root_depth
= 0;