summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sysif.c59
-rw-r--r--sysif.h4
-rw-r--r--txr.145
-rw-r--r--txr.c4
4 files changed, 87 insertions, 25 deletions
diff --git a/sysif.c b/sysif.c
index 4394a422..d7272dcf 100644
--- a/sysif.c
+++ b/sysif.c
@@ -894,13 +894,21 @@ static val setegid_wrap(val nval)
#define RC_MAGIC 0xbe50c001
static uid_t orig_euid, real_uid;
-static unsigned int repress_called = 0, is_setuid = 1;
+static gid_t orig_egid, real_gid;
+static unsigned int repress_called = 0, is_setuid = 1, is_setgid = 1;
void repress_privilege(void)
{
+ real_gid = getgid();
+ orig_egid = getegid();
real_uid = getuid();
orig_euid = geteuid();
+ if (real_gid != orig_egid)
+ setegid(real_gid);
+ else
+ is_setgid = 0;
+
if (real_uid != orig_euid)
seteuid(real_uid);
else
@@ -915,12 +923,14 @@ void drop_privilege(void)
if (repress_called != RC_MAGIC)
abort();
- if (!is_setuid)
+ if (!is_setuid && !is_setgid)
return;
#if HAVE_SETRESUID
{
- if (setresuid(real_uid, real_uid, real_uid) != 0)
+ if (is_setgid && setresgid(real_gid, real_gid, real_gid) != 0)
+ abort();
+ if (is_setuid && setresuid(real_uid, real_uid, real_uid) != 0)
abort();
return;
}
@@ -934,24 +944,37 @@ void drop_privilege(void)
*/
(void) setuid(0);
- if (setuid(real_uid) == 0) {
- if (orig_euid == 0)
- return;
- /* If we can re-gain the previous
- * effective ID, then setuid(getuid())
- * didn't actually work; it didn't
- * set the saved ID.
+ if (is_setgid) {
+ /* If we are setgid, and cannot set effective gid to real,
+ * then abort.
*/
- if (real_uid != 0 && seteuid(orig_euid) == 0)
+ if (setgid(real_gid) != 0)
abort();
}
- abort();
+ if (is_setuid) {
+ if (setuid(real_uid) != 0)
+ abort();
+ /* If we can re-gain previous effective IDs, then setuid(getuid())
+ * didn't actually work; it didn't set the saved ID. We assume
+ * that setuid(getuid()) does work for effective root; i.e. only
+ * setuid non-root has this problem. And of course, if the
+ * real UID is root, we are not "dropping" privileges.
+ */
+ if (orig_euid != 0 && real_uid != 0) {
+ if (seteuid(orig_euid) == 0)
+ abort();
+ }
+ }
+
+ /* If we can regain setgid privileges, abort */
+ if (is_setgid && real_uid != 0 && setegid(orig_egid) == 0)
+ abort();
}
#endif
}
-void simulate_setuid(val open_script)
+void simulate_setuid_setgid(val open_script)
{
if (repress_called != RC_MAGIC || (is_setuid && seteuid(orig_euid) != 0))
abort();
@@ -967,15 +990,21 @@ void simulate_setuid(val open_script)
cnum fd = c_num(fdv);
if (fstat(fd, &stb) != 0)
- abort();
+ goto drop;
+
+ if ((stb.st_mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) {
+ if (setegid(stb.st_gid) == 0)
+ is_setgid = 0; /* do not drop effective gid in drop_privilege. */
+ }
if ((stb.st_mode & (S_ISUID | S_IXUSR)) == (S_ISUID | S_IXUSR)) {
if (seteuid(stb.st_uid) == 0)
- return;
+ is_setuid = 0; /* do not drop effective uid in drop_privilege. */
}
}
}
+drop:
drop_privilege();
}
diff --git a/sysif.h b/sysif.h
index 99cb3ac0..093e4152 100644
--- a/sysif.h
+++ b/sysif.h
@@ -56,10 +56,10 @@ val stdio_fseek(FILE *, val, int whence);
#if HAVE_GETEUID
void repress_privilege(void);
void drop_privilege(void);
-void simulate_setuid(val open_script);
+void simulate_setuid_setgid(val open_script);
#else
INLINE void repress_privilege(void) { }
INLINE void drop_privilege(void) { }
-INLINE void simulate_setuid(val open_script) { }
+INLINE void simulate_setuid_setgid(val open_script) { }
#endif
void sysif_init(void);
diff --git a/txr.1 b/txr.1
index 10ca0370..6db5569e 100644
--- a/txr.1
+++ b/txr.1
@@ -41392,14 +41392,15 @@ If multi-line mode is toggled interactively from within the listener,
the variable is updated to reflect the latest state. This happens
when the command is submitted for evaluation.
-.SH* SETUID OPERATION
+.SH* SETUID/SETGID OPERATION
On platforms with the Unix filesystem and process security model, \*(TX has
-support for executing setuid scripts, even on platforms whose operating system
-kernel does not honor the setuid bit on hash bang scripts. On these systems,
-taking advantage of the feature requires \*(TX to be installed as a setuid
-executable. For this reason, \*(TX is aware when it is executed setuid and
-takes care to manage privileges.
+support for executing setuid/setgid scripts, even on platforms whose operating system
+kernel does not honor the setuid/setgid bit on hash bang scripts. On these
+systems, taking advantage of the feature requires \*(TX to be installed as a
+setuid/setgid executable. For this reason, \*(TX is aware when it is executed
+setuid and takes care to manage privileges. The following description about
+the handling of setuid applies to the parallel handling of setgid also.
When \*(TX starts, early in its execution it determines whether or not is
is executing setuid. If so, it temporarily drops privileges, as a precaution.
@@ -41456,6 +41457,38 @@ where
is the previously noted effective user ID. In other words, it
attempts to re-gain the dropped privilege by recovering the previous
effective ID. If this attempt succeeds, \*(TX immediately aborts.
+Dropping setgid privileges is similar. Where
+.code setresgid
+is available it is used, otherwise an attempt is made with
+.code "setegid(r)"
+where
+.code r
+is the previously noted real group ID. Then a test using
+.code "setegid(e)"
+is performed using the original effective group ID as
+.codn e .
+This is done after dropping any setuid root user ID privilege
+which would allow such a test to succeed.
+
+If \*(TX is running both setuid and setgid, and execute a script
+which is setuid only, it will still drop group privileges, and vice
+versa: if it executed a setgid script, it will drop user privileges.
+For instance, if a root-owned \*(TX runs a setgid script which is owned by
+user
+.code 10
+and group-owned by group
+.codn 20 ,
+that script will run with an effective group ID of 20. The effective user ID
+will be that of the user who invoked the script: \*(TX will drop the root
+privilege to the original real ID of the user, and while for the setgid
+operation, it will change to the group ID of the script.
+
+The setuid/setgid privilege machinery in \*(TX does not manipulate
+the list of supplementary ("ancillary", in the language of POSIX) group IDs.
+It is unnecessary for security because the list does not change while
+running with setuid privilege. No group IDs are added to the list which
+need to be retracted when privileges are dropped. The supplementary
+groups also persist across the execution of a setuid/setgid script.
.SH* DEBUGGER
\*(TX has a simple, crude, built-in debugger. The debugger is invoked by adding
diff --git a/txr.c b/txr.c
index def35cc9..15d10168 100644
--- a/txr.c
+++ b/txr.c
@@ -789,7 +789,7 @@ int txr_main(int argc, char **argv)
} else if (spec_file) {
if (wcscmp(c_str(spec_file), L"-") != 0) {
open_txr_file(spec_file, &txr_lisp_p, &spec_file_str, &parse_stream);
- simulate_setuid(parse_stream);
+ simulate_setuid_setgid(parse_stream);
} else {
drop_privilege();
spec_file_str = lit("stdin");
@@ -814,7 +814,7 @@ int txr_main(int argc, char **argv)
if (!equal(arg, lit("-"))) {
open_txr_file(arg, &txr_lisp_p, &spec_file_str, &parse_stream);
- simulate_setuid(parse_stream);
+ simulate_setuid_setgid(parse_stream);
} else {
drop_privilege();
spec_file_str = lit("stdin");