Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Rewrite pkgdb_access to actually test what we're interested in:
Matthew Seaman committed 13 years ago
commit 829553beb9ae4b2aec154cc9111b4dcae4cf6bef
parent 472b468
2 files changed +155 -65
modified libpkg/pkg.h.in
@@ -805,12 +805,16 @@ int pkg_finish_repo(char *path, pem_password_cb *cb, char *rsa_key_path);

/**
 * Test if the EUID has sufficient privilege to carry out some
-
 * operation using R_OK, W_OK, F_OK as for eaccess() on the databases
-
 * indicated in the database bitmap.
+
 * operation (mode is a bitmap indicating READ, WRITE, CREATE) on the
+
 * databases indicated in the database bitmap.
 */
-
#define ACCESS_LOCAL	0x1
-
#define ACCESS_REPO	0x2
-
int pkgdb_access(int mode, unsigned database);
+
#define PKGDB_MODE_READ		(0x1<<0)
+
#define PKGDB_MODE_WRITE	(0x1<<1)
+
#define PKGDB_MODE_CREATE	(0x1<<2)
+

+
#define PKGDB_DB_LOCAL		(0x1<<0)
+
#define PKGDB_DB_REPO		(0x1<<1)
+
int pkgdb_access(unsigned mode, unsigned database);

/**
 * Open the local package database.
modified libpkg/pkgdb.c
@@ -697,97 +697,183 @@ pkgdb_open_multirepos(const char *dbdir, struct pkgdb *db)
	return (EPKG_OK);
}

-
int
-
pkgdb_access(int mode)
+
static int
+
file_mode_insecure(const char *path, bool install_as_user)
{
-
	const char	*dbdir;
-
	char		 localpath[MAXPATHLEN + 1];
+
	uid_t		euid;
+
	gid_t		egid;
+
	struct stat	sb;
+

+
	if (install_as_user) {
+
		euid = geteuid();
+
		egid = getegid();
+
	} else {
+
		euid = 0;
+
		egid = 0;
+
	}
+

+
	if (stat(path, &sb) != 0) {
+
		if (errno == EACCES)
+
			return (EPKG_ENOACCESS);
+
		else if (errno == ENOENT)
+
			return (EPKG_ENODB);
+
		else 
+
			return (EPKG_FATAL);
+
	}
+

+
	/* if euid == 0, require no group or other read access.  if
+
	   euid != 0, require no other read access and group read
+
	   access IFF the group ownership == egid */
+

+
	if ( (sb.st_mode & S_IWOTH) != 0  ||
+
	     ((euid == 0 || sb.st_gid != egid) &&
+
	      (sb.st_mode & S_IWGRP) != 0)) {
+
		pkg_emit_error("%s: permissions (%#o) too lax", path,
+
			       (sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)));
+
		return (EPKG_INSECURE);
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
database_access(unsigned mode, const char* dbdir, const char *dbname)
+
{
+
	char		 dbpath[MAXPATHLEN + 1];
	int		 access;
+
	int		 retval;
+
	bool		 database_exists;
	bool		 install_as_user;
-
	bool		 i_am_super;
-
	bool		 local_database_exists;

+
	if (dbname != NULL)
+
		snprintf(dbpath, sizeof(dbpath), "%s/%s.sqlite", dbdir,
+
			 dbname);
+
	else
+
		strlcpy(dbpath, dbname, sizeof(dbpath));
+

+
	install_as_user = (getenv("INSTALL_AS_USER") != NULL);
+

+
	retval = file_mode_insecure(dbpath, install_as_user);
+

+
	database_exists = (retval != EPKG_ENODB);
+

+
	if (database_exists && retval != EPKG_OK)
+
		return (retval);
+

+
	if (!database_exists && (mode & PKGDB_MODE_CREATE) != 0)
+
		return (EPKG_OK);
+

+
	switch(mode & (PKGDB_MODE_READ|PKGDB_MODE_WRITE)) {
+
	case 0:		/* Existence test */
+
		access = eaccess(dbpath, F_OK);
+
		break;
+
	case PKGDB_MODE_READ:
+
		access = eaccess(dbpath, R_OK);
+
		break;
+
	case PKGDB_MODE_WRITE:
+
		access = eaccess(dbpath, W_OK);
+
		break;
+
	case PKGDB_MODE_READ|PKGDB_MODE_WRITE:
+
		access = eaccess(dbpath, R_OK|W_OK);
+
		break;
+
	}
+

+
	if (access != 0) {
+
		if (errno == ENOENT)
+
			return (EPKG_ENODB);
+
		else if (errno == EACCES)
+
			return (EPKG_ENOACCESS);
+
		else
+
			return (EPKG_FATAL);
+
	}
+
 
+
	return (EPKG_OK);
+
}
+

+

