Makefile: Changed for HPUX10 tidyup.
[Samba.git] / source / smbd / uid.c
blobcdc4e474c61e8489f413c7c811f4500bc7ef703c
1 /*
2 Unix SMB/Netbios implementation.
3 Version 1.9.
4 uid/user handling
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.
22 #include "includes.h"
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 ****************************************************************************/
39 void init_uid(void)
41 initial_uid = current_user.uid = geteuid();
42 initial_gid = current_user.gid = getegid();
44 if (initial_gid != 0 && initial_uid == 0)
46 #ifdef HPUX
47 setresgid(0,0,0);
48 #else
49 setgid(0);
50 setegid(0);
51 #endif
54 initial_uid = geteuid();
55 initial_gid = getegid();
57 current_user.cnum = -1;
59 ChDir(OriginalDir);
63 /****************************************************************************
64 become the specified uid
65 ****************************************************************************/
66 static BOOL become_uid(int uid)
68 if (initial_uid != 0)
69 return(True);
71 if (uid == -1 || uid == 65535) {
72 DEBUG(1,("WARNING: using uid %d is a security risk\n",uid));
75 #ifdef AIX
77 /* AIX 3 stuff - inspired by a code fragment in wu-ftpd */
78 priv_t priv;
80 priv.pv_priv[0] = 0;
81 priv.pv_priv[1] = 0;
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)\n"));
88 #endif
90 #ifdef USE_SETRES
91 if (setresuid(-1,uid,-1) != 0)
92 #elif defined(USE_SETFS)
93 if (setfsuid(uid) != 0)
94 #else
95 if ((seteuid(uid) != 0) &&
96 (setuid(uid) != 0))
97 #endif
99 DEBUG(0,("Couldn't set uid %d currently set to (%d,%d)\n",
100 uid,getuid(), geteuid()));
101 if (uid > 32000)
102 DEBUG(0,("Looks like your OS doesn't like high uid values - try using a different account\n"));
103 return(False);
106 if (((uid == -1) || (uid == 65535)) && geteuid() != uid) {
107 DEBUG(0,("Invalid uid -1. perhaps you have a account with uid 65535?\n"));
108 return(False);
111 current_user.uid = uid;
113 return(True);
117 /****************************************************************************
118 become the specified gid
119 ****************************************************************************/
120 static BOOL become_gid(int gid)
122 if (initial_uid != 0)
123 return(True);
125 if (gid == -1 || gid == 65535) {
126 DEBUG(1,("WARNING: using gid %d is a security risk\n",gid));
129 #ifdef USE_SETRES
130 if (setresgid(-1,gid,-1) != 0)
131 #elif defined(USE_SETFS)
132 if (setfsgid(gid) != 0)
133 #else
134 if (setgid(gid) != 0)
135 #endif
137 DEBUG(0,("Couldn't set gid %d currently set to (%d,%d)\n",
138 gid,getgid(),getegid()));
139 if (gid > 32000)
140 DEBUG(0,("Looks like your OS doesn't like high gid values - try using a different account\n"));
141 return(False);
144 current_user.gid = gid;
146 return(True);
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)
163 BOOL ret;
164 static struct passwd *pass=NULL;
166 if (initial_uid != 0)
167 return(True);
169 if (!pass)
170 pass = Get_Pwnam(lp_guestaccount(-1),True);
171 if (!pass) return(False);
173 ret = become_id(pass->pw_uid,pass->pw_gid);
175 if (!ret)
176 DEBUG(1,("Failed to become guest. Invalid guest account?\n"));
178 current_user.cnum = -2;
180 return(ret);
183 /*******************************************************************
184 check if a username is OK
185 ********************************************************************/
186 static BOOL check_user_ok(int cnum,user_struct *vuser,int snum)
188 int i;
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++;
200 return(True);
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);
210 int snum,gid;
211 int uid;
213 if (current_user.cnum == cnum && vuser != 0 && current_user.id == vuser->uid) {
214 DEBUG(4,("Skipping become_user - already user\n"));
215 return(True);
218 unbecome_user();
220 if (!OPEN_CNUM(cnum)) {
221 DEBUG(2,("Connection %d not open\n",cnum));
222 return(False);
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;
236 } else {
237 if (!vuser) {
238 DEBUG(2,("Invalid vuid used %d\n",vuid));
239 return(False);
241 uid = vuser->uid;
242 if(!*lp_force_group(snum))
243 gid = vuser->gid;
244 else
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);
255 #ifndef NO_SETGROUPS
256 if (!IS_IPC(cnum)) {
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"));
262 #endif
264 if (!Connections[cnum].admin_user && !become_uid(uid))
265 return(False);
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()));
274 return(True);
277 /****************************************************************************
278 unbecome the user of a connection number
279 ****************************************************************************/
280 BOOL unbecome_user(void )
282 if (current_user.cnum == -1)
283 return(False);
285 ChDir(OriginalDir);
287 if (initial_uid == 0)
289 #ifdef USE_SETRES
290 setresuid(-1,getuid(),-1);
291 setresgid(-1,getgid(),-1);
292 #elif defined(USE_SETFS)
293 setfsuid(initial_uid);
294 setfsgid(initial_gid);
295 #else
296 if (seteuid(initial_uid) != 0)
297 setuid(initial_uid);
298 setgid(initial_gid);
299 #endif
301 #ifdef NO_EID
302 if (initial_uid == 0)
303 DEBUG(2,("Running with no EID\n"));
304 initial_uid = getuid();
305 initial_gid = getgid();
306 #else
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();
317 #endif
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;
331 return(True);
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)
341 int fd;
342 struct stat st;
343 mode_t mode = S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH;
344 int flags = O_RDWR|O_CREAT|O_TRUNC|O_EXCL;
346 close(1);
348 if (shared) {
349 /* become root - unprivilaged users can't delete these files */
350 #ifdef USE_SETRES
351 setresgid(0,0,0);
352 setresuid(0,0,0);
353 #else
354 setuid(0);
355 seteuid(0);
356 #endif
359 if(stat(outfile, &st) == 0) {
360 /* Check we're not deleting a device file. */
361 if(st.st_mode & S_IFREG)
362 unlink(outfile);
363 else
364 flags = O_RDWR;
366 /* now create the file */
367 fd = open(outfile,flags,mode);
369 if (fd == -1) return False;
371 if (fd != 1) {
372 if (dup2(fd,1) != 0) {
373 DEBUG(2,("Failed to create stdout file descriptor\n"));
374 close(fd);
375 return False;
377 close(fd);
379 return True;
383 /****************************************************************************
384 run a command being careful about uid/gid handling and putting the output in
385 outfile (or discard it if outfile is NULL).
387 if shared is True then ensure the file will be writeable by all users
388 but created such that its owned by root. This overcomes a security hole.
390 if shared is not set then open the file with O_EXCL set
391 ****************************************************************************/
392 int smbrun(char *cmd,char *outfile,BOOL shared)
394 int fd,pid;
395 int uid = current_user.uid;
396 int gid = current_user.gid;
398 #if USE_SYSTEM
399 int ret;
400 pstring syscmd;
401 char *path = lp_smbrun();
403 /* in the old method we use system() to execute smbrun which then
404 executes the command (using system() again!). This involves lots
405 of shell launches and is very slow. It also suffers from a
406 potential security hole */
407 if (!file_exist(path,NULL))
409 DEBUG(0,("SMBRUN ERROR: Can't find %s. Installation problem?\n",path));
410 return(1);
413 sprintf(syscmd,"%s %d %d \"(%s 2>&1) > %s\"",
414 path,uid,gid,cmd,
415 outfile?outfile:"/dev/null");
417 DEBUG(5,("smbrun - running %s ",syscmd));
418 ret = system(syscmd);
419 DEBUG(5,("gave %d\n",ret));
420 return(ret);
421 #else
422 /* in this newer method we will exec /bin/sh with the correct
423 arguments, after first setting stdout to point at the file */
425 if ((pid=fork())) {
426 int status=0;
427 /* the parent just waits for the child to exit */
428 if (sys_waitpid(pid,&status,0) != pid) {
429 DEBUG(2,("waitpid(%d) : %s\n",pid,strerror(errno)));
430 return -1;
432 return status;
436 /* we are in the child. we exec /bin/sh to do the work for us. we
437 don't directly exec the command we want because it may be a
438 pipeline or anything else the config file specifies */
440 /* point our stdout at the file we want output to go into */
441 if (outfile && !setup_stdout_file(outfile,shared)) {
442 exit(80);
445 /* now completely lose our privilages. This is a fairly paranoid
446 way of doing it, but it does work on all systems that I know of */
447 #ifdef USE_SETRES
448 setresgid(0,0,0);
449 setresuid(0,0,0);
450 setresgid(gid,gid,gid);
451 setresuid(uid,uid,uid);
452 #else
453 setuid(0);
454 seteuid(0);
455 setgid(gid);
456 setegid(gid);
457 setuid(uid);
458 seteuid(uid);
459 #endif
461 if (getuid() != uid || geteuid() != uid ||
462 getgid() != gid || getegid() != gid) {
463 /* we failed to lose our privilages - do not execute the command */
464 exit(81); /* we can't print stuff at this stage, instead use exit codes
465 for debugging */
468 /* close all other file descriptors, leaving only 0, 1 and 2. 0 and
469 2 point to /dev/null from the startup code */
470 for (fd=3;fd<256;fd++) close(fd);
472 execl("/bin/sh","sh","-c",cmd,NULL);
474 /* not reached */
475 exit(82);
476 #endif
477 return 1;