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)"));
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
)) {
231 uid
= Connections
[cnum
].uid
;
232 gid
= Connections
[cnum
].gid
;
233 current_user
.groups
= Connections
[cnum
].groups
;
234 current_user
.igroups
= Connections
[cnum
].igroups
;
235 current_user
.ngroups
= Connections
[cnum
].ngroups
;
238 DEBUG(2,("Invalid vuid used %d\n",vuid
));
242 if(!*lp_force_group(snum
))
245 gid
= Connections
[cnum
].gid
;
246 current_user
.groups
= vuser
->user_groups
;
247 current_user
.igroups
= vuser
->user_igroups
;
248 current_user
.ngroups
= vuser
->user_ngroups
;
251 if (initial_uid
== 0)
253 if (!become_gid(gid
)) return(False
);
257 /* groups stuff added by ih/wreu */
258 if (current_user
.ngroups
> 0)
259 if (setgroups(current_user
.ngroups
,current_user
.groups
)<0)
260 DEBUG(0,("setgroups call failed!\n"));
264 if (!Connections
[cnum
].admin_user
&& !become_uid(uid
))
268 current_user
.cnum
= cnum
;
269 current_user
.id
= uid
;
271 DEBUG(5,("become_user uid=(%d,%d) gid=(%d,%d)\n",
272 getuid(),geteuid(),getgid(),getegid()));
277 /****************************************************************************
278 unbecome the user of a connection number
279 ****************************************************************************/
280 BOOL
unbecome_user(void )
282 if (current_user
.cnum
== -1)
287 if (initial_uid
== 0)
290 setresuid(-1,getuid(),-1);
291 setresgid(-1,getgid(),-1);
292 #elif defined(USE_SETFS)
293 setfsuid(initial_uid
);
294 setfsgid(initial_gid
);
296 if (seteuid(initial_uid
) != 0)
302 if (initial_uid
== 0)
303 DEBUG(2,("Running with no EID\n"));
304 initial_uid
= getuid();
305 initial_gid
= getgid();
307 if (geteuid() != initial_uid
)
309 DEBUG(0,("Warning: You appear to have a trapdoor uid system\n"));
310 initial_uid
= geteuid();
312 if (getegid() != initial_gid
)
314 DEBUG(0,("Warning: You appear to have a trapdoor gid system\n"));
315 initial_gid
= getegid();
319 current_user
.uid
= initial_uid
;
320 current_user
.gid
= initial_gid
;
322 if (ChDir(OriginalDir
) != 0)
323 DEBUG(0,("%s chdir(%s) failed in unbecome_user\n",
324 timestring(),OriginalDir
));
326 DEBUG(5,("unbecome_user now uid=(%d,%d) gid=(%d,%d)\n",
327 getuid(),geteuid(),getgid(),getegid()));
329 current_user
.cnum
= -1;
335 /****************************************************************************
336 This is a utility function of smbrun(). It must be called only from
337 the child as it may leave the caller in a privilaged state.
338 ****************************************************************************/
339 static BOOL
setup_stdout_file(char *outfile
,BOOL shared
)
342 mode_t mode
= S_IWUSR
|S_IRUSR
|S_IRGRP
|S_IROTH
;
347 /* become root - unprivilaged users can't delete these files */
357 /* now create the file with O_EXCL set */
359 fd
= open(outfile
,O_RDWR
|O_CREAT
|O_TRUNC
|O_EXCL
,mode
);
361 if (fd
== -1) return False
;
364 if (dup2(fd
,1) != 0) {
365 DEBUG(2,("Failed to create stdout file descriptor\n"));
375 /****************************************************************************
376 run a command being careful about uid/gid handling and putting the output in
377 outfile (or discard it if outfile is NULL).
379 if shared is True then ensure the file will be writeable by all users
380 but created such that its owned by root. This overcomes a security hole.
382 if shared is not set then open the file with O_EXCL set
383 ****************************************************************************/
384 int smbrun(char *cmd
,char *outfile
,BOOL shared
)
387 int uid
= current_user
.uid
;
388 int gid
= current_user
.gid
;
393 char *path
= lp_smbrun();
395 /* in the old method we use system() to execute smbrun which then
396 executes the command (using system() again!). This involves lots
397 of shell launches and is very slow. It also suffers from a
398 potential security hole */
399 if (!file_exist(path
,NULL
))
401 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path
));
405 sprintf(syscmd
,"%s %d %d \"(%s 2>&1) > %s\"",
407 outfile
?outfile
:"/dev/null");
409 DEBUG(5,("smbrun - running %s ",syscmd
));
410 ret
= system(syscmd
);
411 DEBUG(5,("gave %d\n",ret
));
414 /* in this newer method we will exec /bin/sh with the correct
415 arguments, after first setting stdout to point at the file */
419 /* the parent just waits for the child to exit */
420 if (sys_waitpid(pid
,&status
,0) != pid
) {
421 DEBUG(2,("waitpid(%d) : %s\n",pid
,strerror(errno
)));
428 /* we are in the child. we exec /bin/sh to do the work for us. we
429 don't directly exec the command we want because it may be a
430 pipeline or anything else the config file specifies */
432 /* point our stdout at the file we want output to go into */
433 if (outfile
&& !setup_stdout_file(outfile
,shared
)) {
437 /* now completely lose our privilages. This is a fairly paranoid
438 way of doing it, but it does work on all systems that I know of */
442 setresgid(gid
,gid
,gid
);
443 setresuid(uid
,uid
,uid
);
453 if (getuid() != uid
|| geteuid() != uid
||
454 getgid() != gid
|| getegid() != gid
) {
455 /* we failed to lose our privilages - do not execute the command */
456 exit(81); /* we can't print stuff at this stage, instead use exit codes
460 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
461 2 point to /dev/null from the startup code */
462 for (fd
=3;fd
<256;fd
++) close(fd
);
464 execl("/bin/sh","sh","-c",cmd
,NULL
);