diff --git a/.gitignore b/.gitignore index ac47793..016e97d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Vim swap files +.*.swp + # Object files *.o *.ko @@ -32,3 +35,6 @@ *.dSYM/ su-exec +su-exec-static +su-exec-debug +license.inc diff --git a/Makefile b/Makefile index bda7689..d53af98 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,37 @@ -CFLAGS ?= -Wall -Werror -g +CFLAGS ?= -Wall -Werror LDFLAGS ?= PROG := su-exec SRCS := $(PROG).c +INCS := license.inc + +PREFIX := /usr/local +INSTALL_DIR := $(PREFIX)/bin +MAN_DIR := $(PREFIX)/share/man/man8 all: $(PROG) -$(PROG): $(SRCS) - $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) +license.inc: LICENSE + xxd -i $^ > $@ + +$(PROG): $(SRCS) $(INCS) + $(CC) $(CFLAGS) -o $@ $(SRCS) $(LDFLAGS) + strip $@ + +$(PROG)-static: $(SRCS) $(INCS) + $(CC) $(CFLAGS) -o $@ $(SRCS) -static $(LDFLAGS) + strip $@ -$(PROG)-static: $(SRCS) - $(CC) $(CFLAGS) -o $@ $^ -static $(LDFLAGS) +$(PROG)-debug: $(SRCS) $(INCS) + $(CC) -g $(CFLAGS) -o $@ $(SRCS) $(LDFLAGS) + +install: + install -d 0755 $(DESTDIR)$(INSTALL_DIR) + install -m 0755 $(PROG) $(DESTDIR)$(INSTALL_DIR) + install -d 0755 $(DESTDIR)$(MAN_DIR) + install -m 0644 su-exec.1 $(DESTDIR)$(MAN_DIR) clean: - rm -f $(PROG) $(PROG)-static + rm -f $(PROG) $(PROG)-static $(PROG)-debug $(INCS) + diff --git a/su-exec.1 b/su-exec.1 new file mode 100644 index 0000000..67cf2be --- /dev/null +++ b/su-exec.1 @@ -0,0 +1,69 @@ +.TH SU-EXEC 8 "14 Oct 2017" + +.SH NAME +su-exec \- change user id and group id before executing a program + +.SH SYNOPSIS +\fBsu-exec\fP \fIuser-spec\fP \fIcommand\fP [ \fIarguments...\fP ] + +\fBsu-exec\fP \fI-l\fP + +.SH DESCRIPTION +\fBsu-exec\fP executes a program with modified privileges. The program +will be exceuted directly and not run as a child, like su and sudo does, +which avoids TTY and signal issues. + +Notice that su-exec depends on being run by the root user, non-root +users do not have permission to change uid/gid. + +.SH OPTIONS +.TP +\fIuser-spec\fP +is either a user name (e.g. \fBnobody\fP) or user name and group name +separated with colon (e.g. \fBnobody:ftp\fP). Numeric uid/gid values +can be used instead of names. + +.TP +\fIcommand\fP +is the program to execute. Can be either absolute or relative path. + +.TP +\fI-l\fP +Print license information and exits. + +.SH EXAMPLES + +.TP +Execute httpd as user \fIapache\fP and gid value 1000 with the two specified arguments: + +$ \fBsu-exec apache:1000 /usr/sbin/httpd -f /opt/www/httpd.conf\fP + +.SH ENVIRONMENT VARIABLES + +.TP +\fBHOME\fP +Is updated to the value matching the user entry in \fC/etc/passwd\fP. + +.TP +\fBPATH\fP +Is used for searching for the program to execute. + +Since su-exec is not running as a suid binary, the dynamic linker or +libc will not strip or ignore variables like LD_LIBRARY_PATH etc. + +.SH EXIT STATUS +.TP +\fB0\fP +When printing license information. + +.TP +\fB1\fP +If \fbsu-exec\fR fails to change priveledges or execute the program it +will return \fB1\fP. In the successfull case the exit value will be +whatever the executed program returns. + +.SH "SEE ALSO" +su(1), runuser(8), sudo(8), gosu(1) + +.SH BUGS +\fBUSER\fP and \fBLOGNAME\fP environmental variables are not updated. diff --git a/su-exec.c b/su-exec.c index 176bbf2..955ff5d 100644 --- a/su-exec.c +++ b/su-exec.c @@ -11,14 +11,44 @@ #include #include + static char *argv0; static void usage(int exitcode) { printf("Usage: %s user-spec command [args]\n", argv0); + printf("Usage: %s -l\n\tShows license.\n", argv0); exit(exitcode); } +#include "license.inc" +static void print_license() +{ + unsigned int i; + for (i=0; ipw_name, gid, glist, &ngroups); if (r >= 0) { - if (setgroups(ngroups, glist) < 0) - err(1, "setgroups"); + if (setgroups(ngroups, glist) < 0) { + print_eperm_warning(WARNING_setgroups); + err(EXIT_FAILURE, "setgroups"); + } break; } glist = realloc(glist, ngroups * sizeof(gid_t)); if (glist == NULL) - err(1, "malloc"); + err(EXIT_FAILURE, "malloc"); } } - if (setgid(gid) < 0) - err(1, "setgid(%i)", gid); + if (setgid(gid) < 0) { + print_eperm_warning(WARNING_setgid); + err(EXIT_FAILURE, "setgid(%i)", gid); + } - if (setuid(uid) < 0) - err(1, "setuid(%i)", uid); + if (setuid(uid) < 0) { + print_eperm_warning(WARNING_setuid); + err(EXIT_FAILURE, "setuid(%i)", uid); + } execvp(cmdargv[0], cmdargv); - err(1, "%s", cmdargv[0]); + err(EXIT_FAILURE, "%s", cmdargv[0]); - return 1; + return EXIT_FAILURE; }