Modular sudo front-end which loads policy and I/O plugins that do
most the actual work. Currently relies on dynamic loading using dlopen(). See doc/plugin.pod for the plugin API.
This commit is contained in:
208
src/Makefile.in
Normal file
208
src/Makefile.in
Normal file
@@ -0,0 +1,208 @@
|
||||
#
|
||||
# Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# @configure_input@
|
||||
#
|
||||
|
||||
#### Start of system configuration section. ####
|
||||
|
||||
srcdir = @srcdir@
|
||||
devdir = @devdir@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
incdir = $(top_srcdir)/include
|
||||
|
||||
# Compiler & tools to use
|
||||
CC = @CC@
|
||||
NROFF = nroff -Tascii
|
||||
|
||||
# Our install program supports extra flags...
|
||||
INSTALL = $(SHELL) $(top_srcdir)/install-sh -c
|
||||
|
||||
# Libraries
|
||||
LIBS = @LIBS@ @SUDO_LIBS@ @GETGROUPS_LIB@ @NET_LIBS@
|
||||
|
||||
# C preprocessor flags
|
||||
CPPFLAGS = -I$(incdir) -I$(top_builddir) -I$(srcdir) -I. @CPPFLAGS@
|
||||
|
||||
# Usually -O and/or -g
|
||||
CFLAGS = @CFLAGS@
|
||||
|
||||
# Flags to pass to the link stage
|
||||
LDFLAGS = @LDFLAGS@
|
||||
|
||||
# Where to install things...
|
||||
prefix = @prefix@
|
||||
exec_prefix = @exec_prefix@
|
||||
bindir = @bindir@
|
||||
sbindir = @sbindir@
|
||||
sysconfdir = @sysconfdir@
|
||||
libexecdir = @libexecdir@
|
||||
datarootdir = @datarootdir@
|
||||
noexecfile = @NOEXECFILE@
|
||||
noexecdir = @NOEXECDIR@
|
||||
|
||||
# User and group ids the installed files should be "owned" by
|
||||
install_uid = 0
|
||||
install_gid = 0
|
||||
|
||||
# Pass in paths and OS dependent defines
|
||||
DEFS = @OSDEFS@
|
||||
|
||||
#### End of system configuration section. ####
|
||||
|
||||
SHELL = /bin/sh
|
||||
|
||||
PROGS = sudo
|
||||
|
||||
# XXX - add back missing ones:
|
||||
# sudo_edit.c selinux.c
|
||||
OBJS = sudo.o parse_args.o lbuf.o alloc.o error.o zero_bytes.o \
|
||||
load_plugins.o conversation.o list.o fmt_string.o tgetpass.o \
|
||||
fileops.o term.o atobool.o script.o pty.o
|
||||
|
||||
LIB_OBJS = @LIBOBJS@
|
||||
|
||||
VERSION = @PACKAGE_VERSION@
|
||||
|
||||
# XXX - update
|
||||
SUDODEP = $(srcdir)/sudo.h $(incdir)/alloc.h \
|
||||
$(incdir)/compat.h $(incdir)/error.h \
|
||||
$(incdir)/list.h $(incdir)/missing.h \
|
||||
$(top_builddir)/pathnames.h $(top_builddir)/config.h
|
||||
|
||||
all: $(PROGS)
|
||||
|
||||
.SUFFIXES: .o .c .h .l .y .man .cat .lo
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $<
|
||||
|
||||
.c.lo:
|
||||
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $<
|
||||
|
||||
sudo: $(OBJS)
|
||||
$(CC) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
|
||||
|
||||
sudo_noexec.lo: $(srcdir)/sudo_noexec.c
|
||||
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_noexec.c
|
||||
|
||||
sudo_noexec.la: sudo_noexec.lo
|
||||
$(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -o $@ sudo_noexec.lo -avoid-version -rpath $(noexecdir)
|
||||
|
||||
# Dependencies (not counting auth functions)
|
||||
aix.o: $(srcdir)/aix.c
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/aix.c
|
||||
alloc.o: $(srcdir)/alloc.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/alloc.c
|
||||
audit.o: $(srcdir)/audit.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/audit.c
|
||||
bsm_audit.o: $(srcdir)/bsm_audit.c $(SUDODEP) bsm_audit.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/bsm_audit.c
|
||||
closefrom.o: $(srcdir)/closefrom.c $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/closefrom.c
|
||||
error.o: $(srcdir)/error.c $(incdir)/compat.h $(incdir)/error.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/error.c
|
||||
fileops.o: $(srcdir)/fileops.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/fileops.c
|
||||
getcwd.o: $(srcdir)/getcwd.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getcwd.c
|
||||
getline.o: $(srcdir)/getline.c $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getline.c
|
||||
getprogname.o: $(srcdir)/getprogname.c $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/getprogname.c
|
||||
isblank.o: $(srcdir)/isblank.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/isblank.c
|
||||
lbuf.o: $(srcdir)/lbuf.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/lbuf.c
|
||||
list.o: $(srcdir)/list.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/list.c
|
||||
memrchr.o: $(srcdir)/memrchr.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/memrchr.c
|
||||
mkstemp.o: $(srcdir)/mkstemp.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/mkstemp.c
|
||||
nanosleep.o: $(srcdir)/nanosleep.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/nanosleep.c
|
||||
pty.o: $(srcdir)/pty.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/pty.c
|
||||
script.o: $(srcdir)/script.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/script.c
|
||||
sigaction.o: $(srcdir)/sigaction.c $(incdir)/compat.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sigaction.c
|
||||
snprintf.o: $(srcdir)/snprintf.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/snprintf.c
|
||||
strcasecmp.o: $(srcdir)/strcasecmp.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strcasecmp.c
|
||||
strerror.o: $(srcdir)/strerror.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strerror.c
|
||||
strlcat.o: $(srcdir)/strlcat.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcat.c
|
||||
strlcpy.o: $(srcdir)/strlcpy.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strlcpy.c
|
||||
strsignal.o: $(srcdir)/strsignal.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/strsignal.c
|
||||
selinux.o: $(srcdir)/selinux.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/selinux.c
|
||||
sudo.o: $(srcdir)/sudo.c $(SUDODEP) sudo_usage.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo.c
|
||||
sudo_edit.o: $(srcdir)/sudo_edit.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_edit.c
|
||||
sudo_noexec.o: $(srcdir)/sudo_noexec.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/sudo_noexec.c
|
||||
term.o: $(srcdir)/term.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/term.c
|
||||
tgetpass.o: $(srcdir)/tgetpass.c $(SUDODEP)
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/tgetpass.c
|
||||
utimes.o: $(srcdir)/utimes.c $(incdir)/compat.h $(srcdir)/emul/utime.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/utimes.c
|
||||
zero_bytes.o: $(srcdir)/zero_bytes.c $(incdir)/compat.h $(top_builddir)/config.h
|
||||
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(OPTIONS) $(srcdir)/zero_bytes.c
|
||||
|
||||
install: install-dirs install-binaries @INSTALL_NOEXEC@
|
||||
|
||||
install-dirs:
|
||||
$(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(sudodir) \
|
||||
$(DESTDIR)$(noexecdir)
|
||||
|
||||
install-binaries: install-dirs $(PROGS)
|
||||
$(INSTALL) -O $(install_uid) -G $(install_gid) -M 4111 -s sudo $(DESTDIR)$(sudodir)/sudo
|
||||
rm -f $(DESTDIR)$(sudodir)/sudoedit
|
||||
ln $(DESTDIR)$(sudodir)/sudo $(DESTDIR)$(sudodir)/sudoedit
|
||||
if [ -f sesh ]; then $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0111 -s sesh $(DESTDIR)$(libexecdir)/sesh; fi
|
||||
|
||||
install-noexec: install-dirs sudo_noexec.la
|
||||
if [ -f .libs/$(noexecfile) ]; then $(INSTALL) -O $(install_uid) -G $(install_gid) -M 0755 .libs/$(noexecfile) $(DESTDIR)$(noexecdir); fi
|
||||
|
||||
check:
|
||||
@echo nothing to check
|
||||
|
||||
clean:
|
||||
-rm -f *.a *.o *.lo stamp-* $(PROGS) core *.core core.*
|
||||
|
||||
mostlyclean: clean
|
||||
|
||||
# XXX - remove some
|
||||
distclean: clean
|
||||
-rm -rf Makefile pathnames.h config.h config.status config.cache \
|
||||
config.log libtool sudo_noexec.lo .libs $(GENERATED) \
|
||||
sudo_usage.h
|
||||
|
||||
clobber: distclean
|
||||
|
||||
realclean: distclean
|
||||
rm -f TAGS tags
|
||||
|
||||
cleandir: realclean
|
109
src/conversation.c
Normal file
109
src/conversation.c
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 1999-2005, 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <stdio.h>
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif /* STDC_HEADERS */
|
||||
#ifdef HAVE_STRING_H
|
||||
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
|
||||
# include <memory.h>
|
||||
# endif
|
||||
# include <string.h>
|
||||
#else
|
||||
# ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
# endif
|
||||
#endif /* HAVE_STRING_H */
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
|
||||
#include "sudo.h"
|
||||
#include "sudo_plugin.h"
|
||||
|
||||
extern int tgetpass_flags; /* XXX */
|
||||
|
||||
/*
|
||||
* Sudo conversation function.
|
||||
*/
|
||||
int
|
||||
sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
|
||||
struct sudo_conv_reply replies[])
|
||||
{
|
||||
struct sudo_conv_reply *repl;
|
||||
const struct sudo_conv_message *msg;
|
||||
char *pass;
|
||||
int n, flags = tgetpass_flags;
|
||||
|
||||
for (n = 0; n < num_msgs; n++) {
|
||||
msg = &msgs[n];
|
||||
repl = &replies[n];
|
||||
switch (msg->msg_type) {
|
||||
case SUDO_CONV_PROMPT_ECHO_ON:
|
||||
SET(flags, TGP_ECHO);
|
||||
case SUDO_CONV_PROMPT_ECHO_OFF:
|
||||
/* Read the password unless interrupted. */
|
||||
/* XXX - look up passwd timeout and pass in XXX */
|
||||
pass = tgetpass(msg->msg, 0, flags);
|
||||
if (pass == NULL)
|
||||
goto err;
|
||||
repl->reply = estrdup(pass);
|
||||
zero_bytes(pass, strlen(pass));
|
||||
break;
|
||||
case SUDO_CONV_INFO_MSG:
|
||||
if (msg->msg)
|
||||
(void) puts(msg->msg);
|
||||
break;
|
||||
case SUDO_CONV_ERROR_MSG:
|
||||
if (msg->msg) {
|
||||
(void) fputs(msg->msg, stderr);
|
||||
(void) fputc('\n', stderr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
|
||||
return(0);
|
||||
|
||||
err:
|
||||
/* Zero and free allocated memory and return an error. */
|
||||
do {
|
||||
repl = &replies[n];
|
||||
if (repl->reply != NULL) {
|
||||
zero_bytes(repl->reply, strlen(repl->reply));
|
||||
free(repl->reply);
|
||||
repl->reply = NULL;
|
||||
}
|
||||
} while (n--);
|
||||
|
||||
return(-1);
|
||||
}
|
@@ -44,7 +44,7 @@
|
||||
# include <time.h>
|
||||
#endif
|
||||
#ifndef HAVE_TIMESPEC
|
||||
# include <compat/timespec.h>
|
||||
# include <emul/timespec.h>
|
||||
#endif
|
||||
|
||||
#include "sudo.h"
|
||||
|
63
src/fmt_string.c
Normal file
63
src/fmt_string.c
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif /* STDC_HEADERS */
|
||||
#ifdef HAVE_STRING_H
|
||||
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
|
||||
# include <memory.h>
|
||||
# endif
|
||||
# include <string.h>
|
||||
#else
|
||||
# ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
# endif
|
||||
#endif /* HAVE_STRING_H */
|
||||
|
||||
#include "sudo.h"
|
||||
|
||||
/*
|
||||
* Allocate storage for a name=value string and return it.
|
||||
*/
|
||||
char *
|
||||
fmt_string(const char *var, const char *val)
|
||||
{
|
||||
size_t var_len = strlen(var);
|
||||
size_t val_len = strlen(val);
|
||||
char *cp, *str;
|
||||
|
||||
cp = str = emalloc(var_len + 1 + val_len + 1);
|
||||
memcpy(cp, var, var_len);
|
||||
cp += var_len;
|
||||
*cp++ = '=';
|
||||
memcpy(cp, val, val_len);
|
||||
cp += val_len;
|
||||
*cp = '\0';
|
||||
|
||||
return(str);
|
||||
}
|
176
src/load_plugins.c
Normal file
176
src/load_plugins.c
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif /* STDC_HEADERS */
|
||||
#ifdef HAVE_STRING_H
|
||||
# include <string.h>
|
||||
#else
|
||||
# ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
# endif
|
||||
#endif /* HAVE_STRING_H */
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "sudo.h"
|
||||
#include "sudo_plugin.h"
|
||||
#include "sudo_plugin_int.h"
|
||||
|
||||
/*
|
||||
* Read in /etc/sudo.conf
|
||||
* Returns a list of plugins.
|
||||
*/
|
||||
static struct plugin_info_list *
|
||||
sudo_read_conf(const char *conf_file)
|
||||
{
|
||||
FILE *fp;
|
||||
char *cp, *name, *path;
|
||||
struct plugin_info *info;
|
||||
static struct plugin_info_list pil; /* XXX */
|
||||
|
||||
if ((fp = fopen(conf_file, "r")) == NULL) {
|
||||
/* Default values */
|
||||
info = emalloc(sizeof(*info));
|
||||
info->symbol_name = "sudoers";
|
||||
info->path = "sudoers_policy";
|
||||
info->prev = info;
|
||||
info->next = NULL;
|
||||
tq_append(&pil, info);
|
||||
/* XXX - io plugin too */
|
||||
goto done;
|
||||
}
|
||||
|
||||
while ((cp = sudo_parseln(fp)) != NULL) {
|
||||
/* Skip blank or comment lines */
|
||||
if (*cp == '\0')
|
||||
continue;
|
||||
|
||||
/* Look for a line starting with "Plugin" */
|
||||
if (strncasecmp(cp, "Plugin", 6) != 0)
|
||||
continue;
|
||||
|
||||
/* Parse line */
|
||||
if ((name = strtok(cp + 6, " \t")) == NULL ||
|
||||
(path = strtok(NULL, " \t")) == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
info = emalloc(sizeof(*info));
|
||||
info->symbol_name = estrdup(name);
|
||||
info->path = estrdup(path);
|
||||
info->prev = info;
|
||||
info->next = NULL;
|
||||
tq_append(&pil, info);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
done:
|
||||
|
||||
return(&pil);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the plugins listed in conf_file.
|
||||
*/
|
||||
void
|
||||
sudo_load_plugins(const char *conf_file,
|
||||
struct plugin_container *policy_plugin,
|
||||
struct plugin_container_list *io_plugins)
|
||||
{
|
||||
struct generic_plugin *plugin;
|
||||
struct plugin_container *container;
|
||||
struct plugin_info *info;
|
||||
struct plugin_info_list *plugin_list;
|
||||
struct stat sb;
|
||||
void *handle;
|
||||
char path[PATH_MAX];
|
||||
|
||||
/* Parse sudo.conf */
|
||||
plugin_list = sudo_read_conf(conf_file);
|
||||
if (tq_empty(plugin_list))
|
||||
errorx(1, "no plugins defined in %s", conf_file);
|
||||
|
||||
tq_foreach_fwd(plugin_list, info) {
|
||||
if (info->path[0] == '/') {
|
||||
if (strlcpy(path, info->path, sizeof(path) >= sizeof(path)))
|
||||
errorx(1, "%s: %s", info->path, strerror(ENAMETOOLONG));
|
||||
} else {
|
||||
if (snprintf(path, sizeof(path), "%s/%s", _PATH_SUDO_PLUGIN_DIR,
|
||||
info->path) >= sizeof(path)) {
|
||||
errorx(1, "%s/%s: %s", _PATH_SUDO_PLUGIN_DIR, info->path,
|
||||
strerror(ENAMETOOLONG));
|
||||
}
|
||||
}
|
||||
if (stat(path, &sb) != 0)
|
||||
error(1, "%s", path);
|
||||
if (sb.st_uid != ROOT_UID)
|
||||
errorx(1, "%s must be owned by uid %d", path, ROOT_UID);
|
||||
if ((sb.st_mode & (S_IWGRP|S_IWOTH)) != 0)
|
||||
errorx(1, "%s must be only be writable by owner", path);
|
||||
|
||||
/* Open plugin and map in symbol */
|
||||
handle = dlopen(path, RTLD_NOW);
|
||||
if (!handle)
|
||||
errorx(1, "unable to dlopen %s: %s", path, dlerror());
|
||||
plugin = dlsym(handle, info->symbol_name);
|
||||
if (!plugin)
|
||||
errorx(1, "unable to find symbol %s in %s", info->symbol_name, path);
|
||||
|
||||
if (plugin->type != SUDO_POLICY_PLUGIN && plugin->type != SUDO_IO_PLUGIN) {
|
||||
errorx(1, "%s: unknown policy type %d", path, plugin->type);
|
||||
}
|
||||
if (SUDO_API_VERSION_GET_MAJOR(plugin->version) != SUDO_API_VERSION_MAJOR) {
|
||||
errorx(1, "%s: incompatible policy major version %d, expected %d",
|
||||
path, SUDO_API_VERSION_GET_MAJOR(plugin->version),
|
||||
SUDO_API_VERSION_MAJOR);
|
||||
}
|
||||
if (plugin->type == SUDO_POLICY_PLUGIN) {
|
||||
if (policy_plugin->handle)
|
||||
errorx(1, "only a single policy plugin may be loaded");
|
||||
policy_plugin->handle = handle;
|
||||
policy_plugin->name = info->symbol_name;
|
||||
policy_plugin->u.generic = plugin;
|
||||
} else if (plugin->type == SUDO_IO_PLUGIN) {
|
||||
container = emalloc(sizeof(*container));
|
||||
container->prev = container;
|
||||
container->next = NULL;
|
||||
container->handle = handle;
|
||||
container->name = info->symbol_name;
|
||||
container->u.generic = plugin;
|
||||
tq_append(io_plugins, container);
|
||||
}
|
||||
}
|
||||
if (policy_plugin->handle == NULL)
|
||||
errorx(1, "%s: at least one policy plugin must be specified", conf_file);
|
||||
if (policy_plugin->u.policy->check_policy == NULL)
|
||||
errorx(1, "policy plugin %s does not include a check_policy method");
|
||||
}
|
431
src/parse_args.c
Normal file
431
src/parse_args.c
Normal file
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
* Copyright (c) 1993-1996, 1998-2009 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
|
||||
/* XXX - prune this */
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif /* STDC_HEADERS */
|
||||
#ifdef HAVE_STRING_H
|
||||
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
|
||||
# include <memory.h>
|
||||
# endif
|
||||
# include <string.h>
|
||||
#else
|
||||
# ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
# endif
|
||||
#endif /* HAVE_STRING_H */
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
|
||||
#include <sudo_usage.h>
|
||||
#include "sudo.h"
|
||||
#include "lbuf.h" /* XXX */
|
||||
|
||||
/* For getopt(3) */
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
||||
/* XXX - better home for these and extern in header file */
|
||||
int tgetpass_flags;
|
||||
int user_closefrom = -1;
|
||||
const char *list_user, *runas_user, *runas_group;
|
||||
|
||||
/*
|
||||
* Local functions.
|
||||
*/
|
||||
static void usage(int) __attribute__((__noreturn__));
|
||||
static void usage_excl(int) __attribute__((__noreturn__));
|
||||
|
||||
/*
|
||||
* Mapping of command line flags to name/value settings.
|
||||
*/
|
||||
struct name_value_pair {
|
||||
const char *name;
|
||||
const char *value;
|
||||
};
|
||||
static struct sudo_settings {
|
||||
struct name_value_pair a;
|
||||
struct name_value_pair c;
|
||||
struct name_value_pair D;
|
||||
struct name_value_pair E;
|
||||
struct name_value_pair g;
|
||||
struct name_value_pair H;
|
||||
struct name_value_pair i;
|
||||
struct name_value_pair k;
|
||||
struct name_value_pair p;
|
||||
struct name_value_pair r;
|
||||
struct name_value_pair t;
|
||||
struct name_value_pair U;
|
||||
struct name_value_pair u;
|
||||
} sudo_settings = {
|
||||
{ "bsdauth_type" },
|
||||
{ "login_class" },
|
||||
{ "debug_level" },
|
||||
{ "preserve_environment" },
|
||||
{ "runas_group" },
|
||||
{ "set_home" },
|
||||
{ "login_shell" },
|
||||
{ "ignore_ticket" },
|
||||
{ "prompt" },
|
||||
{ "selinux_role" },
|
||||
{ "selinux_type" },
|
||||
{ "runas_user" }
|
||||
};
|
||||
static struct name_value_pair *setting_pairs[] = {
|
||||
&sudo_settings.a,
|
||||
&sudo_settings.c,
|
||||
&sudo_settings.D,
|
||||
&sudo_settings.E,
|
||||
&sudo_settings.g,
|
||||
&sudo_settings.H,
|
||||
&sudo_settings.i,
|
||||
&sudo_settings.p,
|
||||
&sudo_settings.r,
|
||||
&sudo_settings.t,
|
||||
&sudo_settings.u
|
||||
};
|
||||
#define settings_size (sizeof(setting_pairs) / sizeof(struct name_value_pair))
|
||||
|
||||
/*
|
||||
* Command line argument parsing.
|
||||
* Sets nargc and nargv which corresponds to the argc/argv we'll use
|
||||
* for the command to be run (if we are running one).
|
||||
*/
|
||||
int
|
||||
parse_args(int argc, char **argv, int *nargc, char ***nargv, char ***settingsp,
|
||||
char ***env_addp)
|
||||
{
|
||||
int mode = 0; /* what mode is sudo to be run in? */
|
||||
int flags = 0; /* mode flags */
|
||||
int valid_flags, ch;
|
||||
int i, j;
|
||||
char **settings;
|
||||
char **env_add;
|
||||
int nenv = 0;
|
||||
int env_size = 32;
|
||||
|
||||
env_add = emalloc2(env_size, sizeof(char *));
|
||||
env_add[0] = NULL;
|
||||
|
||||
/* First, check to see if we were invoked as "sudoedit". */
|
||||
if (strcmp(getprogname(), "sudoedit") == 0)
|
||||
mode = MODE_EDIT;
|
||||
|
||||
/* Returns true if the last option string was "--" */
|
||||
#define got_end_of_args (optind > 1 && argv[optind - 1][0] == '-' && \
|
||||
argv[optind - 1][1] == '-' && argv[optind - 1][2] == '\0')
|
||||
|
||||
/* Returns true if next option is an environment variable */
|
||||
#define is_envar (optind < argc && argv[optind][0] != '/' && \
|
||||
strchr(argv[optind], '=') != NULL)
|
||||
|
||||
/* Flags allowed when running a command */
|
||||
valid_flags = MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|
|
||||
MODE_LOGIN_SHELL|MODE_INVALIDATE|MODE_NONINTERACTIVE|
|
||||
MODE_PRESERVE_GROUPS|MODE_SHELL;
|
||||
/* XXX - should fill in settings at the end to avoid dupes */
|
||||
for (;;) {
|
||||
/*
|
||||
* We disable arg permutation for GNU getopt().
|
||||
* Some trickiness is required to allow environment variables
|
||||
* to be interspersed with command line options.
|
||||
*/
|
||||
if ((ch = getopt(argc, argv, "+Aa:bC:c:D:Eeg:HhiKkLlnPp:r:Sst:U:u:Vv")) != -1) {
|
||||
switch (ch) {
|
||||
case 'A':
|
||||
SET(tgetpass_flags, TGP_ASKPASS);
|
||||
break;
|
||||
#ifdef HAVE_BSD_AUTH_H
|
||||
case 'a':
|
||||
sudo_settings.a.value = optarg;
|
||||
break;
|
||||
#endif
|
||||
case 'b':
|
||||
SET(flags, MODE_BACKGROUND);
|
||||
break;
|
||||
case 'C':
|
||||
if ((user_closefrom = atoi(optarg)) < 3) {
|
||||
warningx("the argument to -C must be at least 3");
|
||||
usage(1);
|
||||
}
|
||||
break;
|
||||
#ifdef HAVE_LOGIN_CAP_H
|
||||
case 'c':
|
||||
sudo_settings.c.value = optarg;
|
||||
break;
|
||||
#endif
|
||||
case 'D':
|
||||
sudo_settings.D.value = optarg;
|
||||
break;
|
||||
case 'E':
|
||||
sudo_settings.c.value = "true";
|
||||
break;
|
||||
case 'e':
|
||||
if (mode && mode != MODE_EDIT)
|
||||
usage_excl(1);
|
||||
mode = MODE_EDIT;
|
||||
valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
|
||||
break;
|
||||
case 'g':
|
||||
runas_group = optarg;
|
||||
sudo_settings.g.value = optarg;
|
||||
break;
|
||||
case 'H':
|
||||
sudo_settings.H.value = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
if (mode && mode != MODE_HELP) {
|
||||
if (strcmp(getprogname(), "sudoedit") != 0)
|
||||
usage_excl(1);
|
||||
}
|
||||
mode = MODE_HELP;
|
||||
valid_flags = 0;
|
||||
break;
|
||||
case 'i':
|
||||
sudo_settings.i.value = "true";
|
||||
SET(flags, MODE_LOGIN_SHELL);
|
||||
break;
|
||||
case 'k':
|
||||
sudo_settings.k.value = "true";
|
||||
SET(flags, MODE_INVALIDATE);
|
||||
break;
|
||||
case 'K':
|
||||
sudo_settings.k.value = "true";
|
||||
if (mode && mode != MODE_KILL)
|
||||
usage_excl(1);
|
||||
mode = MODE_KILL;
|
||||
valid_flags = 0;
|
||||
break;
|
||||
case 'l':
|
||||
if (mode) {
|
||||
if (mode == MODE_LIST)
|
||||
SET(flags, MODE_LONG_LIST);
|
||||
else
|
||||
usage_excl(1);
|
||||
}
|
||||
mode = MODE_LIST;
|
||||
valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE|MODE_LONG_LIST;
|
||||
break;
|
||||
case 'n':
|
||||
SET(flags, MODE_NONINTERACTIVE);
|
||||
break;
|
||||
case 'P':
|
||||
SET(flags, MODE_PRESERVE_GROUPS);
|
||||
break;
|
||||
case 'p':
|
||||
sudo_settings.i.value = optarg;
|
||||
break;
|
||||
#ifdef HAVE_SELINUX
|
||||
case 'r':
|
||||
sudo_settings.r.value = optarg;
|
||||
break;
|
||||
case 't':
|
||||
sudo_settings.t.value = optarg;
|
||||
break;
|
||||
#endif
|
||||
case 'S':
|
||||
SET(tgetpass_flags, TGP_STDIN);
|
||||
break;
|
||||
case 's':
|
||||
SET(flags, MODE_SHELL);
|
||||
break;
|
||||
case 'U':
|
||||
/* XXX - sudo_getpwnam */
|
||||
if ((getpwnam(optarg)) == NULL)
|
||||
errorx(1, "unknown user: %s", optarg);
|
||||
list_user = optarg;
|
||||
break;
|
||||
case 'u':
|
||||
runas_user = optarg;
|
||||
sudo_settings.u.value = optarg;
|
||||
break;
|
||||
case 'v':
|
||||
if (mode && mode != MODE_VALIDATE)
|
||||
usage_excl(1);
|
||||
mode = MODE_VALIDATE;
|
||||
valid_flags = MODE_INVALIDATE|MODE_NONINTERACTIVE;
|
||||
break;
|
||||
case 'V':
|
||||
if (mode && mode != MODE_VERSION)
|
||||
usage_excl(1);
|
||||
mode = MODE_VERSION;
|
||||
valid_flags = 0;
|
||||
break;
|
||||
default:
|
||||
usage(1);
|
||||
}
|
||||
} else if (!got_end_of_args && is_envar) {
|
||||
if (nenv == env_size - 2) {
|
||||
env_size *= 2;
|
||||
env_add = erealloc3(env_add, env_size, sizeof(char *));
|
||||
}
|
||||
env_add[nenv++] = argv[optind];
|
||||
|
||||
/* Crank optind and resume getopt. */
|
||||
optind++;
|
||||
} else {
|
||||
/* Not an option or an environment variable -- we're done. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*nargc = argc - optind;
|
||||
*nargv = argv + optind;
|
||||
|
||||
if (!mode) {
|
||||
/* Defer -k mode setting until we know whether it is a flag or not */
|
||||
if (ISSET(flags, MODE_INVALIDATE) && *nargc == 0) {
|
||||
mode = MODE_INVALIDATE; /* -k by itself */
|
||||
CLR(flags, MODE_INVALIDATE);
|
||||
sudo_settings.k.value = NULL;
|
||||
valid_flags = 0;
|
||||
} else {
|
||||
mode = MODE_RUN; /* running a command */
|
||||
}
|
||||
}
|
||||
|
||||
if (*nargc > 0 && mode == MODE_LIST)
|
||||
mode = MODE_CHECK;
|
||||
|
||||
if (ISSET(flags, MODE_LOGIN_SHELL)) {
|
||||
if (ISSET(flags, MODE_SHELL)) {
|
||||
warningx("you may not specify both the `-i' and `-s' options");
|
||||
usage(1);
|
||||
}
|
||||
if (ISSET(flags, MODE_PRESERVE_ENV)) {
|
||||
warningx("you may not specify both the `-i' and `-E' options");
|
||||
usage(1);
|
||||
}
|
||||
SET(flags, MODE_SHELL);
|
||||
}
|
||||
if ((flags & valid_flags) != flags)
|
||||
usage(1);
|
||||
if (mode == MODE_EDIT &&
|
||||
(ISSET(flags, MODE_PRESERVE_ENV) || env_add[0] != NULL)) {
|
||||
if (ISSET(mode, MODE_PRESERVE_ENV))
|
||||
warningx("the `-E' option is not valid in edit mode");
|
||||
if (env_add[0] != NULL)
|
||||
warningx("you may not specify environment variables in edit mode");
|
||||
usage(1);
|
||||
}
|
||||
if ((runas_user != NULL || runas_group != NULL) &&
|
||||
!ISSET(mode, MODE_EDIT | MODE_RUN | MODE_CHECK | MODE_VALIDATE)) {
|
||||
usage(1);
|
||||
}
|
||||
if (list_user != NULL && mode != MODE_LIST && mode != MODE_CHECK) {
|
||||
warningx("the `-U' option may only be used with the `-l' option");
|
||||
usage(1);
|
||||
}
|
||||
if (ISSET(tgetpass_flags, TGP_STDIN) && ISSET(tgetpass_flags, TGP_ASKPASS)) {
|
||||
warningx("the `-A' and `-S' options may not be used together");
|
||||
usage(1);
|
||||
}
|
||||
if ((*nargc == 0 && mode == MODE_EDIT) ||
|
||||
(*nargc > 0 && !ISSET(mode, MODE_RUN | MODE_EDIT | MODE_CHECK)))
|
||||
usage(1);
|
||||
if (*nargc == 0 && mode == MODE_RUN && !ISSET(flags, MODE_SHELL))
|
||||
SET(flags, (MODE_IMPLIED_SHELL | MODE_SHELL));
|
||||
|
||||
if (mode == MODE_HELP)
|
||||
usage(0);
|
||||
|
||||
/*
|
||||
* Format setting_pairs into settings array.
|
||||
*/
|
||||
settings = emalloc2(settings_size + 1, sizeof (char *));
|
||||
for (i = 0, j = 0; i < settings_size; i++) {
|
||||
if (setting_pairs[i]->value) {
|
||||
settings[j++] = fmt_string(setting_pairs[i]->name,
|
||||
setting_pairs[i]->value);
|
||||
}
|
||||
}
|
||||
settings[j] = NULL;
|
||||
|
||||
*settingsp = settings;
|
||||
*env_addp = env_add;
|
||||
return(mode | flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Give usage message and exit.
|
||||
* The actual usage strings are in sudo_usage.h for configure substitution.
|
||||
* XXX - avoid lbuf usage
|
||||
*/
|
||||
static void
|
||||
usage(int exit_val)
|
||||
{
|
||||
struct lbuf lbuf;
|
||||
char *uvec[6];
|
||||
int i, ulen;
|
||||
|
||||
/*
|
||||
* Use usage vectors appropriate to the progname.
|
||||
*/
|
||||
if (strcmp(getprogname(), "sudoedit") == 0) {
|
||||
uvec[0] = SUDO_USAGE5 + 3;
|
||||
uvec[1] = NULL;
|
||||
} else {
|
||||
uvec[0] = SUDO_USAGE1;
|
||||
uvec[1] = SUDO_USAGE2;
|
||||
uvec[2] = SUDO_USAGE3;
|
||||
uvec[3] = SUDO_USAGE4;
|
||||
uvec[4] = SUDO_USAGE5;
|
||||
uvec[5] = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Print usage and wrap lines as needed, depending on the
|
||||
* tty width.
|
||||
*/
|
||||
ulen = (int)strlen(getprogname()) + 8;
|
||||
lbuf_init(&lbuf, NULL, ulen, 0);
|
||||
for (i = 0; uvec[i] != NULL; i++) {
|
||||
lbuf_append(&lbuf, "usage: ", getprogname(), uvec[i], NULL);
|
||||
lbuf_print(&lbuf);
|
||||
}
|
||||
lbuf_destroy(&lbuf);
|
||||
exit(exit_val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell which options are mutually exclusive and exit.
|
||||
*/
|
||||
static void
|
||||
usage_excl(int exit_val)
|
||||
{
|
||||
warningx("Only one of the -e, -h, -i, -K, -l, -s, -v or -V options may be specified");
|
||||
usage(exit_val);
|
||||
}
|
38
src/pty.c
38
src/pty.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
* Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -58,31 +58,23 @@
|
||||
|
||||
#if defined(HAVE_OPENPTY)
|
||||
int
|
||||
get_pty(master, slave, name, namesz)
|
||||
int *master;
|
||||
int *slave;
|
||||
char *name;
|
||||
size_t namesz;
|
||||
get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
|
||||
{
|
||||
struct group *gr;
|
||||
gid_t ttygid = -1;
|
||||
|
||||
if ((gr = sudo_getgrnam("tty")) != NULL)
|
||||
if ((gr = getgrnam("tty")) != NULL)
|
||||
ttygid = gr->gr_gid;
|
||||
|
||||
if (openpty(master, slave, name, NULL, NULL) != 0)
|
||||
return(0);
|
||||
(void) chown(name, runas_pw->pw_uid, ttygid);
|
||||
(void) chown(name, uid, ttygid);
|
||||
return(1);
|
||||
}
|
||||
|
||||
#elif defined(HAVE__GETPTY)
|
||||
int
|
||||
get_pty(master, slave, name, namesz)
|
||||
int *master;
|
||||
int *slave;
|
||||
char *name;
|
||||
size_t namesz;
|
||||
get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
|
||||
{
|
||||
char *line;
|
||||
|
||||
@@ -95,7 +87,7 @@ get_pty(master, slave, name, namesz)
|
||||
close(*master);
|
||||
return(0);
|
||||
}
|
||||
(void) chown(line, runas_pw->pw_uid, -1);
|
||||
(void) chown(line, uid, -1);
|
||||
strlcpy(name, line, namesz);
|
||||
return(1);
|
||||
}
|
||||
@@ -117,11 +109,7 @@ posix_openpt(oflag)
|
||||
# endif /* HAVE_POSIX_OPENPT */
|
||||
|
||||
int
|
||||
get_pty(master, slave, name, namesz)
|
||||
int *master;
|
||||
int *slave;
|
||||
char *name;
|
||||
size_t namesz;
|
||||
get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
|
||||
{
|
||||
char *line;
|
||||
|
||||
@@ -148,7 +136,7 @@ get_pty(master, slave, name, namesz)
|
||||
ioctl(*slave, I_PUSH, "ptem"); /* pseudo tty emulation module */
|
||||
ioctl(*slave, I_PUSH, "ldterm"); /* line discipline module */
|
||||
# endif
|
||||
(void) chown(line, runas_pw->pw_uid, -1);
|
||||
(void) chown(line, uid, -1);
|
||||
strlcpy(name, line, namesz);
|
||||
return(1);
|
||||
}
|
||||
@@ -158,17 +146,13 @@ get_pty(master, slave, name, namesz)
|
||||
static char line[] = "/dev/ptyXX";
|
||||
|
||||
int
|
||||
get_pty(master, slave, name, namesz)
|
||||
int *master;
|
||||
int *slave;
|
||||
char *name;
|
||||
size_t namesz;
|
||||
get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid)
|
||||
{
|
||||
char *bank, *cp;
|
||||
struct group *gr;
|
||||
gid_t ttygid = -1;
|
||||
|
||||
if ((gr = sudo_getgrnam("tty")) != NULL)
|
||||
if ((gr = getgrnam("tty")) != NULL)
|
||||
ttygid = gr->gr_gid;
|
||||
|
||||
for (bank = "pqrs"; *bank != '\0'; bank++) {
|
||||
@@ -182,7 +166,7 @@ get_pty(master, slave, name, namesz)
|
||||
continue; /* already in use */
|
||||
}
|
||||
line[sizeof("/dev/p") - 2] = 't';
|
||||
(void) chown(line, runas_pw->pw_uid, ttygid);
|
||||
(void) chown(line, uid, ttygid);
|
||||
(void) chmod(line, S_IRUSR|S_IWUSR|S_IWGRP);
|
||||
# ifdef HAVE_REVOKE
|
||||
(void) revoke(line);
|
||||
|
560
src/script.c
560
src/script.c
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
* Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
@@ -33,7 +33,7 @@
|
||||
#endif /* HAVE_TERMIOS_H */
|
||||
#include <sys/ioctl.h>
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
# include <sys/select.h>
|
||||
#endif /* HAVE_SYS_SELECT_H */
|
||||
#include <stdio.h>
|
||||
#ifdef STDC_HEADERS
|
||||
@@ -68,39 +68,29 @@
|
||||
#ifdef HAVE_SELINUX
|
||||
# include <selinux/selinux.h>
|
||||
#endif
|
||||
#ifdef HAVE_ZLIB
|
||||
# include <zlib.h>
|
||||
#endif
|
||||
|
||||
#include "sudo.h"
|
||||
#include "sudo.h" /* XXX? */
|
||||
#include "sudo_plugin.h"
|
||||
#include "sudo_plugin_int.h"
|
||||
|
||||
#define SFD_MASTER 0
|
||||
#define SFD_SLAVE 1
|
||||
#define SFD_LOG 2
|
||||
#define SFD_OUTPUT 3
|
||||
#define SFD_TIMING 4
|
||||
#define SFD_USERTTY 5
|
||||
#define SFD_USERTTY 2
|
||||
|
||||
#define TERM_COOKED 0
|
||||
#define TERM_CBREAK 1
|
||||
#define TERM_RAW 2
|
||||
|
||||
union script_fd {
|
||||
FILE *f;
|
||||
#ifdef HAVE_ZLIB
|
||||
gzFile g;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct script_buf {
|
||||
int len; /* buffer length (how much read in) */
|
||||
int off; /* write position (how much already consumed) */
|
||||
char buf[16 * 1024];
|
||||
};
|
||||
|
||||
static int script_fds[6];
|
||||
static int script_fds[3];
|
||||
static int ttyout;
|
||||
|
||||
/* XXX - use an array of signals instead of just a single variable */
|
||||
static sig_atomic_t alive = 1;
|
||||
static sig_atomic_t recvsig = 0;
|
||||
static sig_atomic_t ttymode = TERM_COOKED;
|
||||
@@ -114,247 +104,68 @@ static int foreground;
|
||||
|
||||
static char slavename[PATH_MAX];
|
||||
|
||||
static int suspend_parent __P((int signo, struct script_buf *output,
|
||||
struct timeval *then, struct timeval *now, union script_fd ofile,
|
||||
union script_fd tfile));
|
||||
static void flush_output __P((struct script_buf *output, struct timeval *then,
|
||||
struct timeval *now, union script_fd ofile, union script_fd tfile));
|
||||
static void handler __P((int s));
|
||||
static void script_child __P((char *path, char *argv[], int, int));
|
||||
static void script_run __P((char *path, char *argv[], int));
|
||||
static void sigchild __P((int s));
|
||||
static void sigwinch __P((int s));
|
||||
static void sync_winsize __P((int src, int dst));
|
||||
static int suspend_parent(int signo, struct script_buf *output);
|
||||
static void flush_output(struct script_buf *output);
|
||||
static void handler(int s);
|
||||
static int script_child(const char *path, char *argv[], char *envp[], int, int);
|
||||
static void script_run(const char *path, char *argv[], char *envp[], int);
|
||||
static void sigchild(int s);
|
||||
static void sigwinch(int s);
|
||||
static void sync_winsize(int src, int dst);
|
||||
|
||||
extern int get_pty __P((int *master, int *slave, char *name, size_t namesz));
|
||||
|
||||
/*
|
||||
* TODO: run monitor as root?
|
||||
*/
|
||||
|
||||
static int
|
||||
fdcompar(v1, v2)
|
||||
const void *v1;
|
||||
const void *v2;
|
||||
{
|
||||
int i = *(int *)v1;
|
||||
int j = *(int *)v2;
|
||||
|
||||
return(script_fds[i] - script_fds[j]);
|
||||
}
|
||||
/* sudo.c */
|
||||
extern struct plugin_container_list io_plugins;
|
||||
|
||||
void
|
||||
script_nextid()
|
||||
script_setup(uid_t uid)
|
||||
{
|
||||
struct stat sb;
|
||||
char buf[32], *ep;
|
||||
int fd, i, ch;
|
||||
unsigned long id = 0;
|
||||
int len;
|
||||
ssize_t nread;
|
||||
char pathbuf[PATH_MAX];
|
||||
|
||||
/*
|
||||
* Create _PATH_SUDO_TRANSCRIPT if it doesn't already exist.
|
||||
*/
|
||||
if (stat(_PATH_SUDO_TRANSCRIPT, &sb) != 0) {
|
||||
if (mkdir(_PATH_SUDO_TRANSCRIPT, S_IRWXU) != 0)
|
||||
log_error(USE_ERRNO, "Can't mkdir %s", _PATH_SUDO_TRANSCRIPT);
|
||||
} else if (!S_ISDIR(sb.st_mode)) {
|
||||
log_error(0, "%s exists but is not a directory (0%o)",
|
||||
_PATH_SUDO_TRANSCRIPT, (unsigned int) sb.st_mode);
|
||||
}
|
||||
|
||||
/*
|
||||
* Open sequence file
|
||||
*/
|
||||
len = snprintf(pathbuf, sizeof(pathbuf), "%s/seq", _PATH_SUDO_TRANSCRIPT);
|
||||
if (len <= 0 || len >= sizeof(pathbuf)) {
|
||||
errno = ENAMETOOLONG;
|
||||
log_error(USE_ERRNO, "%s/seq", pathbuf);
|
||||
}
|
||||
fd = open(pathbuf, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
|
||||
if (fd == -1)
|
||||
log_error(USE_ERRNO, "cannot open %s", pathbuf);
|
||||
lock_file(fd, SUDO_LOCK);
|
||||
|
||||
/* Read seq number (base 36). */
|
||||
nread = read(fd, buf, sizeof(buf));
|
||||
if (nread != 0) {
|
||||
if (nread == -1)
|
||||
log_error(USE_ERRNO, "cannot read %s", pathbuf);
|
||||
id = strtoul(buf, &ep, 36);
|
||||
if (buf == ep || id >= 2176782336U)
|
||||
log_error(0, "invalid sequence number %s", pathbuf);
|
||||
}
|
||||
id++;
|
||||
|
||||
/*
|
||||
* Convert id to a string and stash in sudo_user.sessid.
|
||||
* Note that that least significant digits go at the end of the string.
|
||||
*/
|
||||
for (i = 5; i >= 0; i--) {
|
||||
ch = id % 36;
|
||||
id /= 36;
|
||||
buf[i] = ch < 10 ? ch + '0' : ch - 10 + 'A';
|
||||
}
|
||||
buf[6] = '\n';
|
||||
|
||||
/* Stash id logging purposes */
|
||||
memcpy(sudo_user.sessid, buf, 6);
|
||||
sudo_user.sessid[6] = '\0';
|
||||
|
||||
/* Rewind and overwrite old seq file. */
|
||||
if (lseek(fd, 0, SEEK_SET) == (off_t)-1 || write(fd, buf, 7) != 7)
|
||||
log_error(USE_ERRNO, "Can't write to %s", pathbuf);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static int
|
||||
build_idpath(pathbuf)
|
||||
char *pathbuf;
|
||||
{
|
||||
struct stat sb;
|
||||
int i, len;
|
||||
|
||||
if (sudo_user.sessid[0] == '\0')
|
||||
log_error(0, "tried to build a session id path without a session id");
|
||||
|
||||
/*
|
||||
* Path is of the form /var/log/sudo-session/00/00/01.
|
||||
*/
|
||||
len = snprintf(pathbuf, PATH_MAX, "%s/%c%c/%c%c/%c%c", _PATH_SUDO_TRANSCRIPT,
|
||||
sudo_user.sessid[0], sudo_user.sessid[1], sudo_user.sessid[2],
|
||||
sudo_user.sessid[3], sudo_user.sessid[4], sudo_user.sessid[5]);
|
||||
if (len <= 0 && len >= PATH_MAX) {
|
||||
errno = ENAMETOOLONG;
|
||||
log_error(USE_ERRNO, "%s/%s", _PATH_SUDO_TRANSCRIPT, sudo_user.sessid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Create the intermediate subdirs as needed.
|
||||
*/
|
||||
for (i = 6; i > 0; i -= 3) {
|
||||
pathbuf[len - i] = '\0';
|
||||
if (stat(pathbuf, &sb) != 0) {
|
||||
if (mkdir(pathbuf, S_IRWXU) != 0)
|
||||
log_error(USE_ERRNO, "Can't mkdir %s", pathbuf);
|
||||
} else if (!S_ISDIR(sb.st_mode)) {
|
||||
log_error(0, "%s: %s", pathbuf, strerror(ENOTDIR));
|
||||
}
|
||||
pathbuf[len - i] = '/';
|
||||
}
|
||||
|
||||
return(len);
|
||||
}
|
||||
|
||||
void
|
||||
script_setup()
|
||||
{
|
||||
char pathbuf[PATH_MAX];
|
||||
int len;
|
||||
|
||||
script_fds[SFD_USERTTY] = open(_PATH_TTY, O_RDWR|O_NOCTTY, 0);
|
||||
if (script_fds[SFD_USERTTY] == -1)
|
||||
log_error(0, "tty required for transcript support"); /* XXX */
|
||||
errorx(1, "tty required for transcript support");
|
||||
|
||||
if (!get_pty(&script_fds[SFD_MASTER], &script_fds[SFD_SLAVE],
|
||||
slavename, sizeof(slavename)))
|
||||
log_error(USE_ERRNO, "Can't get pty");
|
||||
|
||||
/*
|
||||
* Build a path containing the session id split into two-digit subdirs,
|
||||
* so ID 000001 becomes /var/log/sudo-session/00/00/01.
|
||||
*/
|
||||
len = build_idpath(pathbuf);
|
||||
|
||||
/*
|
||||
* We create 3 files: a log file, one for the raw session data,
|
||||
* and one for the timing info.
|
||||
*/
|
||||
script_fds[SFD_LOG] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
|
||||
if (script_fds[SFD_LOG] == -1)
|
||||
log_error(USE_ERRNO, "Can't create %s", pathbuf);
|
||||
|
||||
strlcat(pathbuf, ".scr", sizeof(pathbuf));
|
||||
script_fds[SFD_OUTPUT] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY,
|
||||
S_IRUSR|S_IWUSR);
|
||||
if (script_fds[SFD_OUTPUT] == -1)
|
||||
log_error(USE_ERRNO, "Can't create %s", pathbuf);
|
||||
|
||||
pathbuf[len] = '\0';
|
||||
strlcat(pathbuf, ".tim", sizeof(pathbuf));
|
||||
script_fds[SFD_TIMING] = open(pathbuf, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR|S_IWUSR);
|
||||
if (script_fds[SFD_TIMING] == -1)
|
||||
log_error(USE_ERRNO, "Can't create %s", pathbuf);
|
||||
slavename, sizeof(slavename), uid))
|
||||
error(1, "Can't get pty");
|
||||
}
|
||||
|
||||
int
|
||||
script_duplow(fd)
|
||||
int fd;
|
||||
{
|
||||
int i, j, indices[6];
|
||||
|
||||
/* sort fds so we can dup them safely */
|
||||
for (i = 0; i < 6; i++)
|
||||
indices[i] = i;
|
||||
qsort(indices, 6, sizeof(int), fdcompar);
|
||||
|
||||
/* Move pty master/slave and session fds to low numbered fds. */
|
||||
for (i = 0; i < 6; i++) {
|
||||
j = indices[i];
|
||||
if (script_fds[j] != fd) {
|
||||
#ifdef HAVE_DUP2
|
||||
dup2(script_fds[j], fd);
|
||||
#else
|
||||
close(fd);
|
||||
dup(script_fds[j]);
|
||||
close(script_fds[j]);
|
||||
#endif
|
||||
}
|
||||
script_fds[j] = fd++;
|
||||
}
|
||||
return(fd);
|
||||
}
|
||||
|
||||
/* Update output and timing files. */
|
||||
/* Call I/O plugin input method. */
|
||||
static void
|
||||
log_output(buf, n, then, now, ofile, tfile)
|
||||
char *buf;
|
||||
int n;
|
||||
struct timeval *then;
|
||||
struct timeval *now;
|
||||
union script_fd ofile;
|
||||
union script_fd tfile;
|
||||
log_input(char *buf, unsigned int n)
|
||||
{
|
||||
struct timeval tv;
|
||||
struct plugin_container *plugin;
|
||||
sigset_t omask;
|
||||
|
||||
sigprocmask(SIG_BLOCK, &ttyblock, &omask);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
if (def_compress_transcript)
|
||||
gzwrite(ofile.g, buf, n);
|
||||
else
|
||||
#endif
|
||||
fwrite(buf, 1, n, ofile.f);
|
||||
timersub(now, then, &tv);
|
||||
#ifdef HAVE_ZLIB
|
||||
if (def_compress_transcript)
|
||||
gzprintf(tfile.g, "%f %d\n",
|
||||
tv.tv_sec + ((double)tv.tv_usec / 1000000), n);
|
||||
else
|
||||
#endif
|
||||
fprintf(tfile.f, "%f %d\n",
|
||||
tv.tv_sec + ((double)tv.tv_usec / 1000000), n);
|
||||
then->tv_sec = now->tv_sec;
|
||||
then->tv_usec = now->tv_usec;
|
||||
tq_foreach_fwd(&io_plugins, plugin) {
|
||||
/* XXX - die if return != TRUE */
|
||||
if (plugin->u.io->log_input)
|
||||
plugin->u.io->log_input(buf, n);
|
||||
}
|
||||
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
}
|
||||
|
||||
/* Call I/O plugin output method. */
|
||||
static void
|
||||
log_output(char *buf, unsigned int n)
|
||||
{
|
||||
struct plugin_container *plugin;
|
||||
sigset_t omask;
|
||||
|
||||
sigprocmask(SIG_BLOCK, &ttyblock, &omask);
|
||||
|
||||
tq_foreach_fwd(&io_plugins, plugin) {
|
||||
/* XXX - die if return != TRUE */
|
||||
if (plugin->u.io->log_output)
|
||||
plugin->u.io->log_output(buf, n);
|
||||
}
|
||||
|
||||
sigprocmask(SIG_SETMASK, &omask, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
check_foreground()
|
||||
check_foreground(void)
|
||||
{
|
||||
foreground = tcgetpgrp(script_fds[SFD_USERTTY]) == parent;
|
||||
if (foreground && !tty_initialized) {
|
||||
@@ -370,13 +181,7 @@ check_foreground()
|
||||
* Returns SIGUSR1 if the child should be resume in foreground else SIGUSR2.
|
||||
*/
|
||||
static int
|
||||
suspend_parent(signo, output, then, now, ofile, tfile)
|
||||
int signo;
|
||||
struct script_buf *output;
|
||||
struct timeval *then;
|
||||
struct timeval *now;
|
||||
union script_fd ofile;
|
||||
union script_fd tfile;
|
||||
suspend_parent(int signo, struct script_buf *output)
|
||||
{
|
||||
sigaction_t sa, osa;
|
||||
int n, oldmode = ttymode, rval = 0;
|
||||
@@ -404,7 +209,7 @@ suspend_parent(signo, output, then, now, ofile, tfile)
|
||||
/* FALLTHROUGH */
|
||||
case SIGTSTP:
|
||||
/* Flush any remaining output to master tty. */
|
||||
flush_output(output, then, now, ofile, tfile);
|
||||
flush_output(output);
|
||||
|
||||
/* Restore original tty mode before suspending. */
|
||||
if (oldmode != TERM_COOKED) {
|
||||
@@ -469,19 +274,18 @@ suspend_parent(signo, output, then, now, ofile, tfile)
|
||||
* controlling tty, belongs to child's session but has its own pgrp.
|
||||
*/
|
||||
int
|
||||
script_execv(path, argv)
|
||||
char *path;
|
||||
char *argv[];
|
||||
script_execve(struct command_details *details, char *argv[], char *envp[],
|
||||
struct command_status *cstat)
|
||||
{
|
||||
sigaction_t sa;
|
||||
struct script_buf input, output;
|
||||
struct timeval now, then;
|
||||
int n, nready, exitcode = 1;
|
||||
int n, nready;
|
||||
int relaysig, sv[2];
|
||||
fd_set *fdsr, *fdsw;
|
||||
FILE *idfile;
|
||||
union script_fd ofile, tfile;
|
||||
int rbac_enabled = 0;
|
||||
int maxfd;
|
||||
|
||||
cstat->type = 0; /* XXX */
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
rbac_enabled = is_selinux_enabled() > 0 && user_role != NULL;
|
||||
@@ -491,7 +295,7 @@ script_execv(path, argv)
|
||||
close(script_fds[SFD_SLAVE]);
|
||||
script_fds[SFD_SLAVE] = open(slavename, O_RDWR|O_NOCTTY, 0);
|
||||
if (script_fds[SFD_SLAVE] == -1)
|
||||
log_error(USE_ERRNO, "cannot open %s", slavename);
|
||||
error(1, "cannot open %s", slavename);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -542,8 +346,8 @@ script_execv(path, argv)
|
||||
* We communicate with the child over a bi-directional pipe.
|
||||
* Parent sends signal info to child and child sends back wait status.
|
||||
*/
|
||||
if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) != 0)
|
||||
log_error(USE_ERRNO, "cannot create sockets");
|
||||
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
|
||||
error(1, "cannot create sockets");
|
||||
|
||||
if (foreground) {
|
||||
/* Copy terminal attrs from user tty -> pty slave. */
|
||||
@@ -559,7 +363,7 @@ script_execv(path, argv)
|
||||
ttymode == TERM_CBREAK);
|
||||
} while (!n && errno == EINTR);
|
||||
if (!n)
|
||||
log_error(USE_ERRNO, "Can't set terminal to raw mode");
|
||||
error(1, "Can't set terminal to raw mode");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -569,43 +373,22 @@ script_execv(path, argv)
|
||||
child = fork();
|
||||
switch (child) {
|
||||
case -1:
|
||||
log_error(USE_ERRNO, "fork");
|
||||
error(1, "fork");
|
||||
break;
|
||||
case 0:
|
||||
/* child */
|
||||
close(sv[0]);
|
||||
script_child(path, argv, sv[1], rbac_enabled);
|
||||
/* NOTREACHED */
|
||||
break;
|
||||
if (exec_setup(details) == 0) {
|
||||
/* headed for execve() */
|
||||
script_child(details->command, argv, envp, sv[1], rbac_enabled);
|
||||
}
|
||||
cstat->type = CMD_ERRNO;
|
||||
cstat->val = errno;
|
||||
send(sv[1], cstat, sizeof(*cstat), 0);
|
||||
_exit(1);
|
||||
}
|
||||
close(sv[1]);
|
||||
|
||||
if ((idfile = fdopen(script_fds[SFD_LOG], "w")) == NULL)
|
||||
log_error(USE_ERRNO, "fdopen");
|
||||
#ifdef HAVE_ZLIB
|
||||
if (def_compress_transcript) {
|
||||
if ((ofile.g = gzdopen(script_fds[SFD_OUTPUT], "w")) == NULL)
|
||||
log_error(USE_ERRNO, "gzdopen");
|
||||
if ((tfile.g = gzdopen(script_fds[SFD_TIMING], "w")) == NULL)
|
||||
log_error(USE_ERRNO, "gzdopen");
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
if ((ofile.f = fdopen(script_fds[SFD_OUTPUT], "w")) == NULL)
|
||||
log_error(USE_ERRNO, "fdopen");
|
||||
if ((tfile.f = fdopen(script_fds[SFD_TIMING], "w")) == NULL)
|
||||
log_error(USE_ERRNO, "fdopen");
|
||||
}
|
||||
|
||||
gettimeofday(&then, NULL);
|
||||
|
||||
/* XXX - log more stuff? window size? environment? */
|
||||
fprintf(idfile, "%ld:%s:%s:%s:%s\n", then.tv_sec, user_name,
|
||||
runas_pw->pw_name, runas_gr ? runas_gr->gr_name : "", user_tty);
|
||||
fprintf(idfile, "%s\n", user_cwd);
|
||||
fprintf(idfile, "%s%s%s\n", user_cmnd, user_args ? " " : "",
|
||||
user_args ? user_args : "");
|
||||
fclose(idfile);
|
||||
|
||||
n = fcntl(script_fds[SFD_MASTER], F_GETFL, 0);
|
||||
if (n != -1) {
|
||||
n |= O_NONBLOCK;
|
||||
@@ -622,13 +405,19 @@ script_execv(path, argv)
|
||||
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
||||
}
|
||||
|
||||
/* Max fd we will be selecting on. */
|
||||
maxfd = sv[0];
|
||||
if (maxfd < script_fds[SFD_MASTER])
|
||||
maxfd = script_fds[SFD_MASTER];
|
||||
if (maxfd < script_fds[SFD_USERTTY])
|
||||
maxfd = script_fds[SFD_USERTTY];
|
||||
|
||||
/*
|
||||
* In the event loop we pass input from user tty to master
|
||||
* and pass output from master to stdout and ofile. Note that
|
||||
* we've set things up such that master is > 3 (see sudo.c).
|
||||
* and pass output from master to stdout and IO plugin.
|
||||
*/
|
||||
fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
|
||||
fdsw = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
|
||||
fdsr = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
|
||||
fdsw = (fd_set *)emalloc2(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask));
|
||||
zero_bytes(&input, sizeof(input));
|
||||
zero_bytes(&output, sizeof(output));
|
||||
while (alive) {
|
||||
@@ -643,8 +432,8 @@ script_execv(path, argv)
|
||||
if (output.off == output.len)
|
||||
output.off = output.len = 0;
|
||||
|
||||
zero_bytes(fdsw, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
|
||||
zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
|
||||
zero_bytes(fdsw, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
||||
zero_bytes(fdsr, howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask));
|
||||
|
||||
if (ttymode == TERM_RAW && input.len != sizeof(input.buf))
|
||||
FD_SET(script_fds[SFD_USERTTY], fdsr);
|
||||
@@ -658,51 +447,50 @@ script_execv(path, argv)
|
||||
if (relaysig)
|
||||
FD_SET(sv[0], fdsw);
|
||||
|
||||
nready = select(sv[0] + 1, fdsr, fdsw, NULL, NULL);
|
||||
nready = select(maxfd + 1, fdsr, fdsw, NULL, NULL);
|
||||
if (nready == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
log_error(USE_ERRNO, "select failed");
|
||||
error(1, "select failed");
|
||||
}
|
||||
if (FD_ISSET(sv[0], fdsr)) {
|
||||
/* read child status */
|
||||
n = read(sv[0], &child_status, sizeof(child_status));
|
||||
n = recv(sv[0], cstat, sizeof(*cstat), 0);
|
||||
if (n == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno != EAGAIN)
|
||||
break;
|
||||
} else if (n != sizeof(child_status)) {
|
||||
} else if (n != sizeof(*cstat)) {
|
||||
break; /* EOF? */
|
||||
}
|
||||
if (WIFSTOPPED(child_status)) {
|
||||
/* Suspend parent and tell child how to resume on return. */
|
||||
if (cstat->type == CMD_WSTATUS) {
|
||||
if (WIFSTOPPED(cstat->val)) {
|
||||
/* Suspend parent and tell child how to resume on return. */
|
||||
#ifdef SCRIPT_DEBUG
|
||||
warningx("child stopped, suspending parent");
|
||||
warningx("child stopped, suspending parent");
|
||||
#endif
|
||||
relaysig = suspend_parent(WSTOPSIG(child_status),
|
||||
&output, &then, &now, ofile, tfile);
|
||||
/* XXX - write relaysig immediately? */
|
||||
continue;
|
||||
} else {
|
||||
/* Child exited or was killed, either way we are done. */
|
||||
if (WIFEXITED(child_status))
|
||||
exitcode = WEXITSTATUS(child_status);
|
||||
else if (WIFSIGNALED(child_status))
|
||||
exitcode = WTERMSIG(child_status) | 128;
|
||||
relaysig = suspend_parent(WSTOPSIG(cstat->val), &output);
|
||||
/* XXX - write relaysig immediately? */
|
||||
continue;
|
||||
} else {
|
||||
/* Child exited or was killed, either way we are done. */
|
||||
break;
|
||||
}
|
||||
} else if (cstat->type == CMD_ERRNO) {
|
||||
/* Child was unable to execute command. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(sv[0], fdsw)) {
|
||||
/* XXX - we rely on child to be suspended before we suspend us */
|
||||
n = write(sv[0], &relaysig, sizeof(relaysig));
|
||||
cstat->type = CMD_SIGNO;
|
||||
cstat->val = relaysig;
|
||||
relaysig = 0;
|
||||
if (n == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
if (errno != EAGAIN)
|
||||
break;
|
||||
} else if (n != sizeof(relaysig)) {
|
||||
do {
|
||||
n = send(sv[0], cstat, sizeof(*cstat), 0);
|
||||
} while (n == -1 && errno == EINTR);
|
||||
if (n != sizeof(relaysig)) {
|
||||
break; /* should not happen */
|
||||
}
|
||||
}
|
||||
@@ -717,6 +505,7 @@ script_execv(path, argv)
|
||||
} else {
|
||||
if (n == 0)
|
||||
break; /* got EOF */
|
||||
log_input(input.buf + input.len, n);
|
||||
input.len += n;
|
||||
}
|
||||
}
|
||||
@@ -733,7 +522,6 @@ script_execv(path, argv)
|
||||
}
|
||||
}
|
||||
if (FD_ISSET(script_fds[SFD_MASTER], fdsr)) {
|
||||
gettimeofday(&now, NULL);
|
||||
n = read(script_fds[SFD_MASTER], output.buf + output.len,
|
||||
sizeof(output.buf) - output.len);
|
||||
if (n == -1) {
|
||||
@@ -744,9 +532,7 @@ script_execv(path, argv)
|
||||
} else {
|
||||
if (n == 0)
|
||||
break; /* got EOF */
|
||||
|
||||
/* Update output and timing files. */
|
||||
log_output(output.buf + output.len, n, &then, &now, ofile, tfile);
|
||||
log_output(output.buf + output.len, n);
|
||||
output.len += n;
|
||||
}
|
||||
}
|
||||
@@ -770,26 +556,15 @@ script_execv(path, argv)
|
||||
n &= ~O_NONBLOCK;
|
||||
(void) fcntl(STDOUT_FILENO, F_SETFL, n);
|
||||
}
|
||||
flush_output(&output, &then, &now, ofile, tfile);
|
||||
|
||||
#ifdef HAVE_ZLIB
|
||||
if (def_compress_transcript) {
|
||||
gzclose(ofile.g);
|
||||
gzclose(tfile.g);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
fclose(ofile.f);
|
||||
fclose(tfile.f);
|
||||
}
|
||||
flush_output(&output);
|
||||
|
||||
#ifdef HAVE_STRSIGNAL
|
||||
if (WIFSIGNALED(child_status)) {
|
||||
int signo = WTERMSIG(child_status);
|
||||
if (cstat->type == CMD_WSTATUS && WIFSIGNALED(cstat->val)) {
|
||||
int signo = WTERMSIG(cstat->val);
|
||||
if (signo && signo != SIGINT && signo != SIGPIPE) {
|
||||
char *reason = strsignal(signo);
|
||||
write(STDOUT_FILENO, reason, strlen(reason));
|
||||
if (WCOREDUMP(child_status))
|
||||
if (WCOREDUMP(cstat->val))
|
||||
write(STDOUT_FILENO, " (core dumped)", 14);
|
||||
write(STDOUT_FILENO, "\n", 1);
|
||||
}
|
||||
@@ -800,30 +575,22 @@ script_execv(path, argv)
|
||||
n = term_restore(script_fds[SFD_USERTTY], 0);
|
||||
} while (!n && errno == EINTR);
|
||||
|
||||
exit(exitcode);
|
||||
return cstat->type == CMD_ERRNO ? -1 : 0;
|
||||
}
|
||||
|
||||
void
|
||||
script_child(path, argv, backchannel, rbac_enabled)
|
||||
char *path;
|
||||
char *argv[];
|
||||
int backchannel;
|
||||
int rbac_enabled;
|
||||
int
|
||||
script_child(const char *path, char *argv[], char *envp[], int backchannel, int rbac)
|
||||
{
|
||||
struct command_status cstat;
|
||||
fd_set *fdsr;
|
||||
sigaction_t sa;
|
||||
pid_t pid, self = getpid();
|
||||
int nread, signo, status;
|
||||
#ifndef TIOCSCTTY
|
||||
int n;
|
||||
#endif
|
||||
int n, signo, status;
|
||||
|
||||
recvsig = 0;
|
||||
|
||||
/* Close unused fds. */
|
||||
close(script_fds[SFD_MASTER]);
|
||||
close(script_fds[SFD_LOG]);
|
||||
close(script_fds[SFD_OUTPUT]);
|
||||
close(script_fds[SFD_TIMING]);
|
||||
close(script_fds[SFD_USERTTY]);
|
||||
|
||||
/* Reset signal handlers. */
|
||||
@@ -840,8 +607,7 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
sigaction(SIGTTIN, &sa, NULL);
|
||||
sigaction(SIGTTOU, &sa, NULL);
|
||||
|
||||
/* We want SIGCHLD to interrupt us. */
|
||||
sa.sa_flags = 0; /* do not restart syscalls for these signals. */
|
||||
/* SIGCHLD will interrupt select. */
|
||||
sa.sa_handler = handler;
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
@@ -853,7 +619,7 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
#ifdef HAVE_SETSID
|
||||
if (setsid() == -1) {
|
||||
warning("setsid");
|
||||
_exit(1);
|
||||
goto bad;
|
||||
}
|
||||
#else
|
||||
# ifdef TIOCNOTTY
|
||||
@@ -869,7 +635,7 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
#endif
|
||||
#ifdef TIOCSCTTY
|
||||
if (ioctl(script_fds[SFD_SLAVE], TIOCSCTTY, NULL) != 0)
|
||||
log_error(USE_ERRNO, "unable to set controlling tty");
|
||||
error(1, "unable to set controlling tty");
|
||||
#else
|
||||
/* Set controlling tty by reopening slave. */
|
||||
if ((n = open(slavename, O_RDWR)) >= 0)
|
||||
@@ -880,10 +646,11 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
foreground = 0;
|
||||
|
||||
/* Start command and wait for it to stop or exit */
|
||||
/* XXX - use details->timeout */
|
||||
child = fork();
|
||||
if (child == -1) {
|
||||
warning("Can't fork");
|
||||
_exit(1);
|
||||
goto bad;
|
||||
}
|
||||
if (child == 0) {
|
||||
/* Reset signal handlers. */
|
||||
@@ -900,9 +667,9 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
sigaction(SIGUSR2, &sa, NULL);
|
||||
|
||||
/* setup tty and exec command */
|
||||
script_run(path, argv, rbac_enabled);
|
||||
warning("unable to execute %s", path);
|
||||
_exit(127);
|
||||
script_run(path, argv, envp, rbac);
|
||||
warning("unable to execute %s", path); /* XXX - leave this to plugin? */
|
||||
goto bad;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -917,6 +684,9 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
}
|
||||
|
||||
/* Wait for signal on backchannel or for SIGCHLD */
|
||||
fdsr = (fd_set *)emalloc2(howmany(backchannel + 1, NFDBITS), sizeof(fd_mask));
|
||||
zero_bytes(fdsr, howmany(backchannel + 1, NFDBITS) * sizeof(fd_mask));
|
||||
FD_SET(backchannel, fdsr);
|
||||
for (;;) {
|
||||
/* Read child status, assumes recvsig can only be SIGCHLD */
|
||||
while (recvsig) {
|
||||
@@ -934,26 +704,43 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
else
|
||||
warningx("command exited?");
|
||||
#endif
|
||||
if (write(backchannel, &status, sizeof(status)) != sizeof(status))
|
||||
cstat.type = CMD_WSTATUS;
|
||||
cstat.val = status;
|
||||
do {
|
||||
n = send(backchannel, &cstat, sizeof(cstat), 0);
|
||||
} while (n == -1 && errno == EINTR);
|
||||
if (n != sizeof(cstat))
|
||||
break; /* XXX - error, kill child and exit */
|
||||
#ifdef SCRIPT_DEBUG
|
||||
warningx("sent signo to parent");
|
||||
#endif
|
||||
if (!WIFSTOPPED(status)) {
|
||||
/* XXX */
|
||||
_exit(1); /* child dead */
|
||||
}
|
||||
}
|
||||
}
|
||||
nread = read(backchannel, &signo, sizeof(signo));
|
||||
if (nread == -1) {
|
||||
if (errno != EINTR)
|
||||
break; /* XXX - error, kill child and exit */
|
||||
continue;
|
||||
n = select(backchannel + 1, fdsr, NULL, NULL, NULL);
|
||||
if (n == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
error(1, "select failed");
|
||||
}
|
||||
if (nread != sizeof(signo)) {
|
||||
/* EOF? */
|
||||
|
||||
/* read child status */
|
||||
n = recv(backchannel, &cstat, sizeof(cstat), 0);
|
||||
if (n == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
} else if (n != sizeof(cstat)) {
|
||||
warningx("error reading command status");
|
||||
break;
|
||||
}
|
||||
if (cstat.type != CMD_SIGNO) {
|
||||
warningx("unexpected reply type on backchannel: %d", cstat.type);
|
||||
continue;
|
||||
}
|
||||
signo = cstat.val;
|
||||
|
||||
/* Handle signal from parent. */
|
||||
#ifdef SCRIPT_DEBUG
|
||||
@@ -961,7 +748,7 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
#endif
|
||||
switch (signo) {
|
||||
case SIGKILL:
|
||||
_exit(1);
|
||||
_exit(1); /* XXX */
|
||||
/* NOTREACHED */
|
||||
case SIGHUP:
|
||||
case SIGTERM:
|
||||
@@ -991,16 +778,14 @@ script_child(path, argv, backchannel, rbac_enabled)
|
||||
}
|
||||
}
|
||||
|
||||
_exit(1);
|
||||
_exit(1); /* XXX */
|
||||
|
||||
bad:
|
||||
return errno;
|
||||
}
|
||||
|
||||
static void
|
||||
flush_output(output, then, now, ofile, tfile)
|
||||
struct script_buf *output;
|
||||
struct timeval *then;
|
||||
struct timeval *now;
|
||||
union script_fd ofile;
|
||||
union script_fd tfile;
|
||||
flush_output(struct script_buf *output)
|
||||
{
|
||||
int n;
|
||||
|
||||
@@ -1017,7 +802,8 @@ flush_output(output, then, now, ofile, tfile)
|
||||
n = read(script_fds[SFD_MASTER], output->buf, sizeof(output->buf));
|
||||
if (n <= 0)
|
||||
break;
|
||||
log_output(output->buf, n, &then, &now, ofile, tfile);
|
||||
/* XXX */
|
||||
log_output(output->buf, n);
|
||||
output->off = 0;
|
||||
output->len = n;
|
||||
do {
|
||||
@@ -1031,10 +817,7 @@ flush_output(output, then, now, ofile, tfile)
|
||||
}
|
||||
|
||||
static void
|
||||
script_run(path, argv, rbac_enabled)
|
||||
char *path;
|
||||
char *argv[];
|
||||
int rbac_enabled;
|
||||
script_run(const char *path, char *argv[], char *envp[], int rbac_enabled)
|
||||
{
|
||||
pid_t self = getpid();
|
||||
|
||||
@@ -1058,16 +841,14 @@ script_run(path, argv, rbac_enabled)
|
||||
|
||||
#ifdef HAVE_SELINUX
|
||||
if (rbac_enabled)
|
||||
selinux_execv(path, argv);
|
||||
selinux_execve(path, argv, envp);
|
||||
else
|
||||
#endif
|
||||
execv(path, argv);
|
||||
execve(path, argv, envp);
|
||||
}
|
||||
|
||||
static void
|
||||
sync_winsize(src, dst)
|
||||
int src;
|
||||
int dst;
|
||||
sync_winsize(int src, int dst)
|
||||
{
|
||||
#ifdef TIOCGWINSZ
|
||||
struct winsize win;
|
||||
@@ -1087,8 +868,7 @@ sync_winsize(src, dst)
|
||||
* Handler for SIGCHLD in parent
|
||||
*/
|
||||
static void
|
||||
sigchild(s)
|
||||
int s;
|
||||
sigchild(int s)
|
||||
{
|
||||
pid_t pid;
|
||||
int serrno = errno;
|
||||
@@ -1110,8 +890,7 @@ sigchild(s)
|
||||
* Generic handler for signals passed from parent -> child
|
||||
*/
|
||||
static void
|
||||
handler(s)
|
||||
int s;
|
||||
handler(int s)
|
||||
{
|
||||
recvsig = s;
|
||||
}
|
||||
@@ -1120,8 +899,7 @@ handler(s)
|
||||
* Handler for SIGWINCH in parent
|
||||
*/
|
||||
static void
|
||||
sigwinch(s)
|
||||
int s;
|
||||
sigwinch(int s)
|
||||
{
|
||||
int serrno = errno;
|
||||
|
||||
|
893
src/sudo.c
Normal file
893
src/sudo.c
Normal file
@@ -0,0 +1,893 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2010 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef __TANDEM
|
||||
# include <floss.h>
|
||||
#endif
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
# include <sys/select.h>
|
||||
#endif /* HAVE_SYS_SELECT_H */
|
||||
#ifdef HAVE_SETRLIMIT
|
||||
# include <sys/time.h>
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#ifdef STDC_HEADERS
|
||||
# include <stdlib.h>
|
||||
# include <stddef.h>
|
||||
#else
|
||||
# ifdef HAVE_STDLIB_H
|
||||
# include <stdlib.h>
|
||||
# endif
|
||||
#endif /* STDC_HEADERS */
|
||||
#ifdef HAVE_STRING_H
|
||||
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
|
||||
# include <memory.h>
|
||||
# endif
|
||||
# include <string.h>
|
||||
#else
|
||||
# ifdef HAVE_STRINGS_H
|
||||
# include <strings.h>
|
||||
# endif
|
||||
#endif /* HAVE_STRING_H */
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif /* HAVE_UNISTD_H */
|
||||
#include <pwd.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <signal.h>
|
||||
#include <grp.h>
|
||||
#if TIME_WITH_SYS_TIME
|
||||
# include <time.h>
|
||||
#endif
|
||||
#ifdef HAVE_SETLOCALE
|
||||
# include <locale.h>
|
||||
#endif
|
||||
#ifdef HAVE_LOGIN_CAP_H
|
||||
# include <login_cap.h>
|
||||
#endif
|
||||
|
||||
#include <sudo_usage.h>
|
||||
#include "sudo.h"
|
||||
#include "sudo_plugin.h"
|
||||
#include "sudo_plugin_int.h"
|
||||
|
||||
#ifdef USING_NONUNIX_GROUPS
|
||||
# include "nonunix.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables
|
||||
*/
|
||||
struct plugin_container policy_plugin;
|
||||
struct plugin_container_list io_plugins;
|
||||
|
||||
/*
|
||||
* Local functions
|
||||
*/
|
||||
static void fix_fds(void);
|
||||
static void disable_coredumps(void);
|
||||
static char **get_user_info(struct user_details *);
|
||||
static void command_info_to_details(char * const info[],
|
||||
struct command_details *details);
|
||||
static int run_command(struct command_details *details, char *argv[],
|
||||
char *envp[]);
|
||||
|
||||
/* XXX - header file */
|
||||
extern const char *list_user, *runas_user, *runas_group;
|
||||
|
||||
/* Used by getprogname() unless crt0 supports getting program name. */
|
||||
int Argc;
|
||||
char **Argv;
|
||||
|
||||
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
|
||||
static struct rlimit corelimit;
|
||||
#endif /* RLIMIT_CORE && !SUDO_DEVEL */
|
||||
sigaction_t saved_sa_int, saved_sa_quit, saved_sa_tstp;
|
||||
|
||||
int
|
||||
main(int argc, char *argv[], char *envp[])
|
||||
{
|
||||
sigaction_t sa;
|
||||
int nargc, sudo_mode;
|
||||
char **nargv, **settings, **env_add;
|
||||
char **user_info, **command_info, **argv_out, **user_env_out;
|
||||
struct plugin_container *plugin;
|
||||
struct user_details user_details;
|
||||
struct command_details command_details;
|
||||
int ok;
|
||||
#if defined(SUDO_DEVEL) && defined(__OpenBSD__)
|
||||
extern char *malloc_options;
|
||||
malloc_options = "AFGJPR";
|
||||
#endif
|
||||
|
||||
Argc = argc;
|
||||
Argv = argv;
|
||||
|
||||
#ifdef HAVE_SETLOCALE
|
||||
setlocale(LC_ALL, "");
|
||||
#endif
|
||||
|
||||
if (geteuid() != 0)
|
||||
errorx(1, "must be setuid root");
|
||||
|
||||
/* XXX - Must be done before shadow file lookups... */
|
||||
#if defined(HAVE_GETPRPWNAM) && defined(HAVE_SET_AUTH_PARAMETERS)
|
||||
(void) set_auth_parameters(Argc, Argv);
|
||||
# ifdef HAVE_INITPRIVS
|
||||
initprivs();
|
||||
# endif
|
||||
#endif /* HAVE_GETPRPWNAM && HAVE_SET_AUTH_PARAMETERS */
|
||||
|
||||
/*
|
||||
* Signal setup:
|
||||
* Ignore keyboard-generated signals so the user cannot interrupt
|
||||
* us at some point and avoid the logging.
|
||||
* XXX - leave this to the plugin?
|
||||
*/
|
||||
zero_bytes(&sa, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
sa.sa_handler = SIG_IGN;
|
||||
(void) sigaction(SIGINT, &sa, &saved_sa_int);
|
||||
(void) sigaction(SIGQUIT, &sa, &saved_sa_quit);
|
||||
(void) sigaction(SIGTSTP, &sa, &saved_sa_tstp);
|
||||
|
||||
/* Turn off core dumps and make sure fds 0-2 are open. */
|
||||
disable_coredumps();
|
||||
fix_fds();
|
||||
|
||||
/* Parse command line arguments. */
|
||||
sudo_mode = parse_args(Argc, Argv, &nargc, &nargv, &settings, &env_add);
|
||||
|
||||
/* Read sudo.conf and load plugins. */
|
||||
sudo_load_plugins(_PATH_SUDO_CONF, &policy_plugin, &io_plugins);
|
||||
|
||||
/* Fill in user_info with user name, uid, cwd, etc. */
|
||||
memset(&user_details, 0, sizeof(user_details));
|
||||
user_info = get_user_info(&user_details);
|
||||
|
||||
/* Open each plugin (XXX - check for errors). */
|
||||
policy_plugin.u.policy->open(SUDO_API_VERSION, sudo_conversation,
|
||||
settings, user_info, envp);
|
||||
tq_foreach_fwd(&io_plugins, plugin) {
|
||||
/* XXX - remove from list if open returns 0 */
|
||||
plugin->u.io->open(SUDO_API_VERSION, sudo_conversation, settings,
|
||||
user_info, envp);
|
||||
}
|
||||
|
||||
/* XXX - should not need to check for MODE_INVALIDATE ORed in */
|
||||
warningx("sudo_mode %d", sudo_mode); /* XXX */
|
||||
switch (sudo_mode & MODE_MASK) {
|
||||
case MODE_VERSION:
|
||||
policy_plugin.u.policy->show_version(!user_details.uid);
|
||||
tq_foreach_fwd(&io_plugins, plugin) {
|
||||
plugin->u.io->show_version(!user_details.uid);
|
||||
}
|
||||
break;
|
||||
case MODE_VALIDATE:
|
||||
case MODE_VALIDATE|MODE_INVALIDATE:
|
||||
if (policy_plugin.u.policy->validate == NULL) {
|
||||
warningx("policy plugin %s does not support the -v flag",
|
||||
policy_plugin.name);
|
||||
ok = FALSE;
|
||||
} else {
|
||||
ok = policy_plugin.u.policy->validate();
|
||||
}
|
||||
exit(ok != TRUE);
|
||||
case MODE_KILL:
|
||||
case MODE_INVALIDATE:
|
||||
if (policy_plugin.u.policy->validate == NULL) {
|
||||
warningx("policy plugin %s does not support the -k/-K flags",
|
||||
policy_plugin.name);
|
||||
exit(1);
|
||||
}
|
||||
policy_plugin.u.policy->invalidate(sudo_mode == MODE_KILL);
|
||||
exit(0);
|
||||
break;
|
||||
case MODE_CHECK:
|
||||
case MODE_CHECK|MODE_INVALIDATE:
|
||||
case MODE_LIST:
|
||||
case MODE_LIST|MODE_INVALIDATE:
|
||||
if (policy_plugin.u.policy->list == NULL) {
|
||||
warningx("policy plugin %s does not support listing privileges",
|
||||
policy_plugin.name);
|
||||
ok = FALSE;
|
||||
} else {
|
||||
ok = policy_plugin.u.policy->list(nargc, nargv,
|
||||
ISSET(sudo_mode, MODE_LONG_LIST), list_user);
|
||||
}
|
||||
exit(ok != TRUE);
|
||||
case MODE_RUN:
|
||||
ok = policy_plugin.u.policy->check_policy(nargc, nargv, env_add,
|
||||
&command_info, &argv_out, &user_env_out);
|
||||
warningx("policy plugin returns %d", ok); /* XXX */
|
||||
if (ok != TRUE)
|
||||
exit(ok); /* plugin printed error message */
|
||||
command_info_to_details(command_info, &command_details);
|
||||
/* Restore coredumpsize resource limit before running. */
|
||||
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
|
||||
(void) setrlimit(RLIMIT_CORE, &corelimit);
|
||||
#endif /* RLIMIT_CORE && !SUDO_DEVEL */
|
||||
/* run_command will call close for us */
|
||||
run_command(&command_details, argv_out, user_env_out);
|
||||
break;
|
||||
case MODE_EDIT:
|
||||
/* XXX - fill in */
|
||||
break;
|
||||
default:
|
||||
errorx(1, "unexpected sudo mode 0x%x", sudo_mode);
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Ensure that stdin, stdout and stderr are open; set to /dev/null if not.
|
||||
* Some operating systems do this automatically in the kernel or libc.
|
||||
*/
|
||||
static void
|
||||
fix_fds(void)
|
||||
{
|
||||
int miss[3], devnull = -1;
|
||||
|
||||
/*
|
||||
* stdin, stdout and stderr must be open; set them to /dev/null
|
||||
* if they are closed.
|
||||
*/
|
||||
miss[STDIN_FILENO] = fcntl(STDIN_FILENO, F_GETFL, 0) == -1;
|
||||
miss[STDOUT_FILENO] = fcntl(STDOUT_FILENO, F_GETFL, 0) == -1;
|
||||
miss[STDERR_FILENO] = fcntl(STDERR_FILENO, F_GETFL, 0) == -1;
|
||||
if (miss[STDIN_FILENO] || miss[STDOUT_FILENO] || miss[STDERR_FILENO]) {
|
||||
if ((devnull = open(_PATH_DEVNULL, O_RDWR, 0644)) != -1) {
|
||||
if (miss[STDIN_FILENO])
|
||||
(void) dup2(devnull, STDIN_FILENO);
|
||||
if (miss[STDOUT_FILENO])
|
||||
(void) dup2(devnull, STDOUT_FILENO);
|
||||
if (miss[STDERR_FILENO])
|
||||
(void) dup2(devnull, STDERR_FILENO);
|
||||
if (devnull > STDERR_FILENO)
|
||||
close(devnull);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
get_user_groups(struct user_details *ud)
|
||||
{
|
||||
char *gid_list = NULL;
|
||||
#ifdef HAVE_GETGROUPS
|
||||
size_t glsize;
|
||||
char *cp;
|
||||
int i;
|
||||
|
||||
if ((ud->ngroups = getgroups(0, NULL)) <= 0)
|
||||
return NULL;
|
||||
|
||||
ud->groups = emalloc2(ud->ngroups, sizeof(GETGROUPS_T));
|
||||
if (getgroups(ud->ngroups, ud->groups) < 0)
|
||||
error(1, "can't get group vector");
|
||||
glsize = sizeof("groups=") - 1 + (ud->ngroups * (MAX_UID_T_LEN + 1));
|
||||
gid_list = emalloc(glsize);
|
||||
memcpy(gid_list, "groups=", sizeof("groups=") - 1);
|
||||
cp = gid_list + sizeof("groups=") - 1;
|
||||
for (i = 0; i < ud->ngroups; i++) {
|
||||
snprintf(cp, glsize - (cp - gid_list), "%lu%s",
|
||||
(unsigned long)ud->groups[i], i ? "," : "");
|
||||
}
|
||||
#endif
|
||||
return gid_list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return user information as an array of name=value pairs.
|
||||
* and fill in struct user_details (which shares the same strings).
|
||||
*/
|
||||
static char **
|
||||
get_user_info(struct user_details *ud)
|
||||
{
|
||||
char cwd[PATH_MAX];
|
||||
char host[MAXHOSTNAMELEN];
|
||||
char **user_info, *cp;
|
||||
struct passwd *pw;
|
||||
int i = 0;
|
||||
|
||||
/* XXX - bound check number of entries */
|
||||
user_info = emalloc2(32, sizeof(char *));
|
||||
|
||||
ud->uid = getuid();
|
||||
ud->euid = geteuid();
|
||||
ud->gid = getgid();
|
||||
ud->egid = getegid();
|
||||
|
||||
pw = getpwuid(ud->uid);
|
||||
if (pw == NULL)
|
||||
errorx(1, "unknown uid %lu: who are you?", (unsigned long)ud->uid);
|
||||
|
||||
user_info[i] = fmt_string("user", pw->pw_name);
|
||||
ud->username = user_info[i] + sizeof("user=") - 1;
|
||||
|
||||
easprintf(&user_info[++i], "uid=%lu", (unsigned long)ud->uid);
|
||||
easprintf(&user_info[++i], "euid=%lu", (unsigned long)ud->euid);
|
||||
easprintf(&user_info[++i], "gid=%lu", (unsigned long)ud->gid);
|
||||
easprintf(&user_info[++i], "egid=%lu", (unsigned long)ud->egid);
|
||||
|
||||
if ((cp = get_user_groups(ud)) != NULL)
|
||||
user_info[++i] = cp;
|
||||
|
||||
if (getcwd(cwd, sizeof(cwd)) != NULL) {
|
||||
user_info[++i] = fmt_string("cwd", cwd);
|
||||
ud->cwd = user_info[i] + sizeof("cwd=") - 1;
|
||||
}
|
||||
|
||||
if ((cp = ttyname(STDIN_FILENO)) || (cp = ttyname(STDOUT_FILENO)) ||
|
||||
(cp = ttyname(STDERR_FILENO))) {
|
||||
user_info[++i] = fmt_string("tty", cp);
|
||||
ud->tty = user_info[i] + sizeof("tty=") - 1;
|
||||
}
|
||||
|
||||
if (gethostname(host, sizeof(host)) == 0)
|
||||
host[sizeof(host) - 1] = '\0';
|
||||
else
|
||||
strlcpy(host, "localhost", sizeof(host));
|
||||
user_info[++i] = fmt_string("host", host);
|
||||
ud->host = user_info[i] + sizeof("host=") - 1;
|
||||
|
||||
user_info[++i] = NULL;
|
||||
|
||||
return user_info;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert a command_info array into a command_details structure.
|
||||
*/
|
||||
static void
|
||||
command_info_to_details(char * const info[], struct command_details *details)
|
||||
{
|
||||
int i;
|
||||
long lval;
|
||||
unsigned long ulval;
|
||||
char *cp, *ep;
|
||||
|
||||
memset(details, 0, sizeof(*details));
|
||||
|
||||
for (i = 0; info[i] != NULL; i++) {
|
||||
/* XXX - should ignore empty entries */
|
||||
switch (info[i][0]) {
|
||||
case 'c':
|
||||
if (strncmp("chroot=", info[i], sizeof("chroot=") - 1) == 0) {
|
||||
details->chroot = info[i] + sizeof("chroot=") - 1;
|
||||
break;
|
||||
}
|
||||
if (strncmp("command=", info[i], sizeof("command=") - 1) == 0) {
|
||||
details->command = info[i] + sizeof("command=") - 1;
|
||||
break;
|
||||
}
|
||||
if (strncmp("cwd=", info[i], sizeof("cwd=") - 1) == 0) {
|
||||
details->cwd = info[i] + sizeof("cwd=") - 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (strncmp("login_class=", info[i], sizeof("login_class=") - 1) == 0) {
|
||||
details->login_class = info[i] + sizeof("login_class=") - 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'n':
|
||||
/* XXX - bounds check -NZERO to NZERO (inclusive). */
|
||||
if (strncmp("nice=", info[i], sizeof("nice=") - 1) == 0) {
|
||||
cp = info[i] + sizeof("nice=") - 1;
|
||||
errno = 0;
|
||||
lval = strtol(cp, &ep, 0);
|
||||
if (*cp != '\0' && *ep == '\0' &&
|
||||
!(errno == ERANGE &&
|
||||
(lval == LONG_MAX || lval == LONG_MIN)) &&
|
||||
lval < INT_MAX && lval > INT_MIN) {
|
||||
details->priority = (int)lval;
|
||||
SET(details->flags, CD_SET_PRIORITY);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (strncmp("noexec=", info[i], sizeof("noexec=") - 1) == 0) {
|
||||
if (atobool(info[i] + sizeof("noexec=") - 1))
|
||||
SET(details->flags, CD_NOEXEC);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
if (strncmp("preserve_groups=", info[i], sizeof("preserve_groups=") - 1) == 0) {
|
||||
if (atobool(info[i] + sizeof("preserve_groups=") - 1))
|
||||
SET(details->flags, CD_PRESERVE_GROUPS);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (strncmp("runas_egid=", info[i], sizeof("runas_egid=") - 1) == 0) {
|
||||
cp = info[i] + sizeof("runas_egid=") - 1;
|
||||
errno = 0;
|
||||
ulval = strtoul(cp, &ep, 0);
|
||||
if (*cp != '\0' && *ep == '\0' &&
|
||||
(errno != ERANGE || ulval != ULONG_MAX)) {
|
||||
details->egid = (gid_t)ulval;
|
||||
SET(details->flags, CD_SET_EGID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (strncmp("runas_euid=", info[i], sizeof("runas_euid=") - 1) == 0) {
|
||||
cp = info[i] + sizeof("runas_euid=") - 1;
|
||||
errno = 0;
|
||||
ulval = strtoul(cp, &ep, 0);
|
||||
if (*cp != '\0' && *ep == '\0' &&
|
||||
(errno != ERANGE || ulval != ULONG_MAX)) {
|
||||
details->euid = (uid_t)ulval;
|
||||
SET(details->flags, CD_SET_EUID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (strncmp("runas_gid=", info[i], sizeof("runas_gid=") - 1) == 0) {
|
||||
cp = info[i] + sizeof("runas_gid=") - 1;
|
||||
errno = 0;
|
||||
ulval = strtoul(cp, &ep, 0);
|
||||
if (*cp != '\0' && *ep == '\0' &&
|
||||
(errno != ERANGE || ulval != ULONG_MAX)) {
|
||||
details->gid = (gid_t)ulval;
|
||||
SET(details->flags, CD_SET_GID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (strncmp("runas_groups=", info[i], sizeof("runas_groups=") - 1) == 0) {
|
||||
int j;
|
||||
|
||||
/* count groups, alloc and fill in */
|
||||
cp = info[i] + sizeof("runas_groups=") - 1;
|
||||
for (;;) {
|
||||
details->ngroups++;
|
||||
if ((cp = strchr(cp, ',')) == NULL)
|
||||
break;
|
||||
cp++;
|
||||
}
|
||||
details->groups = emalloc2(details->ngroups, sizeof(GETGROUPS_T));
|
||||
cp = info[i] + sizeof("runas_groups=") - 1;
|
||||
for (j = 0; j < details->ngroups;) {
|
||||
errno = 0;
|
||||
ulval = strtoul(cp, &ep, 0);
|
||||
if (*cp != '\0' && (*ep == ',' || *ep == '\0') &&
|
||||
(errno != ERANGE || ulval != ULONG_MAX)) {
|
||||
details->groups[j++] = (gid_t)ulval;
|
||||
}
|
||||
}
|
||||
details->ngroups = j;
|
||||
break;
|
||||
}
|
||||
if (strncmp("runas_uid=", info[i], sizeof("runas_uid=") - 1) == 0) {
|
||||
cp = info[i] + sizeof("runas_uid=") - 1;
|
||||
errno = 0;
|
||||
ulval = strtoul(cp, &ep, 0);
|
||||
if (*cp != '\0' && *ep == '\0' &&
|
||||
(errno != ERANGE || ulval != ULONG_MAX)) {
|
||||
details->uid = (uid_t)ulval;
|
||||
SET(details->flags, CD_SET_UID);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
if (strncmp("selinux_role=", info[i], sizeof("selinux_role=") - 1) == 0) {
|
||||
details->selinux_role = info[i] + sizeof("selinux_role=") - 1;
|
||||
break;
|
||||
}
|
||||
if (strncmp("selinux_type=", info[i], sizeof("selinux_type=") - 1) == 0) {
|
||||
details->selinux_type = info[i] + sizeof("selinux_type=") - 1;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 't':
|
||||
if (strncmp("timeout=", info[i], sizeof("timeout=") - 1) == 0) {
|
||||
cp = info[i] + sizeof("timeout=") - 1;
|
||||
errno = 0;
|
||||
lval = strtol(cp, &ep, 0);
|
||||
if (*cp != '\0' && *ep == '\0' &&
|
||||
!(errno == ERANGE &&
|
||||
(lval == LONG_MAX || lval == LONG_MIN)) &&
|
||||
lval <= INT_MAX && lval >= 0) {
|
||||
details->timeout = (int)lval;
|
||||
SET(details->flags, CD_SET_TIMEOUT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (strncmp("umask=", info[i], sizeof("umask=") - 1) == 0) {
|
||||
cp = info[i] + sizeof("umask=") - 1;
|
||||
errno = 0;
|
||||
ulval = strtoul(cp, &ep, 8);
|
||||
if (*cp != '\0' && *ep == '\0' &&
|
||||
(errno != ERANGE || ulval != ULONG_MAX)) {
|
||||
details->umask = (uid_t)ulval;
|
||||
SET(details->flags, CD_SET_UMASK);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ISSET(details->flags, CD_SET_EUID))
|
||||
details->euid = details->uid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable core dumps to avoid dropping a core with user password in it.
|
||||
* We will reset this limit before executing the command.
|
||||
* Not all operating systems disable core dumps for setuid processes.
|
||||
*/
|
||||
static void
|
||||
disable_coredumps(void)
|
||||
{
|
||||
#if defined(__linux__) || (defined(RLIMIT_CORE) && !defined(SUDO_DEVEL))
|
||||
struct rlimit rl;
|
||||
#endif
|
||||
|
||||
#if defined(__linux__)
|
||||
/*
|
||||
* Unlimit the number of processes since Linux's setuid() will
|
||||
* apply resource limits when changing uid and return EAGAIN if
|
||||
* nproc would be violated by the uid switch.
|
||||
*/
|
||||
rl.rlim_cur = rl.rlim_max = RLIM_INFINITY;
|
||||
if (setrlimit(RLIMIT_NPROC, &rl)) {
|
||||
if (getrlimit(RLIMIT_NPROC, &rl) == 0) {
|
||||
rl.rlim_cur = rl.rlim_max;
|
||||
(void)setrlimit(RLIMIT_NPROC, &rl);
|
||||
}
|
||||
}
|
||||
#endif /* __linux__ */
|
||||
#if defined(RLIMIT_CORE) && !defined(SUDO_DEVEL)
|
||||
/*
|
||||
* Turn off core dumps.
|
||||
*/
|
||||
(void) getrlimit(RLIMIT_CORE, &corelimit);
|
||||
memcpy(&rl, &corelimit, sizeof(struct rlimit));
|
||||
rl.rlim_cur = 0;
|
||||
(void) setrlimit(RLIMIT_CORE, &rl);
|
||||
#endif /* RLIMIT_CORE && !SUDO_DEVEL */
|
||||
}
|
||||
|
||||
/*
|
||||
* Cleanup hook for error()/errorx()
|
||||
*/
|
||||
void
|
||||
cleanup(gotsignal)
|
||||
int gotsignal;
|
||||
{
|
||||
#if 0 /* XXX */
|
||||
struct sudo_nss *nss;
|
||||
|
||||
if (!gotsignal) {
|
||||
if (snl != NULL) {
|
||||
tq_foreach_fwd(snl, nss)
|
||||
nss->close(nss);
|
||||
}
|
||||
sudo_endpwent();
|
||||
sudo_endgrent();
|
||||
}
|
||||
#ifdef _PATH_SUDO_TRANSCRIPT
|
||||
if (def_transcript)
|
||||
term_restore(STDIN_FILENO, 0);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup the execution environment immediately prior to the call to execve()
|
||||
*/
|
||||
int
|
||||
exec_setup(struct command_details *details)
|
||||
{
|
||||
struct passwd *pw;
|
||||
|
||||
pw = getpwuid(details->euid);
|
||||
if (pw != NULL) {
|
||||
#ifdef HAVE_GETUSERATTR
|
||||
aix_setlimits(pw->pw_name);
|
||||
#endif
|
||||
#ifdef HAVE_LOGIN_CAP_H
|
||||
if (details->login_class) {
|
||||
int flags;
|
||||
login_cap_t *lc;
|
||||
|
||||
/*
|
||||
* We only use setusercontext() to set the nice value and rlimits.
|
||||
*/
|
||||
lc = login_getclass((char *)details->login_class);
|
||||
if (!lc) {
|
||||
warningx("unknown login class %s", details->login_class);
|
||||
errno = ENOENT;
|
||||
goto done;
|
||||
}
|
||||
flags = LOGIN_SETRESOURCES|LOGIN_SETPRIORITY;
|
||||
if (setusercontext(lc, pw, pw->pw_uid, flags)) {
|
||||
if (pw->pw_uid != ROOT_UID) {
|
||||
warning("unable to set user context");
|
||||
goto done;
|
||||
} else
|
||||
warning("unable to set user context");
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_LOGIN_CAP_H */
|
||||
}
|
||||
|
||||
/*
|
||||
* Set groups, including supplementary group vector.
|
||||
*/
|
||||
#ifdef HAVE_SETEUID
|
||||
if (ISSET(details->flags, CD_SET_EGID) && setegid(details->egid)) {
|
||||
warning("unable to set egid to runas gid");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
if (ISSET(details->flags, CD_SET_GID) && setgid(details->gid)) {
|
||||
warning("unable to set gid to runas gid");
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (!ISSET(details->flags, CD_PRESERVE_GROUPS)) {
|
||||
/* XXX - may need to initgroups anyway--plugin may not have list */
|
||||
#ifdef HAVE_GETGROUPS
|
||||
if (details->ngroups >= 0) {
|
||||
if (setgroups(details->ngroups, details->groups) < 0) {
|
||||
warning("unable to set supplementary group IDs");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (pw && initgroups(pw->pw_name, pw->pw_gid) < 0) {
|
||||
warning("unable to set supplementary group IDs");
|
||||
goto done;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (ISSET(details->flags, CD_SET_PRIORITY)) {
|
||||
if (setpriority(PRIO_PROCESS, 0, details->priority) != 0) {
|
||||
warning("unable to set process priority");
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (ISSET(details->flags, CD_SET_UMASK))
|
||||
(void) umask(details->umask);
|
||||
if (ISSET(details->flags, CD_SET_TIMEOUT))
|
||||
alarm(details->timeout);
|
||||
if (details->chroot) {
|
||||
if (chroot(details->chroot) != 0 || chdir("/") != 0) {
|
||||
warning("unable to change root to %s", details->chroot);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
if (details->cwd) {
|
||||
/* cwd is relative to the new root, if any */
|
||||
if (chdir(details->cwd) != 0) {
|
||||
warning("unable to change directory to %s", details->cwd);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* Must set uids last */
|
||||
#ifdef HAVE_SETRESUID
|
||||
if (setresuid(details->uid, details->euid, details->euid) != 0) {
|
||||
warning("unable to change to runas uid");
|
||||
goto done;
|
||||
}
|
||||
#elif HAVE_SETREUID
|
||||
if (setreuid(details->uid, details->euid) != 0) {
|
||||
warning("unable to change to runas uid");
|
||||
goto done;
|
||||
}
|
||||
#else
|
||||
if (seteuid(details->euid) != 0 || setuid(details->euid) != 0) {
|
||||
warning("unable to change to runas uid");
|
||||
goto done;
|
||||
}
|
||||
#endif /* !HAVE_SETRESUID && !HAVE_SETREUID */
|
||||
|
||||
errno = 0;
|
||||
|
||||
done:
|
||||
return errno;
|
||||
}
|
||||
|
||||
static sig_atomic_t sigchld;
|
||||
|
||||
static void
|
||||
sigchild(int s)
|
||||
{
|
||||
sigchld = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the command and wait for it to complete.
|
||||
*/
|
||||
static int
|
||||
run_command(struct command_details *details, char *argv[], char *envp[])
|
||||
{
|
||||
struct plugin_container *plugin;
|
||||
struct command_status cstat;
|
||||
int exitcode = 1;
|
||||
|
||||
cstat.type = CMD_INVALID;
|
||||
cstat.val = 0;
|
||||
|
||||
/*
|
||||
* XXX - missing closefrom(), may not be possible in new scheme
|
||||
* also no background support
|
||||
* or selinux...
|
||||
*/
|
||||
|
||||
/* If there are I/O plugins, allocate a pty and exec */
|
||||
if (!tq_empty(&io_plugins)) {
|
||||
warningx("script mode"); /* XXX */
|
||||
script_setup(details->euid);
|
||||
script_execve(details, argv, envp, &cstat);
|
||||
} else {
|
||||
pid_t child, pid;
|
||||
int nready, sv[2];
|
||||
ssize_t nread;
|
||||
sigaction_t sa;
|
||||
fd_set *fdsr;
|
||||
|
||||
zero_bytes(&sa, sizeof(sa));
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_RESTART;
|
||||
|
||||
/* Want select() to be interrupted when child dies. */
|
||||
sa.sa_handler = sigchild;
|
||||
sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
/* Ignore SIGPIPE from other end of socketpair. */
|
||||
sa.sa_handler = SIG_IGN;
|
||||
sigaction(SIGPIPE, &sa, NULL);
|
||||
|
||||
if (socketpair(PF_UNIX, SOCK_DGRAM, 0, sv) != 0)
|
||||
error(1, "cannot create sockets");
|
||||
|
||||
child = fork();
|
||||
if (child == -1)
|
||||
error(1, "unable to fork");
|
||||
|
||||
if (child == 0) {
|
||||
/* child */
|
||||
close(sv[0]);
|
||||
if (exec_setup(details) == 0) {
|
||||
/* XXX - fallback via /bin/sh */
|
||||
execve(details->command, argv, envp);
|
||||
}
|
||||
cstat.type = CMD_ERRNO;
|
||||
cstat.val = errno;
|
||||
write(sv[1], &cstat, sizeof(cstat));
|
||||
_exit(1);
|
||||
}
|
||||
close(sv[1]);
|
||||
warningx("waiting for child"); /* XXX */
|
||||
|
||||
/* wait for child to complete or for data on sv[0] */
|
||||
fdsr = (fd_set *)emalloc2(howmany(sv[0] + 1, NFDBITS), sizeof(fd_mask));
|
||||
zero_bytes(fdsr, howmany(sv[0] + 1, NFDBITS) * sizeof(fd_mask));
|
||||
FD_SET(sv[0], fdsr);
|
||||
for (;;) {
|
||||
if (sigchld) {
|
||||
sigchld = 0;
|
||||
do {
|
||||
pid = waitpid(child, &cstat.val, WNOHANG);
|
||||
if (pid == child)
|
||||
cstat.type = CMD_WSTATUS;
|
||||
} while (pid == -1 && errno == EINTR);
|
||||
if (cstat.type == CMD_WSTATUS) {
|
||||
/* command terminated, we're done */
|
||||
break;
|
||||
}
|
||||
}
|
||||
nready = select(sv[0] + 1, fdsr, NULL, NULL, NULL);
|
||||
if (nready == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
error(1, "select failed");
|
||||
}
|
||||
if (FD_ISSET(sv[0], fdsr)) {
|
||||
/* read child status */
|
||||
nread = recv(sv[0], &cstat, sizeof(cstat), 0);
|
||||
if (nread == -1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
} else if (nread != sizeof(cstat)) {
|
||||
warningx("error reading command status");
|
||||
}
|
||||
break; /* XXX */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (cstat.type) {
|
||||
case CMD_ERRNO:
|
||||
/* exec_setup() or execve() returned an error. */
|
||||
policy_plugin.u.policy->close(0, cstat.val);
|
||||
tq_foreach_fwd(&io_plugins, plugin) {
|
||||
plugin->u.io->close(0, cstat.val);
|
||||
}
|
||||
exitcode = 1;
|
||||
break;
|
||||
case CMD_WSTATUS:
|
||||
/* Command ran, exited or was killed. */
|
||||
policy_plugin.u.policy->close(cstat.val, 0);
|
||||
tq_foreach_fwd(&io_plugins, plugin) {
|
||||
plugin->u.io->close(0, cstat.val);
|
||||
}
|
||||
if (WIFEXITED(cstat.val))
|
||||
exitcode = WEXITSTATUS(cstat.val);
|
||||
else if (WIFSIGNALED(cstat.val))
|
||||
exitcode = WTERMSIG(cstat.val) | 128;
|
||||
break;
|
||||
default:
|
||||
warningx("unexpected child termination condition: %d", cstat.type);
|
||||
break;
|
||||
}
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
#if 0 /* XXX - convert warning/error to something log this */
|
||||
/*
|
||||
* Simple debugging/logging.
|
||||
* XXX - use askpass if configured?
|
||||
*/
|
||||
void
|
||||
sudo_log(int level, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
switch (level) {
|
||||
case SUDO_LOG_INFO:
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stdout, fmt, ap);
|
||||
va_end(ap);
|
||||
break;
|
||||
case SUDO_LOG_DEBUG1:
|
||||
case SUDO_LOG_DEBUG2:
|
||||
case SUDO_LOG_DEBUG3:
|
||||
case SUDO_LOG_DEBUG4:
|
||||
case SUDO_LOG_DEBUG5:
|
||||
case SUDO_LOG_DEBUG6:
|
||||
case SUDO_LOG_DEBUG7:
|
||||
case SUDO_LOG_DEBUG8:
|
||||
case SUDO_LOG_DEBUG9:
|
||||
if (level > debug_level)
|
||||
return;
|
||||
/* FALLTHROUGH */
|
||||
case SUDO_LOG_WARN:
|
||||
case SUDO_LOG_ERROR:
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
#endif
|
234
src/sudo.h
Normal file
234
src/sudo.h
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (c) 1993-1996, 1998-2005, 2007-2009
|
||||
* Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* Sponsored in part by the Defense Advanced Research Projects
|
||||
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
||||
* Materiel Command, USAF, under agreement number F39502-99-1-0512.
|
||||
*
|
||||
* $Sudo: sudo.h,v 1.290 2009/12/12 16:12:26 millert Exp $
|
||||
*/
|
||||
|
||||
#ifndef _SUDO_SUDO_H
|
||||
#define _SUDO_SUDO_H
|
||||
|
||||
#include <pathnames.h>
|
||||
#include <limits.h>
|
||||
#include "compat.h"
|
||||
#include "alloc.h"
|
||||
#include "error.h"
|
||||
#include "list.h"
|
||||
#include "missing.h"
|
||||
|
||||
#ifdef __TANDEM
|
||||
# define ROOT_UID 65535
|
||||
#else
|
||||
# define ROOT_UID 0
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Pseudo-boolean values
|
||||
*/
|
||||
#undef TRUE
|
||||
#define TRUE 1
|
||||
#undef FALSE
|
||||
#define FALSE 0
|
||||
|
||||
/*
|
||||
* Various modes sudo can be in (based on arguments) in hex
|
||||
*/
|
||||
#define MODE_RUN 0x00000001
|
||||
#define MODE_EDIT 0x00000002
|
||||
#define MODE_VALIDATE 0x00000004
|
||||
#define MODE_INVALIDATE 0x00000008
|
||||
#define MODE_KILL 0x00000010
|
||||
#define MODE_VERSION 0x00000020
|
||||
#define MODE_HELP 0x00000040
|
||||
#define MODE_LIST 0x00000080
|
||||
#define MODE_CHECK 0x00000100
|
||||
#define MODE_MASK 0x0000ffff
|
||||
|
||||
/* Mode flags */
|
||||
/* XXX - prune this */
|
||||
#define MODE_BACKGROUND 0x00010000
|
||||
#define MODE_SHELL 0x00020000
|
||||
#define MODE_LOGIN_SHELL 0x00040000
|
||||
#define MODE_IMPLIED_SHELL 0x00080000
|
||||
#define MODE_RESET_HOME 0x00100000
|
||||
#define MODE_PRESERVE_GROUPS 0x00200000
|
||||
#define MODE_PRESERVE_ENV 0x00400000
|
||||
#define MODE_NONINTERACTIVE 0x00800000
|
||||
#define MODE_LONG_LIST 0x01000000
|
||||
|
||||
/*
|
||||
* Used with set_perms()
|
||||
*/
|
||||
#define PERM_ROOT 0x00
|
||||
#define PERM_USER 0x01
|
||||
#define PERM_FULL_USER 0x02
|
||||
#define PERM_SUDOERS 0x03
|
||||
#define PERM_RUNAS 0x04
|
||||
#define PERM_FULL_RUNAS 0x05
|
||||
#define PERM_TIMESTAMP 0x06
|
||||
#define PERM_NOEXIT 0x10 /* flag */
|
||||
#define PERM_MASK 0xf0
|
||||
|
||||
/*
|
||||
* We used to use the system definition of PASS_MAX or _PASSWD_LEN,
|
||||
* but that caused problems with various alternate authentication
|
||||
* methods. So, we just define our own and assume that it is >= the
|
||||
* system max.
|
||||
*/
|
||||
#define SUDO_PASS_MAX 256
|
||||
|
||||
/*
|
||||
* Flags for lock_file()
|
||||
*/
|
||||
#define SUDO_LOCK 1 /* lock a file */
|
||||
#define SUDO_TLOCK 2 /* test & lock a file (non-blocking) */
|
||||
#define SUDO_UNLOCK 4 /* unlock a file */
|
||||
|
||||
/*
|
||||
* Flags for tgetpass()
|
||||
*/
|
||||
#define TGP_ECHO 0x01 /* leave echo on when reading passwd */
|
||||
#define TGP_STDIN 0x02 /* read from stdin, not /dev/tty */
|
||||
#define TGP_ASKPASS 0x04 /* read from askpass helper program */
|
||||
#define TGP_FEEDBACK 0x08 /* visual feedback during input */
|
||||
|
||||
struct user_details {
|
||||
uid_t uid;
|
||||
uid_t euid;
|
||||
uid_t gid;
|
||||
uid_t egid;
|
||||
const char *username;
|
||||
const char *cwd;
|
||||
const char *tty;
|
||||
const char *host;
|
||||
GETGROUPS_T *groups;
|
||||
int ngroups;
|
||||
};
|
||||
|
||||
#define CD_SET_UID 0x0001
|
||||
#define CD_SET_EUID 0x0002
|
||||
#define CD_SET_GID 0x0004
|
||||
#define CD_SET_EGID 0x0008
|
||||
#define CD_PRESERVE_GROUPS 0x0010
|
||||
#define CD_NOEXEC 0x0020
|
||||
#define CD_SET_PRIORITY 0x0040
|
||||
#define CD_SET_UMASK 0x0080
|
||||
#define CD_SET_TIMEOUT 0x0100
|
||||
|
||||
struct command_details {
|
||||
uid_t uid;
|
||||
uid_t euid;
|
||||
gid_t gid;
|
||||
gid_t egid;
|
||||
mode_t umask;
|
||||
int flags;
|
||||
int priority;
|
||||
int timeout;
|
||||
int ngroups;
|
||||
GETGROUPS_T *groups;
|
||||
const char *command;
|
||||
const char *cwd;
|
||||
const char *login_class;
|
||||
const char *chroot;
|
||||
const char *selinux_role;
|
||||
const char *selinux_type;
|
||||
};
|
||||
|
||||
/* Status passed between parent and child via socketpair */
|
||||
struct command_status {
|
||||
#define CMD_INVALID 0
|
||||
#define CMD_ERRNO 1
|
||||
#define CMD_WSTATUS 2
|
||||
#define CMD_SIGNO 3
|
||||
int type;
|
||||
int val;
|
||||
};
|
||||
|
||||
/* For error() and errorx() (XXX - needed?) */
|
||||
void cleanup(int);
|
||||
|
||||
/* tgetpass.c */
|
||||
char *tgetpass(const char *, int, int);
|
||||
int tty_present(void);
|
||||
|
||||
/* zero_bytes.c */
|
||||
void zero_bytes(volatile void *, size_t);
|
||||
|
||||
/* fileops.c */
|
||||
int lock_file(int, int);
|
||||
char *sudo_parseln(FILE *);
|
||||
|
||||
/* script.c */
|
||||
int script_duplow(int);
|
||||
int script_execve(struct command_details *details, char *argv[], char *envp[],
|
||||
struct command_status *cstat);
|
||||
void script_setup(uid_t);
|
||||
|
||||
/* term.c */
|
||||
int term_cbreak(int);
|
||||
int term_copy(int, int, int);
|
||||
int term_noecho(int);
|
||||
int term_raw(int, int, int);
|
||||
int term_restore(int, int);
|
||||
|
||||
/* fmt_string.h */
|
||||
char *fmt_string(const char *var, const char *value);
|
||||
|
||||
/* atobool.c */
|
||||
int atobool(const char *str);
|
||||
|
||||
/* parse_args.c */
|
||||
int parse_args(int argc, char **argv, int *nargc, char ***nargv,
|
||||
char ***settingsp, char ***env_addp);
|
||||
|
||||
/* pty.c */
|
||||
int get_pty(int *master, int *slave, char *name, size_t namesz, uid_t uid);
|
||||
|
||||
/* sudo.c */
|
||||
int exec_setup(struct command_details *details);
|
||||
extern int debug_level;
|
||||
extern struct plugin_container_list io_plugins;
|
||||
|
||||
#ifndef errno
|
||||
extern int errno;
|
||||
#endif
|
||||
|
||||
#ifdef ntoyet
|
||||
/*
|
||||
* Sudo logging/debugging, printf-style.
|
||||
* XXX - not hooked up yet
|
||||
* The debug level may be set on the command line via the -D flag.
|
||||
* A higher debug level yields more verbose debugging.
|
||||
*/
|
||||
#define SUDO_LOG_DEBUG1 1
|
||||
#define SUDO_LOG_DEBUG2 2
|
||||
#define SUDO_LOG_DEBUG3 3
|
||||
#define SUDO_LOG_DEBUG4 4
|
||||
#define SUDO_LOG_DEBUG5 5
|
||||
#define SUDO_LOG_DEBUG6 6
|
||||
#define SUDO_LOG_DEBUG7 7
|
||||
#define SUDO_LOG_DEBUG8 8
|
||||
#define SUDO_LOG_DEBUG9 9
|
||||
#define SUDO_LOG_INFO 10
|
||||
#define SUDO_LOG_WARN 11
|
||||
#define SUDO_LOG_ERROR 12
|
||||
void sudo_log(int level, const char *format, ...) __printflike(2, 3);
|
||||
#endif
|
||||
|
||||
#endif /* _SUDO_SUDO_H */
|
39
src/sudo_plugin_int.h
Normal file
39
src/sudo_plugin_int.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#ifndef _SUDO_PLUGIN_INT_H
|
||||
#define _SUDO_PLUGIN_INT_H
|
||||
|
||||
/*
|
||||
* Sudo plugin internals.
|
||||
*/
|
||||
|
||||
struct plugin_info {
|
||||
struct plugin_info *prev; /* required */
|
||||
struct plugin_info *next; /* required */
|
||||
const char *path;
|
||||
const char *symbol_name;
|
||||
};
|
||||
TQ_DECLARE(plugin_info)
|
||||
|
||||
struct plugin_container {
|
||||
struct plugin_container *prev; /* required */
|
||||
struct plugin_container *next; /* required */
|
||||
const char *name;
|
||||
void *handle;
|
||||
union {
|
||||
struct generic_plugin *generic;
|
||||
struct policy_plugin *policy;
|
||||
struct io_plugin *io;
|
||||
} u;
|
||||
};
|
||||
TQ_DECLARE(plugin_container)
|
||||
|
||||
extern struct plugin_container_list policy_plugins;
|
||||
extern struct plugin_container_list io_plugins;
|
||||
|
||||
int sudo_conversation(int num_msgs, const struct sudo_conv_message msgs[],
|
||||
struct sudo_conv_reply replies[]);
|
||||
|
||||
void sudo_load_plugins(const char *conf_file,
|
||||
struct plugin_container *policy_plugin,
|
||||
struct plugin_container_list *io_plugins);
|
||||
|
||||
#endif /* _SUDO_PLUGIN_INT_H */
|
32
src/sudo_usage.h.in
Normal file
32
src/sudo_usage.h.in
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2007-2009 Todd C. Miller <Todd.Miller@courtesan.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _SUDO_USAGE_H
|
||||
#define _SUDO_USAGE_H
|
||||
|
||||
/*
|
||||
* Usage strings for sudo. These are here because we
|
||||
* need to be able to substitute values from configure.
|
||||
*/
|
||||
#define SUDO_USAGE1 " -h | -K | -k | -L | -V"
|
||||
#define SUDO_USAGE2 " -v [-AknS] @BSDAUTH_USAGE@[-g groupname|#gid] [-p prompt] [-u user name|#uid]"
|
||||
#define SUDO_USAGE3 " -l[l] [-AknS] @BSDAUTH_USAGE@[-g groupname|#gid] [-p prompt] [-U user name] [-u user name|#uid] [-g groupname|#gid] [command]"
|
||||
#define SUDO_USAGE4 " [-AbEHknPS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C fd] @LOGINCAP_USAGE@[-g groupname|#gid] [-p prompt] [-u user name|#uid] [-g groupname|#gid] [VAR=value] [-i|-s] [<command>]"
|
||||
#define SUDO_USAGE5 " -e [-AknS] @BSDAUTH_USAGE@@SELINUX_USAGE@[-C fd] @LOGINCAP_USAGE@[-g groupname|#gid] [-p prompt] [-u user name|#uid] file ..."
|
||||
|
||||
#endif /* _SUDO_USAGE_H */
|
@@ -56,6 +56,9 @@
|
||||
|
||||
#include "sudo.h"
|
||||
|
||||
/* XXX */
|
||||
char *user_askpass = NULL;
|
||||
|
||||
static volatile sig_atomic_t signo;
|
||||
|
||||
static void handler __P((int));
|
||||
@@ -111,7 +114,7 @@ restart:
|
||||
(void) sigaction(SIGTTIN, &sa, &savettin);
|
||||
(void) sigaction(SIGTTOU, &sa, &savettou);
|
||||
|
||||
if (def_pwfeedback)
|
||||
if (ISSET(flags, TGP_FEEDBACK))
|
||||
neednl = term_cbreak(input);
|
||||
else
|
||||
neednl = term_noecho(input);
|
||||
@@ -123,7 +126,7 @@ restart:
|
||||
|
||||
if (timeout > 0)
|
||||
alarm(timeout);
|
||||
pass = getln(input, buf, sizeof(buf), def_pwfeedback);
|
||||
pass = getln(input, buf, sizeof(buf), ISSET(flags, TGP_FEEDBACK));
|
||||
alarm(0);
|
||||
save_errno = errno;
|
||||
|
||||
@@ -184,7 +187,8 @@ sudo_askpass(prompt)
|
||||
if (pid == 0) {
|
||||
/* child, point stdout to output side of the pipe and exec askpass */
|
||||
(void) dup2(pfd[1], STDOUT_FILENO);
|
||||
set_perms(PERM_FULL_USER);
|
||||
//XXX - set real and effective uid to user
|
||||
//set_perms(PERM_FULL_USER);
|
||||
closefrom(STDERR_FILENO + 1);
|
||||
execl(user_askpass, user_askpass, prompt, (char *)NULL);
|
||||
warning("unable to run %s", user_askpass);
|
||||
|
Reference in New Issue
Block a user