Run most of the code as root, not the invoking user. It doesn't really

gain us anything to run as the user since an attacker can just have
an setuid(0) in their egg.  Running as root solves potential problems
wrt signalling.
This commit is contained in:
Todd C. Miller
1999-08-20 20:37:16 +00:00
parent bc65b24ed3
commit 275c2fc980
9 changed files with 61 additions and 94 deletions

View File

@@ -31,8 +31,8 @@ The variables in the struct are as follows:
initialized in the "init" or "setup" routines. initialized in the "init" or "setup" routines.
Possible values of sudo_auth.flags: Possible values of sudo_auth.flags:
FLAG_ROOT Whether or not the auth functions should run with FLAG_USER Whether or not the auth functions should run with
an euid of 0 or the uid of the invoking user. the euid of the invoking user instead of 0.
FLAG_CONFIGURED If set then the auth method is assumed to have been FLAG_CONFIGURED If set then the auth method is assumed to have been
configured successfully. All auth methods start out configured successfully. All auth methods start out
@@ -102,23 +102,23 @@ in sudo_auth.h. For instance, for a method, ``fooauth'', add:
#elif defined(HAVE_FOOAUTH) #elif defined(HAVE_FOOAUTH)
# define AUTH_STANDALONE \ # define AUTH_STANDALONE \
AUTH_ENTRY(FLAG_ROOT, "foo", \ AUTH_ENTRY(0, "foo", \
foo_init, foo_setup, foo_verify, foo_cleanup) foo_init, foo_setup, foo_verify, foo_cleanup)
If the method doesn't need to run as root, replace FLAG_ROOT with 0. If the method needs to run as the user, not root, replace the first
If you don't have a init/setup/cleanup routine, just use a NULL for that parameter to AUTH_ENTRY (0) with FLAG_USER. If you don't have a
field. init/setup/cleanup routine, just use a NULL for that field.
For a normal authentication method, add it to the ``auth_switch'' in For a normal authentication method, add it to the ``auth_switch'' in
sudo_auth.c. If ``fooauth'' is a normal auth method, its entry sudo_auth.c. If ``fooauth'' is a normal auth method, its entry
would look like: would look like:
# ifdef HAVE_FOOAUTH # ifdef HAVE_FOOAUTH
AUTH_ENTRY(FLAG_ROOT, "foo", foo_init, foo_setup, foo_verify, foo_cleanup) AUTH_ENTRY(0, "foo", foo_init, foo_setup, foo_verify, foo_cleanup)
# endif # endif
Again, if the method doesn't need to run as root, replace FLAG_ROOT Again, if the method doesn't need to run as root, replace the 0 with
with 0. Likewise, if you don't have a init/setup/cleanup routine, FLAG_USER. Likewise, if you don't have a init/setup/cleanup routine,
just use a NULL for that field. just use a NULL for that field.
NOTE: You should not make a method both ``standalone'' and NOTE: You should not make a method both ``standalone'' and

View File

