compat/regress/glob/files
compat/regress/glob/globtest.c
compat/regress/glob/globtest.in
+compat/secure_path.c
compat/setenv.c
compat/siglist.in
compat/snprintf.c
include/lbuf.h
include/list.h
include/missing.h
+include/secure_path.h
include/sudo_conf.h
include/sudo_debug.h
include/sudo_plugin.h
SHELL = @SHELL@
-LTOBJS = alloc.lo atobool.lo fileops.lo fmt_string.lo lbuf.lo \
- list.lo setgroups.lo sudo_conf.lo sudo_debug.lo term.lo \
+LTOBJS = alloc.lo atobool.lo fileops.lo fmt_string.lo lbuf.lo list.lo \
+ secure_path.lo setgroups.lo sudo_conf.lo sudo_debug.lo term.lo \
zero_bytes.lo @COMMON_OBJS@
all: libcommon.la
list.lo: $(srcdir)/list.c $(top_builddir)/config.h $(incdir)/missing.h \
$(incdir)/list.h $(incdir)/error.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/list.c
+secure_path.lo: $(srcdir)/secure_path.c $(top_builddir)/config.h \
+ $(incdir)/missing.h $(incdir)/sudo_debug.h \
+ $(incdir)/secure_path.h
+ $(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/secure_path.c
setgroups.lo: $(srcdir)/setgroups.c $(top_builddir)/config.h \
$(incdir)/missing.h $(incdir)/sudo_debug.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/setgroups.c
$(top_srcdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/alloc.h $(incdir)/error.h $(incdir)/fileops.h \
$(top_builddir)/pathnames.h $(incdir)/sudo_plugin.h \
- $(incdir)/sudo_conf.h $(incdir)/list.h $(incdir)/sudo_debug.h
+ $(incdir)/sudo_conf.h $(incdir)/list.h $(incdir)/sudo_debug.h \
+ $(incdir)/secure_path.h $(incdir)/gettext.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/sudo_conf.c
sudo_debug.lo: $(srcdir)/sudo_debug.c $(top_builddir)/config.h \
$(top_srcdir)/compat/stdbool.h $(incdir)/missing.h \
--- /dev/null
+/*
+ * Copyright (c) 2012 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/stat.h>
+#include <sys/param.h>
+#include <stdio.h>
+#ifdef HAVE_STRING_H
+# include <string.h>
+#endif /* HAVE_STRING_H */
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif /* HAVE_STRINGS_H */
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <errno.h>
+
+#include "missing.h"
+#include "sudo_debug.h"
+#include "secure_path.h"
+
+/*
+ * Verify that path is a regular file and not writable by other users.
+ */
+int
+sudo_secure_path(const char *path, uid_t uid, gid_t gid, struct stat *sbp)
+{
+ struct stat sb;
+ int rval = SUDO_PATH_MISSING;
+ debug_decl(sudo_secure_path, SUDO_DEBUG_UTIL)
+
+ if (path != NULL && stat_sudoers(path, &sb) == 0) {
+ if (!S_ISREG(sb.st_mode)) {
+ rval = SUDO_PATH_BAD_TYPE;
+ } else if (uid != (uid_t)-1 && sb.st_uid != uid) {
+ rval = SUDO_PATH_WRONG_OWNER;
+ } else if (sb.st_mode & S_IWOTH) {
+ rval = SUDO_PATH_WORLD_WRITABLE;
+ } else if (ISSET(sb.st_mode, S_IWGRP) &&
+ (gid == (gid_t)-1 || sb.st_gid != gid)) {
+ rval = SUDO_PATH_GROUP_WRITABLE;
+ } else {
+ rval = SUDO_PATH_SECURE;
+ }
+ if (sbp)
+ (void) memcpy(sbp, &sb, sizeof(struct stat));
+ }
+
+ debug_return_int(rval);
+}
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <ctype.h>
+#include <errno.h>
#define SUDO_ERROR_WRAP 0
#include "sudo_plugin.h"
#include "sudo_conf.h"
#include "sudo_debug.h"
+#include "secure_path.h"
+#include "gettext.h"
+
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
#ifndef _PATH_SUDO_ASKPASS
# define _PATH_SUDO_ASKPASS NULL
}
/*
- * Reads in /etc/sudo.conf
- * Returns a list of plugins.
+ * Reads in /etc/sudo.conf and populates sudo_conf_data.
*/
void
sudo_conf_read(void)
{
struct sudo_conf_table *cur;
struct plugin_info *info;
+ struct stat sb;
FILE *fp;
char *cp;
- if ((fp = fopen(_PATH_SUDO_CONF, "r")) == NULL)
+ switch (sudo_secure_path(_PATH_SUDO_CONF, ROOT_UID, -1, &sb)) {
+ case SUDO_PATH_SECURE:
+ break;
+ case SUDO_PATH_MISSING:
+ /* Root should always be able to read sudo.conf. */
+ if (errno != ENOENT && geteuid() == ROOT_UID)
+ warning(_("unable to stat %s"), _PATH_SUDO_CONF);
+ goto done;
+ case SUDO_PATH_BAD_TYPE:
+ warningx(_("%s is not a regular file"), _PATH_SUDO_CONF);
+ goto done;
+ case SUDO_PATH_WRONG_OWNER:
+ warningx(_("%s is owned by uid %u, should be %u"),
+ _PATH_SUDO_CONF, (unsigned int) sb.st_uid, ROOT_UID);
+ goto done;
+ case SUDO_PATH_WORLD_WRITABLE:
+ warningx(_("%s is world writable"), _PATH_SUDO_CONF);
+ goto done;
+ case SUDO_PATH_GROUP_WRITABLE:
+ warningx(_("%s is group writable"), _PATH_SUDO_CONF);
+ goto done;
+ default:
+ /* NOTREACHED */
+ goto done;
+ }
+
+ if ((fp = fopen(_PATH_SUDO_CONF, "r")) == NULL) {
+ if (errno != ENOENT && geteuid() == ROOT_UID)
+ warning(_("unable to open %s"), _PATH_SUDO_CONF);
goto done;
+ }
while ((cp = sudo_parseln(fp)) != NULL) {
/* Skip blank or comment lines */
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+#ifndef _SUDO_SECURE_PATH_H
+#define _SUDO_SECURE_PATH_H
+
+#define SUDO_PATH_SECURE 0
+#define SUDO_PATH_MISSING -1
+#define SUDO_PATH_BAD_TYPE -2
+#define SUDO_PATH_WRONG_OWNER -3
+#define SUDO_PATH_WORLD_WRITABLE -4
+#define SUDO_PATH_GROUP_WRITABLE -5
+
+int sudo_secure_path(const char *path, uid_t uid, gid_t gid, struct stat *sbp);
+
+#endif /* _SUDO_SECURE_PATH_H */
$(srcdir)/defaults.h $(devdir)/def_data.h $(srcdir)/logging.h \
$(srcdir)/sudo_nss.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_debug.h $(incdir)/gettext.h $(srcdir)/interfaces.h \
- $(srcdir)/sudoers_version.h $(srcdir)/auth/sudo_auth.h
+ $(srcdir)/sudoers_version.h $(srcdir)/auth/sudo_auth.h \
+ $(incdir)/secure_path.h
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEFS) $(srcdir)/sudoers.c
sudoreplay.o: $(srcdir)/sudoreplay.c $(top_builddir)/config.h \
$(top_srcdir)/compat/timespec.h $(top_srcdir)/compat/stdbool.h \
#include "interfaces.h"
#include "sudoers_version.h"
#include "auth/sudo_auth.h"
+#include "secure_path.h"
/*
* Prototypes
FILE *
open_sudoers(const char *sudoers, bool doedit, bool *keepopen)
{
- struct stat statbuf;
+ struct stat sb;
FILE *fp = NULL;
- int rootstat;
debug_decl(open_sudoers, SUDO_DEBUG_PLUGIN)
- /*
- * Fix the mode and group on sudoers file from old default.
- * Only works if file system is readable/writable by root.
- */
- if ((rootstat = stat_sudoers(sudoers, &statbuf)) == 0 &&
- sudoers_uid == statbuf.st_uid && sudoers_mode != 0400 &&
- (statbuf.st_mode & 0007777) == 0400) {
-
- if (chmod(sudoers, sudoers_mode) == 0) {
- warningx(_("fixed mode on %s"), sudoers);
- SET(statbuf.st_mode, sudoers_mode);
- if (statbuf.st_gid != sudoers_gid) {
- if (chown(sudoers, (uid_t) -1, sudoers_gid) == 0) {
- warningx(_("set group on %s"), sudoers);
- statbuf.st_gid = sudoers_gid;
- } else
- warning(_("unable to set group on %s"), sudoers);
- }
- } else
- warning(_("unable to fix mode on %s"), sudoers);
- }
-
- /*
- * Sanity checks on sudoers file. Must be done as sudoers
- * file owner. We already did a stat as root, so use that
- * data if we can't stat as sudoers file owner.
- */
set_perms(PERM_SUDOERS);
- if (rootstat != 0 && stat_sudoers(sudoers, &statbuf) != 0)
- log_error(USE_ERRNO|NO_EXIT, _("unable to stat %s"), sudoers);
- else if (!S_ISREG(statbuf.st_mode))
- log_error(NO_EXIT, _("%s is not a regular file"), sudoers);
- else if ((statbuf.st_mode & 07577) != (sudoers_mode & 07577))
- log_error(NO_EXIT, _("%s is mode 0%o, should be 0%o"), sudoers,
- (unsigned int) (statbuf.st_mode & 07777),
- (unsigned int) sudoers_mode);
- else if (statbuf.st_uid != sudoers_uid)
- log_error(NO_EXIT, _("%s is owned by uid %u, should be %u"), sudoers,
- (unsigned int) statbuf.st_uid, (unsigned int) sudoers_uid);
- else if (statbuf.st_gid != sudoers_gid && ISSET(statbuf.st_mode, S_IRGRP|S_IWGRP))
- log_error(NO_EXIT, _("%s is owned by gid %u, should be %u"), sudoers,
- (unsigned int) statbuf.st_gid, (unsigned int) sudoers_gid);
- else if ((fp = fopen(sudoers, "r")) == NULL)
- log_error(USE_ERRNO|NO_EXIT, _("unable to open %s"), sudoers);
- else {
- /*
- * Make sure we can actually read sudoers so we can present the
- * user with a reasonable error message (unlike the lexer).
- */
- if (statbuf.st_size != 0 && fgetc(fp) == EOF) {
- log_error(USE_ERRNO|NO_EXIT, _("unable to read %s"), sudoers);
- fclose(fp);
- fp = NULL;
- }
- }
-
- if (fp != NULL) {
- rewind(fp);
- (void) fcntl(fileno(fp), F_SETFD, 1);
+ switch (sudo_secure_path(sudoers, sudoers_uid, sudoers_gid, &sb)) {
+ case SUDO_PATH_SECURE:
+ if ((fp = fopen(sudoers, "r")) == NULL) {
+ log_error(USE_ERRNO|NO_EXIT, _("unable to open %s"), sudoers);
+ } else {
+ /*
+ * Make sure we can actually read sudoers so we can present the
+ * user with a reasonable error message (unlike the lexer).
+ */
+ if (sb.st_size != 0 && fgetc(fp) == EOF) {
+ log_error(USE_ERRNO|NO_EXIT, _("unable to read %s"),
+ sudoers);
+ fclose(fp);
+ fp = NULL;
+ } else {
+ /* Rewind fp and set close on exec flag. */
+ rewind(fp);
+ (void) fcntl(fileno(fp), F_SETFD, 1);
+ }
+ }
+ break;
+ case SUDO_PATH_MISSING:
+ log_error(USE_ERRNO|NO_EXIT, _("unable to stat %s"), sudoers);
+ break;
+ case SUDO_PATH_BAD_TYPE:
+ log_error(NO_EXIT, _("%s is not a regular file"), sudoers);
+ break;
+ case SUDO_PATH_WRONG_OWNER:
+ log_error(NO_EXIT, _("%s is owned by uid %u, should be %u"),
+ sudoers, (unsigned int) sb.st_uid, (unsigned int) sudoers_uid);
+ break;
+ case SUDO_PATH_WORLD_WRITABLE:
+ log_error(NO_EXIT, _("%s is world writable"), sudoers);
+ break;
+ case SUDO_PATH_GROUP_WRITABLE:
+ log_error(NO_EXIT, _("%s is owned by gid %u, should be %u"),
+ sudoers, (unsigned int) sb.st_gid, (unsigned int) sudoers_gid);
+ break;
+ default:
+ /* NOTREACHED */
+ break;
}
restore_perms(); /* change back to root */
+
debug_return_ptr(fp);
}