diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2016-05-02 21:09:19 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2016-05-02 21:09:19 -0700 |
commit | 94c4ac621f23e55b42f10d4cba920f2c7580005e (patch) | |
tree | 769051da2c54b0e9cca04b798b5129b4272bfaac | |
parent | 112aa0b16bcc53b06633dfb9878194f6d59ada4d (diff) | |
download | txr-94c4ac621f23e55b42f10d4cba920f2c7580005e.tar.gz txr-94c4ac621f23e55b42f10d4cba920f2c7580005e.tar.bz2 txr-94c4ac621f23e55b42f10d4cba920f2c7580005e.zip |
Implement setgid script support.
TXR now notices that it's running setgid, and
implements setgid logic for scripts.
* sysif.c (orig_egid, real_gid, is_setgid): New static
variables.
(repress_privilege): Treat real and effective
group ID similarly to user ID. Save them in global
variables, and set the is_setgid flag.
(drop_privilege): Drop setgid privilege similarly
to setuid. Operate selectively: if neither setuid
nor setgid is in effect, do nothing, but if just
one is in effect, don't do anything for the other.
(simulate_setuid): Function renamed to simulate_setuid_setgid.
A small change is made here: if the fstat fails, just drop
privilege, don't abort. Implement setgid operation
independently of setuid. The is_setgid and is_setuid
flags are manipulated here now to get the drop_privileges
function to selectively drop setuid if we are running
a script which is only segid, or drop setgid if we are running
a setuid only script.
* sysif.h (simulate_setuid): Declaration name change to
simulate_setuid_setgid.
* txr.c (txr_main): Follow rename of simulate_setuid.
* txr.1: SETUID OPERATION section changed to SETUID/SETGID
operation, and setgid description worked into the text.
-rw-r--r-- | sysif.c | 59 | ||||
-rw-r--r-- | sysif.h | 4 | ||||
-rw-r--r-- | txr.1 | 45 | ||||
-rw-r--r-- | txr.c | 4 |
4 files changed, 87 insertions, 25 deletions
@@ -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(); } @@ -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); @@ -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 @@ -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"); |