@@ -65,25 +65,25 @@ sudo_auth auth_switch[] = {
AUTH_STANDALONE AUTH_STANDALONE
#else #else
# ifndef WITHOUT_PASSWD # ifndef WITHOUT_PASSWD
AUTH_ENTRY(FLAG_ROOT, "passwd", NULL, NULL, passwd_verify, NULL) AUTH_ENTRY(0, "passwd", NULL, NULL, passwd_verify, NULL)
# endif # endif
# if defined(HAVE_SECUREWARE) && !defined(WITHOUT_PASSWD) # if defined(HAVE_SECUREWARE) && !defined(WITHOUT_PASSWD)
AUTH_ENTRY(FLAG_ROOT, "secureware", secureware_init, NULL, secureware_verify, NULL) AUTH_ENTRY(0, "secureware", secureware_init, NULL, secureware_verify, NULL)
# endif # endif
# ifdef HAVE_AFS # ifdef HAVE_AFS
AUTH_ENTRY(FLAG_ROOT, "afs", NULL, NULL, afs_verify, NULL) AUTH_ENTRY(0, "afs", NULL, NULL, afs_verify, NULL)
# endif # endif
# ifdef HAVE_KERB4 # ifdef HAVE_KERB4
AUTH_ENTRY(FLAG_ROOT, "kerb4", kerb4_init, NULL, kerb4_verify, NULL) AUTH_ENTRY(0, "kerb4", kerb4_init, NULL, kerb4_verify, NULL)
# endif # endif
# ifdef HAVE_KERB5 # ifdef HAVE_KERB5
AUTH_ENTRY(FLAG_ROOT, "kerb5", kerb5_init, NULL, kerb5_verify, NULL) AUTH_ENTRY(0, "kerb5", kerb5_init, NULL, kerb5_verify, NULL)
# endif # endif
# ifdef HAVE_SKEY # ifdef HAVE_SKEY
AUTH_ENTRY(FLAG_ROOT, "S/Key", NULL, rfc1938_setup, rfc1938_verify, NULL) AUTH_ENTRY(0, "S/Key", NULL, rfc1938_setup, rfc1938_verify, NULL)
# endif # endif
# ifdef HAVE_OPIE # ifdef HAVE_OPIE
AUTH_ENTRY(FLAG_ROOT, "OPIE", NULL, rfc1938_setup, rfc1938_verify, NULL) AUTH_ENTRY(0, "OPIE", NULL, rfc1938_setup, rfc1938_verify, NULL)
# endif # endif
#endif /* AUTH_STANDALONE */ #endif /* AUTH_STANDALONE */
AUTH_ENTRY(0, NULL, NULL, NULL, NULL, NULL) AUTH_ENTRY(0, NULL, NULL, NULL, NULL, NULL)
@@ -107,8 +107,8 @@ verify_user()
/* Initialize auth methods and unconfigure the method if necessary. */ /* Initialize auth methods and unconfigure the method if necessary. */
for (auth = auth_switch; auth->name; auth++) { for (auth = auth_switch; auth->name; auth++) {
if (auth->init && IS_CONFIGURED(auth)) { if (auth->init && IS_CONFIGURED(auth)) {
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_ROOT, 0); set_perms(PERM_USER, 0);
status = (auth->init)(sudo_user.pw, &user_prompt, auth); status = (auth->init)(sudo_user.pw, &user_prompt, auth);
if (status == AUTH_FAILURE) if (status == AUTH_FAILURE)
@@ -116,8 +116,8 @@ verify_user()
else if (status == AUTH_FATAL) /* XXX log */ else if (status == AUTH_FATAL) /* XXX log */
exit(1); /* assume error msg already printed */ exit(1); /* assume error msg already printed */
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_USER, 0); set_perms(PERM_ROOT, 0);
} }
} }
@@ -125,8 +125,8 @@ verify_user()
/* Do any per-method setup and unconfigure the method if needed */ /* Do any per-method setup and unconfigure the method if needed */
for (auth = auth_switch; auth->name; auth++) { for (auth = auth_switch; auth->name; auth++) {
if (auth->setup && IS_CONFIGURED(auth)) { if (auth->setup && IS_CONFIGURED(auth)) {
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_ROOT, 0); set_perms(PERM_USER, 0);
status = (auth->setup)(sudo_user.pw, &user_prompt, auth); status = (auth->setup)(sudo_user.pw, &user_prompt, auth);
if (status == AUTH_FAILURE) if (status == AUTH_FAILURE)
@@ -134,8 +134,8 @@ verify_user()
else if (status == AUTH_FATAL) /* XXX log */ else if (status == AUTH_FATAL) /* XXX log */
exit(1); /* assume error msg already printed */ exit(1); /* assume error msg already printed */
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_USER, 0); set_perms(PERM_ROOT, 0);
} }
} }
@@ -154,14 +154,14 @@ verify_user()
if (!IS_CONFIGURED(auth)) if (!IS_CONFIGURED(auth))
continue; continue;
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_ROOT, 0); set_perms(PERM_USER, 0);
success = auth->status = (auth->verify)(sudo_user.pw, p, auth); success = auth->status = (auth->verify)(sudo_user.pw, p, auth);
(void) memset(p, 0, strlen(p)); (void) memset(p, 0, strlen(p));
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_USER, 0); set_perms(PERM_ROOT, 0);
if (auth->status != AUTH_FAILURE) if (auth->status != AUTH_FAILURE)
goto cleanup; goto cleanup;
@@ -182,15 +182,15 @@ cleanup:
/* Call cleanup routines. */ /* Call cleanup routines. */
for (auth = auth_switch; auth->name; auth++) { for (auth = auth_switch; auth->name; auth++) {
if (auth->cleanup && IS_CONFIGURED(auth)) { if (auth->cleanup && IS_CONFIGURED(auth)) {
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_ROOT, 0); set_perms(PERM_USER, 0);
status = (auth->cleanup)(sudo_user.pw, auth); status = (auth->cleanup)(sudo_user.pw, auth);
if (status == AUTH_FATAL) /* XXX log */ if (status == AUTH_FATAL) /* XXX log */
exit(1); /* assume error msg already printed */ exit(1); /* assume error msg already printed */
if (NEEDS_ROOT(auth)) if (NEEDS_USER(auth))
set_perms(PERM_USER, 0); set_perms(PERM_ROOT, 0);
} }
} }