+
int
+
pkgdb_access(unsigned mode, unsigned database)
+
{
+
	bool		 multirepos_enabled;
+
	const char	*dbdir;
+
	int		 retval = EPKG_OK;

	/*
	 * This will return one of:
	 *
-
	 * EPKG_NODB:  local.sqlite doesn't exist (we don't want to create
-
	 *             it.)
+
	 * EPKG_NODB:  a database doesn't exist and we don't want to create
+
	 *             it, or dbdir doesn't exist
+
	 * 
+
	 * EPKG_INSECURE: the dbfile or one of the directories in the
+
	 *	       path to it are writable by other than root or
+
	 *             (if $INSTALL_AS_USER is set) the current euid
+
	 *             and egid
	 *
-
	 * EPKG_ENOACCESS: we don't have privileges to read or write packages
+
	 * EPKG_ENOACCESS: we don't have privileges to read or write a
	 *
	 * EPKG_FATAL: Couldn't determine the answer for other reason,
-
	 *     like configuration screwed up, read-only filesystem, etc.
+
	 *     like configuration screwed up, invalid argument values,
+
	 *     read-only filesystem, etc.
	 *
	 * EPKG_OK: We can go ahead
	 */
-
 
+

	if (pkg_config_string(PKG_CONFIG_DBDIR, &dbdir) != EPKG_OK)
		return (EPKG_FATAL); /* Config borked */

-
	snprintf(localpath, sizeof(localpath), "%s/local.sqlite", dbdir);
-

-
	/* We only care about F_OK, W_OK and R_OK. Not X_OK */
+
	pkg_config_bool(PKG_CONFIG_MULTIREPOS, &multirepos_enabled);

-
	if ((mode & ~(R_OK|W_OK)) != 0)
+
	if ((mode & ~(PKGDB_MODE_READ|PKGDB_MODE_WRITE|PKGDB_MODE_CREATE))
+
	    != 0)
		return (EPKG_FATAL); /* EINVAL */

-
	/* Does the database even exist? */
+
	if ((database & ~(PKGDB_DB_LOCAL|PKGDB_DB_REPO)) != 0)
+
		return (EPKG_FATAL); /* EINVAL */

-
	local_database_exists = (eaccess(localpath, F_OK) == 0);
+
	/* Test the enclosing directory: if we're going to create the
+
	   DB, then we need read and write permissions on the dir.
+
	   Otherwise, just test for read access */

-
	if (mode == F_OK && !local_database_exists) {
-
		pkg_emit_nolocaldb();
-
		return (EPKG_ENODB);
-
	}
+
	if ((mode & PKGDB_MODE_CREATE) != 0) 
+
		retval = database_access(PKGDB_MODE_READ|PKGDB_MODE_WRITE,
+
					 dbdir, NULL);
+
	else
+
		retval = database_access(PKGDB_MODE_READ, dbdir, NULL);
+
	if (retval != EPKG_OK)
+
		return (retval);

-
	/* Subtly different: do we have read access to the DB?  
-
	   mode == (R_OK|W_OK) implies we don't want to create the DB */
+
	/* Test local.sqlite, if required */

-
	if ((mode & R_OK) == R_OK &&
-
	    !(local_database_exists && eaccess(localpath, R_OK) != 0)) {
-
		if (errno == ENOTDIR || errno == ENOENT) {
-
			pkg_emit_nolocaldb();
-
			return (EPKG_ENODB);
-
		} else if (errno == EACCES)
-
			return (EPKG_ENOACCESS);
-
		else
-
			return (EPKG_FATAL);
+
	if ((database & PKGDB_DB_LOCAL) != 0) {
+
		retval = database_access(mode, dbdir, "local");
+
		if (retval != EPKG_OK)
+
			return (retval);
	}

-
	/* If we aren't being asked about write permissions, then
-
	   we're done. */
-

-
	if ((mode & W_OK) == 0)
-
		return (EPKG_OK);
-

-
	/* Are we too plebean? Only root is allowed, unless
-
	   $INSTALL_AS_USER is set in the environment.  */
+
	/* Test repo.sqlite if required, or test all the repo DBs 
+
	   if in multirepos_enabled mode */

-
	i_am_super = (geteuid() == 0);
-
	install_as_user = (getenv("INSTALL_AS_USER") != NULL);
+
	if (!multirepos_enabled && (database & PKGDB_DB_REPO) != 0) {
+
		retval = database_access(mode, dbdir, "repo");
+
		if (retval != EPKG_OK)
+
			return (retval);
+
	}

-
	if (!i_am_super && !install_as_user)
-
		return (EPKG_ENOACCESS);
+
	if (multirepos_enabled && (database & PKGDB_DB_REPO) != 0) {
+
		struct pkg_config_kv	*all_repos = NULL;
+
		const char		*reponame;

-
	/* We want to write to the DB and/or the system generally to
-
	   manipulate packages.  If the local database doesn't exist,
-
	   we will create it -- so test write access to the containing
-
	   directory in that case. */
+
		while (pkg_config_kvlist(PKG_CONFIG_REPOS, &all_repos) ==
+
		       EPKG_OK) {
+
			reponame = pkg_config_kv_get(all_repos,
+
					 PKG_CONFIG_KV_KEY);

-
	if (local_database_exists)
-
		access = eaccess(localpath, W_OK);
-
	else
-
		access = eaccess(dbdir, W_OK);
-

-
	if (access != 0) {
-
		if (errno == EACCES)
-
			return (EPKG_ENOACCESS);
-
		else
-
			return (EPKG_FATAL);
+
			retval = database_access(mode, dbdir, reponame);
+
			if (retval != EPKG_OK)
+
				return (retval);
+
		}
	}
-
 
-
	return (EPKG_OK);
+
	return (retval);
}

int