validate package metadata after loading

alpm has certain requirements for package metadata necessary for proper
functioning, name and version in particular.  These requirements are
already enforced in makepkg, but nowhere in alpm.

Exceptions are treated as errors for non-local packages because they
cannot be installed without potentially resulting in undefined behavior.
Exceptions for local packages are treated as warnings because they are
already installed, so any damage has already been done, and the user
would otherwise have no way to uninstall them.

Signed-off-by: Andrew Gregory <andrew.gregory.8@gmail.com>
This commit is contained in:
Andrew Gregory 2024-02-04 11:47:18 -08:00 committed by Allan McRae
parent fde59b99e8
commit 0a394144b2
9 changed files with 101 additions and 0 deletions

View file

@ -630,6 +630,10 @@ static int local_db_populate(alpm_db_t *db)
continue; continue;
} }
/* treat local metadata errors as warning-only,
* they are already installed and otherwise they can't be operated on */
_alpm_pkg_check_meta(pkg);
/* add to the collection */ /* add to the collection */
_alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n", _alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
pkg->name, db->treename); pkg->name, db->treename);

View file

@ -673,6 +673,10 @@ alpm_pkg_t *_alpm_pkg_load_internal(alpm_handle_t *handle,
newpkg->infolevel |= INFRQ_FILES; newpkg->infolevel |= INFRQ_FILES;
} }
if(_alpm_pkg_check_meta(newpkg) != 0) {
goto pkg_invalid;
}
_alpm_archive_read_free(archive); _alpm_archive_read_free(archive);
close(fd); close(fd);
return newpkg; return newpkg;

View file

@ -344,6 +344,11 @@ static alpm_pkg_t *load_pkg_for_entry(alpm_db_t *db, const char *entryname,
pkg->ops = get_sync_pkg_ops(); pkg->ops = get_sync_pkg_ops();
pkg->handle = db->handle; pkg->handle = db->handle;
if(_alpm_pkg_check_meta(pkg) != 0) {
_alpm_pkg_free(pkg);
RET_ERR(db->handle, ALPM_ERR_PKG_INVALID, NULL);
}
/* add to the collection */ /* add to the collection */
_alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n", _alpm_log(db->handle, ALPM_LOG_FUNCTION, "adding '%s' to package cache for db '%s'\n",
pkg->name, db->treename); pkg->name, db->treename);

View file

@ -21,6 +21,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <limits.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
@ -844,3 +845,58 @@ int SYMEXPORT alpm_pkg_should_ignore(alpm_handle_t *handle, alpm_pkg_t *pkg)
return 0; return 0;
} }
/* check that package metadata meets our requirements */
int _alpm_pkg_check_meta(alpm_pkg_t *pkg)
{
char *c;
int error_found = 0;
#define EPKGMETA(error) do { \
error_found = -1; \
_alpm_log(pkg->handle, ALPM_LOG_ERROR, error, pkg->name, pkg->version); \
} while(0)
/* sanity check */
if(pkg->handle == NULL) {
return -1;
}
/* immediate bail if package doesn't have name or version */
if(pkg->name == NULL || pkg->name[0] == '\0'
|| pkg->version == NULL || pkg->version[0] == '\0') {
_alpm_log(pkg->handle, ALPM_LOG_ERROR,
_("invalid package metadata (name or version missing)"));
return -1;
}
if(pkg->name[0] == '-' || pkg->name[0] == '.') {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package name cannot start with '.' or '-')\n"));
}
if(_alpm_fnmatch(pkg->name, "[![:alnum:]+_.@-]") == 0) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package name contains invalid characters)\n"));
}
/* multiple '-' in pkgver can cause local db entries for different packages
* to overlap (e.g. foo-1=2-3 and foo=1-2-3 both give foo-1-2-3) */
if((c = strchr(pkg->version, '-')) && (strchr(c + 1, '-'))) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package version contains invalid characters)\n"));
}
if(strchr(pkg->version, '/')) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package version contains invalid characters)\n"));
}
/* local db entry is <pkgname>-<pkgver> */
if(strlen(pkg->name) + strlen(pkg->version) + 1 > NAME_MAX) {
EPKGMETA(_("invalid metadata for package %s-%s "
"(package name and version too long)\n"));
}
#undef EPKGMETA
return error_found;
}

View file

@ -165,4 +165,6 @@ int _alpm_pkg_compare_versions(alpm_pkg_t *local_pkg, alpm_pkg_t *pkg);
alpm_pkg_xdata_t *_alpm_pkg_parse_xdata(const char *string); alpm_pkg_xdata_t *_alpm_pkg_parse_xdata(const char *string);
void _alpm_pkg_xdata_free(alpm_pkg_xdata_t *pd); void _alpm_pkg_xdata_free(alpm_pkg_xdata_t *pd);
int _alpm_pkg_check_meta(alpm_pkg_t *pkg);
#endif /* ALPM_PACKAGE_H */ #endif /* ALPM_PACKAGE_H */

View file

@ -97,6 +97,9 @@ pacman_tests = [
'tests/pacman003.py', 'tests/pacman003.py',
'tests/pacman004.py', 'tests/pacman004.py',
'tests/pacman005.py', 'tests/pacman005.py',
'tests/pkg-meta-invalid-name-file.py',
'tests/pkg-meta-invalid-name-local.py',
'tests/pkg-meta-invalid-name-sync.py',
'tests/provision001.py', 'tests/provision001.py',
'tests/provision002.py', 'tests/provision002.py',
'tests/provision003.py', 'tests/provision003.py',

View file

@ -0,0 +1,9 @@
self.description = "package name with invalid characters cannot be installed (file)"
p = pmpkg("-foo")
self.addpkg(p)
self.args = "-U -- %s" % p.filename()
self.addrule("!PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=-foo")

View file

@ -0,0 +1,9 @@
self.description = "local package name with invalid characters can be removed"
sp = pmpkg("-foo")
self.addpkg2db("local", sp)
self.args = "-R -- %s" % sp.name
self.addrule("PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=-foo")

View file

@ -0,0 +1,9 @@
self.description = "package name with invalid characters cannot be installed"
sp = pmpkg("-foo")
self.addpkg2db("sync", sp)
self.args = "-S -- %s" % sp.name
self.addrule("!PACMAN_RETCODE=0")
self.addrule("!PKG_EXIST=-foo")