View File

@@ -55,12 +55,12 @@ typedef struct sudo_auth {
/* Values for sudo_auth.flags. */ /* Values for sudo_auth.flags. */
/* XXX - these names are too long for my liking */ /* XXX - these names are too long for my liking */
#define FLAG_ROOT 0x01 /* functions must run as root */ #define FLAG_USER 0x01 /* functions must run as root */
#define FLAG_CONFIGURED 0x02 /* method configured ok */ #define FLAG_CONFIGURED 0x02 /* method configured ok */
#define FLAG_ONEANDONLY 0x04 /* one and only auth method */ #define FLAG_ONEANDONLY 0x04 /* one and only auth method */
/* Shortcuts for using the flags above. */ /* Shortcuts for using the flags above. */
#define NEEDS_ROOT(x) ((x)->flags & FLAG_ROOT) #define NEEDS_USER(x) ((x)->flags & FLAG_USER)
#define IS_CONFIGURED(x) ((x)->flags & FLAG_CONFIGURED) #define IS_CONFIGURED(x) ((x)->flags & FLAG_CONFIGURED)
#define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY) #define IS_ONEANDONLY(x) ((x)->flags & FLAG_ONEANDONLY)
@@ -99,27 +99,27 @@ int securid_verify __P((struct passwd *pw, char *pass, sudo_auth *auth));
/* Some methods cannots (or should not) interoperate with any others */ /* Some methods cannots (or should not) interoperate with any others */
#if defined(HAVE_PAM) #if defined(HAVE_PAM)
# define AUTH_STANDALONE \ # define AUTH_STANDALONE \
AUTH_ENTRY(FLAG_ROOT, "pam", \ AUTH_ENTRY(0, "pam", \
pam_init, NULL, pam_verify, pam_cleanup) pam_init, NULL, pam_verify, pam_cleanup)
#elif defined(HAVE_SECURID) #elif defined(HAVE_SECURID)
# define AUTH_STANDALONE \ # define AUTH_STANDALONE \
AUTH_ENTRY(FLAG_ROOT, "SecurId", \ AUTH_ENTRY(0, "SecurId",
securid_init, securid_setup, securid_verify, NULL) securid_init, securid_setup, securid_verify, NULL)
#elif defined(HAVE_SIA) #elif defined(HAVE_SIA)
# define AUTH_STANDALONE \ # define AUTH_STANDALONE \
AUTH_ENTRY(FLAG_ROOT, "sia", \ AUTH_ENTRY(0, "sia", \
NULL, sia_setup, sia_verify, sia_cleanup) NULL, sia_setup, sia_verify, sia_cleanup)
#elif defined(HAVE_DCE) #elif defined(HAVE_DCE)
# define AUTH_STANDALONE \ # define AUTH_STANDALONE \
AUTH_ENTRY(FLAG_ROOT, "dce", \ AUTH_ENTRY(0, "dce", \
NULL, NULL, dce_verify, NULL) NULL, NULL, dce_verify, NULL)
#elif defined(HAVE_AUTHENTICATE) #elif defined(HAVE_AUTHENTICATE)
# define AUTH_STANDALONE \ # define AUTH_STANDALONE \
AUTH_ENTRY(FLAG_ROOT, "aixauth", \ AUTH_ENTRY(0, "aixauth", \
NULL, NULL, aixauth_verify, NULL) NULL, NULL, aixauth_verify, NULL)
#elif defined(HAVE_FWTK) #elif defined(HAVE_FWTK)
# define AUTH_STANDALONE \ # define AUTH_STANDALONE \
AUTH_ENTRY(FLAG_ROOT, "fwtk", fwtk_init, \ AUTH_ENTRY(0, "fwtk", fwtk_init, \
NULL, fwtk_verify, fwtk_cleanup) NULL, fwtk_verify, fwtk_cleanup)
#endif #endif

13
check.c
View File

@@ -137,8 +137,6 @@ update_timestamp(timestampdir, timestampfile)
char *timestampfile; char *timestampfile;
{ {
set_perms(PERM_ROOT, 0); /* become root */
if (touch(timestampfile ? timestampfile : timestampdir, time(NULL)) == -1) { if (touch(timestampfile ? timestampfile : timestampdir, time(NULL)) == -1) {
if (timestampfile) { if (timestampfile) {
int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600); int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600);
@@ -152,8 +150,6 @@ update_timestamp(timestampdir, timestampfile)
log_error(NO_EXIT|USE_ERRNO, "Can't mkdir %s", timestampdir); log_error(NO_EXIT|USE_ERRNO, "Can't mkdir %s", timestampdir);
} }
} }
set_perms(PERM_USER, 0); /* relinquish root */
} }
/* /*
@@ -288,8 +284,6 @@ timestamp_status(timestampdir, timestampfile, user, make_dirs)
time_t now; time_t now;
int status = TS_ERROR; /* assume the worst */ int status = TS_ERROR; /* assume the worst */
set_perms(PERM_ROOT, 0); /* become root */
/* /*
* Sanity check _PATH_SUDO_TIMEDIR and make it if it doesn't already exist. * Sanity check _PATH_SUDO_TIMEDIR and make it if it doesn't already exist.
* We start out assuming the worst (that the dir is not sane) and * We start out assuming the worst (that the dir is not sane) and
@@ -325,10 +319,8 @@ timestamp_status(timestampdir, timestampfile, user, make_dirs)
status = TS_MISSING; status = TS_MISSING;
} }
} }
if (status == TS_ERROR) { if (status == TS_ERROR)
set_perms(PERM_USER, 0); /* relinquish root */
return(status); return(status);
}
/* /*
* Sanity check the user's ticket dir. We start by downgrading * Sanity check the user's ticket dir. We start by downgrading
@@ -435,7 +427,6 @@ timestamp_status(timestampdir, timestampfile, user, make_dirs)
} }
} }
set_perms(PERM_USER, 0); /* relinquish root */
return(status); return(status);
} }
@@ -455,7 +446,6 @@ remove_timestamp(remove)
status = timestamp_status(timestampdir, timestampfile, user_name, FALSE); status = timestamp_status(timestampdir, timestampfile, user_name, FALSE);
if (status == TS_OLD || status == TS_CURRENT) { if (status == TS_OLD || status == TS_CURRENT) {
ts = timestampfile ? timestampfile : timestampdir; ts = timestampfile ? timestampfile : timestampdir;
set_perms(PERM_ROOT, 0); /* become root */
if (remove) { if (remove) {
if (timestampfile) if (timestampfile)
status = unlink(timestampfile); status = unlink(timestampfile);
@@ -471,7 +461,6 @@ remove_timestamp(remove)
(void) fprintf(stderr, "%s: can't reset %s to epoch: %s\n", (void) fprintf(stderr, "%s: can't reset %s to epoch: %s\n",
Argv[0], ts, strerror(errno)); Argv[0], ts, strerror(errno));
} }
set_perms(PERM_USER, 0); /* relinquish root */
} }
(void) free(timestampdir); (void) free(timestampdir);

View File

@@ -112,7 +112,6 @@ find_path(infile, outfile)
path = estrdup(path); path = estrdup(path);
origpath = path; origpath = path;
/* XXX use strtok() */
do { do {
if ((n = strchr(path, ':'))) if ((n = strchr(path, ':')))
*n = '\0'; *n = '\0';

View File

@@ -67,19 +67,12 @@ sudo_goodpath(path)
const char *path; const char *path;
{ {
struct stat sb; struct stat sb;
int err;
/* Check for brain damage */ /* Check for brain damage */
if (path == NULL || path[0] == '\0') if (path == NULL || path[0] == '\0')
return(NULL); return(NULL);
/* Do the stat() as root. */ if (stat(path, &sb))
set_perms(PERM_ROOT, 0);
err = stat(path, &sb);
set_perms(PERM_USER, 0);
/* stat() failed */
if (err)
return(NULL); return(NULL);
/* Make sure path describes an executable regular file. */ /* Make sure path describes an executable regular file. */

View File

@@ -161,15 +161,10 @@ do_logfile(msg)
FILE *fp; FILE *fp;
mode_t oldmask; mode_t oldmask;
time_t now; time_t now;
int oldeuid = geteuid();
int maxlen = MAXLOGFILELEN; int maxlen = MAXLOGFILELEN;
now = time((time_t) 0); now = time((time_t) 0);
/* Become root if we are not already. */
if (oldeuid)
set_perms(PERM_ROOT, 0);
oldmask = umask(077); oldmask = umask(077);
fp = fopen(_PATH_SUDO_LOGFILE, "a"); fp = fopen(_PATH_SUDO_LOGFILE, "a");
(void) umask(oldmask); (void) umask(oldmask);
@@ -254,9 +249,6 @@ do_logfile(msg)
(void) lock_file(fileno(fp), SUDO_UNLOCK); (void) lock_file(fileno(fp), SUDO_UNLOCK);
(void) fclose(fp); (void) fclose(fp);
} }
if (oldeuid)
set_perms(PERM_USER, 0); /* relinquish root */
} }
#endif /* LOGGING & SLOG_FILE */ #endif /* LOGGING & SLOG_FILE */
@@ -299,20 +291,18 @@ log_auth(status, inform_user)
mail_auth(status, logline); /* send mail based on status */ mail_auth(status, logline); /* send mail based on status */
/* Inform the user if they failed to authenticate. */ /* Inform the user if they failed to authenticate. */
if (inform_user) { if (inform_user && (status & VALIDATE_NOT_OK)) {
if (status & FLAG_NO_USER) if (status & FLAG_NO_USER)
(void) fprintf(stderr, "%s is not in the sudoers file. %s", (void) fprintf(stderr, "%s is not in the sudoers file. %s",
user_name, "This incident will be reported.\n"); user_name, "This incident will be reported.\n");
else if (status & FLAG_NO_HOST) else if (status & FLAG_NO_HOST)
(void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s", (void) fprintf(stderr, "%s is not allowed to run sudo on %s. %s",
user_name, user_shost, "This incident will be reported.\n"); user_name, user_shost, "This incident will be reported.\n");
else if (status & VALIDATE_NOT_OK) else
(void) fprintf(stderr, (void) fprintf(stderr,
"Sorry, user %s is not allowed to execute '%s%s%s' as %s on %s.\n", "Sorry, user %s is not allowed to execute '%s%s%s' as %s on %s.\n",
user_name, user_cmnd, user_args ? " " : "", user_name, user_cmnd, user_args ? " " : "",
user_args ? user_args : "", user_runas, user_host); user_args ? user_args : "", user_runas, user_host);
else
(void) fprintf(stderr, "An unknown error has occurred.\n");
} }
/* /*
@@ -351,6 +341,10 @@ log_error(va_alist)
fmt = va_arg(ap, const char *); fmt = va_arg(ap, const char *);
#endif #endif
/* Become root if we are not already to avoid user control */
if (geteuid() != 0)
set_perms(PERM_ROOT, 0);
/* Expand printf-style format + args. */ /* Expand printf-style format + args. */
evasprintf(&message, fmt, ap); evasprintf(&message, fmt, ap);
va_end(ap); va_end(ap);
@@ -409,6 +403,9 @@ log_error(va_alist)
free(message); free(message);
} }
/*
* Send a message to ALERTMAIL
*/
#ifdef _PATH_SENDMAIL #ifdef _PATH_SENDMAIL
static void static void
send_mail(line) send_mail(line)
@@ -481,7 +478,7 @@ send_mail(line)
user_name, line); user_name, line);
fclose(mail); fclose(mail);
reapchild(0); reapchild(0);
exit(0); _exit(0);
} else { } else {
/* Parent, just return unless there is an error. */ /* Parent, just return unless there is an error. */
if (pid == -1) { if (pid == -1) {
@@ -510,6 +507,7 @@ mail_auth(status, line)
{ {
int mail_mask; int mail_mask;
/* If any of these bits are set in status, we send mail. */
mail_mask = VALIDATE_ERROR; mail_mask = VALIDATE_ERROR;
#ifdef SEND_MAIL_WHEN_OK #ifdef SEND_MAIL_WHEN_OK
mail_mask |= VALIDATE_OK; mail_mask |= VALIDATE_OK;

22
parse.c
View File

@@ -124,26 +124,17 @@ sudoers_lookup(check_cmnd)
yyin = sudoers_fp; yyin = sudoers_fp;
yyout = stdout; yyout = stdout;
/* /* Allocate space for data structures in the parser. */
* Allocate space for data structures in the parser.
*/
init_parser(); init_parser();
/* /* Need to be root while stat'ing things in the parser. */
* Need to be root while stat'ing things in the parser.
*/
set_perms(PERM_ROOT, 0); set_perms(PERM_ROOT, 0);
error = yyparse(); error = yyparse();
/* /* Close the sudoers file now that we are done with it. */
* Don't need to keep this open...
*/
(void) fclose(sudoers_fp); (void) fclose(sudoers_fp);
sudoers_fp = NULL; sudoers_fp = NULL;
/* relinquish extra privs */
set_perms(PERM_USER, 0);
if (error || parse_error) if (error || parse_error)
return(VALIDATE_ERROR); return(VALIDATE_ERROR);
@@ -159,10 +150,9 @@ sudoers_lookup(check_cmnd)
} }
/* /*
* Only check the actual command if the check_cmnd * Only check the actual command if the check_cmnd flag is set.
* flag is set. It is not set for the "validate" * It is not set for the "validate" and "list" pseudo-commands.
* and "list" pseudo-commands. Always check the * Always check the host and user.
* host and user.
*/ */
if (check_cmnd == FALSE) if (check_cmnd == FALSE)
while (top) { while (top) {

6
sudo.c
View File

@@ -258,7 +258,7 @@ main(argc, argv)
cmnd_status = init_vars(sudo_mode); cmnd_status = init_vars(sudo_mode);
set_perms(PERM_USER, sudo_mode); /* At this point, ruid == euid == 0 */
check_sudoers(); /* check mode/owner on _PATH_SUDOERS */ check_sudoers(); /* check mode/owner on _PATH_SUDOERS */
@@ -763,7 +763,6 @@ check_sudoers()
* Fix the mode and group on sudoers file from old default. * Fix the mode and group on sudoers file from old default.
* Only works if filesystem is readable/writable by root. * Only works if filesystem is readable/writable by root.
*/ */
set_perms(PERM_ROOT, 0);
if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 && if ((rootstat = lstat(_PATH_SUDOERS, &statbuf)) == 0 &&
SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 && SUDOERS_UID == statbuf.st_uid && SUDOERS_MODE != 0400 &&
(statbuf.st_mode & 0007777) == 0400) { (statbuf.st_mode & 0007777) == 0400) {
@@ -824,8 +823,7 @@ check_sudoers()
log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS); log_error(USE_ERRNO, "can't open %s", _PATH_SUDOERS);
} }
set_perms(PERM_ROOT, 0); set_perms(PERM_ROOT, 0); /* change back to root */
set_perms(PERM_USER, 0);
} }
/* /*