diff options
Diffstat (limited to 'src/kmk/kmkbuiltin/kbuild_protection.c')
-rw-r--r-- | src/kmk/kmkbuiltin/kbuild_protection.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/src/kmk/kmkbuiltin/kbuild_protection.c b/src/kmk/kmkbuiltin/kbuild_protection.c new file mode 100644 index 0000000..373fd88 --- /dev/null +++ b/src/kmk/kmkbuiltin/kbuild_protection.c @@ -0,0 +1,376 @@ +/* $Id: kbuild_protection.c 3192 2018-03-26 20:25:56Z bird $ */ +/** @file + * Simple File Protection. + */ + +/* + * Copyright (c) 2008-2010 knut st. osmundsen <bird-kBuild-spamx@anduin.net> + * + * This file is part of kBuild. + * + * kBuild is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * kBuild is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with kBuild. If not, see <http://www.gnu.org/licenses/> + * + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include "config.h" +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <assert.h> +#if defined(_MSC_VER) || defined(__OS2__) +# include <limits.h> +# include <direct.h> +#else +# include <unistd.h> +#endif +#include "kbuild_protection.h" +#include "err.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ +#define KBUILD_PROTECTION_MAGIC 0x00111100 + +#if defined(__EMX__) || defined(_MSC_VER) +# define IS_SLASH(ch) ( (ch) == '/' || (ch) == '\\' ) +# define DEFAULT_PROTECTION_DEPTH 1 +#else +# define IS_SLASH(ch) ( (ch) == '/' ) +# define DEFAULT_PROTECTION_DEPTH 2 +#endif + + + +/** + * Counts the components in the specified sub path. + * This is a helper for count_path_components. + * + * etc = 1 + * etc/ = 1 + * etc/x11 = 2 + * and so and and so forth. + */ +static int countSubPathComponents(const char *pszPath, int cDepth) +{ + for (;;) + { + const char *pszEnd; + size_t cch; + + /* skip slashes. */ + while (IS_SLASH(*pszPath)) + pszPath++; + if (!*pszPath) + break; + + /* find end of component. */ + pszEnd = pszPath; + while (!IS_SLASH(*pszEnd) && *pszEnd) + pszEnd++; + + /* count it, checking for '..' and '.'. */ + cch = pszEnd - pszPath; + if (cch == 2 && pszPath[0] == '.' && pszPath[1] == '.') + { + if (cDepth > 0) + cDepth--; + } + else if (cch != 1 || pszPath[0] != '.') + cDepth++; + + /* advance */ + if (!*pszEnd) + break; + pszPath = pszEnd + 1; + } + return cDepth; +} + + +/** + * Parses the specified path counting the number of components + * relative to root. + * + * We don't check symbolic links and such, just some simple and cheap + * path parsing. + * + * @param pszPath The path to process. + * + * @returns 0 or higher on success. + * On failure an error is printed, eval is set and -1 is returned. + */ +static int countPathComponents(PCKBUILDPROTECTION pThis, const char *pszPath) +{ + int cComponents = 0; + + /* + * Deal with root, UNC, drive letter. + */ +#if defined(_MSC_VER) || defined(__OS2__) + if (IS_SLASH(pszPath[0]) && IS_SLASH(pszPath[1]) && !IS_SLASH(pszPath[2])) + { + /* skip the root - UNC */ + pszPath += 3; + while (!IS_SLASH(*pszPath) && *pszPath) /* server name */ + pszPath++; + while (IS_SLASH(*pszPath)) + pszPath++; + while (!IS_SLASH(*pszPath) && *pszPath) /* share name */ + pszPath++; + while (IS_SLASH(*pszPath)) + pszPath++; + } + else + { + unsigned uDriveLetter = (unsigned)toupper(pszPath[0]) - (unsigned)'A'; + if (uDriveLetter <= (unsigned)('Z' - 'A') && pszPath[1] == ':') + uDriveLetter++; /* A == 1 */ + else + uDriveLetter = 0; /* 0 == default */ + + if (!IS_SLASH(pszPath[uDriveLetter ? 2 : 0])) + { + /* + * Relative path, must count cwd depth first. + */ +#ifdef __OS2__ /** @todo remove when ticket 194 has been fixed */ + char *pszCwd = _getdcwd(uDriveLetter, NULL, PATH_MAX); +#else + char *pszCwd = _getdcwd(uDriveLetter, NULL, 0); +#endif + char *pszTmp = pszCwd; + if (!pszTmp) + { + err(pThis->pCtx, 1, "_getdcwd"); + return -1; + } + + if (IS_SLASH(pszTmp[0]) && IS_SLASH(pszTmp[1])) + { + /* skip the root - UNC */ + pszTmp += 2; + while (!IS_SLASH(*pszTmp) && *pszTmp) /* server name */ + pszTmp++; + while (IS_SLASH(*pszTmp)) + pszTmp++; + while (!IS_SLASH(*pszTmp) && *pszTmp) /* share name */ + pszTmp++; + } + else + { + /* skip the drive letter and while we're at it, the root slash too. */ + pszTmp += 1 + (pszTmp[1] == ':'); + } + cComponents = countSubPathComponents(pszTmp, 0); + free(pszCwd); + } + else + { + /* skip the drive letter and while we're at it, the root slash too. */ + pszPath += uDriveLetter ? 3 : 1; + } + } +#else /* !WIN && !OS2 */ + if (!IS_SLASH(pszPath[0])) + { + /* + * Relative path, must count cwd depth first. + */ + char szCwd[4096]; + if (!getcwd(szCwd, sizeof(szCwd))) + { + err(pThis->pCtx, 1, "getcwd"); + return -1; + } + cComponents = countSubPathComponents(szCwd, 0); + } +#endif /* !WIN && !OS2 */ + + /* + * We're now past any UNC or drive letter crap, possibly positioned + * at the root slash or at the start of a path component at the + * given depth. Count the remainder. + */ + return countSubPathComponents(pszPath, cComponents); +} + + +/** + * Initializes the instance data. + * + * @param pThis Pointer to the instance data. + */ +void kBuildProtectionInit(PKBUILDPROTECTION pThis, PKMKBUILTINCTX pCtx) +{ + pThis->uMagic = KBUILD_PROTECTION_MAGIC; + pThis->pCtx = pCtx; + pThis->afTypes[KBUILDPROTECTIONTYPE_FULL] = 0; + pThis->afTypes[KBUILDPROTECTIONTYPE_RECURSIVE] = 1; + pThis->cProtectionDepth = DEFAULT_PROTECTION_DEPTH; +} + + +/** + * Destroys the instance data. + * + * @param pThis Pointer to the instance data. + */ +void kBuildProtectionTerm(PKBUILDPROTECTION pThis) +{ + pThis->uMagic = 0; +} + + +void kBuildProtectionEnable(PKBUILDPROTECTION pThis, KBUILDPROTECTIONTYPE enmType) +{ + assert(pThis->uMagic == KBUILD_PROTECTION_MAGIC); + assert(enmType < KBUILDPROTECTIONTYPE_MAX && enmType >= KBUILDPROTECTIONTYPE_FIRST); + pThis->afTypes[enmType] |= 1; +} + + +void kBuildProtectionDisable(PKBUILDPROTECTION pThis, KBUILDPROTECTIONTYPE enmType) +{ + assert(pThis->uMagic == KBUILD_PROTECTION_MAGIC); + assert(enmType < KBUILDPROTECTIONTYPE_MAX && enmType >= KBUILDPROTECTIONTYPE_FIRST); + pThis->afTypes[enmType] &= ~1U; +} + + +/** + * Sets the protection depth according to the option argument. + * + * @param pszValue The value. + * + * @returns 0 on success, -1 and errx on failure. + */ +int kBuildProtectionSetDepth(PKBUILDPROTECTION pThis, const char *pszValue) +{ + /* skip leading blanks, they don't count either way. */ + while (isspace(*pszValue)) + pszValue++; + + /* number or path? */ + if (!isdigit(*pszValue) || strpbrk(pszValue, ":/\\")) + pThis->cProtectionDepth = countPathComponents(pThis, pszValue); + else + { + char *pszMore = 0; + pThis->cProtectionDepth = strtol(pszValue, &pszMore, 0); + if (pThis->cProtectionDepth != 0 && pszMore) + { + /* trailing space is harmless. */ + while (isspace(*pszMore)) + pszMore++; + } + if (!pThis->cProtectionDepth || pszValue == pszMore || *pszMore) + return errx(pThis->pCtx, 1, "bogus protection depth: %s", pszValue); + } + + if (pThis->cProtectionDepth < 1) + return errx(pThis->pCtx, 1, "bogus protection depth: %s", pszValue); + return 0; +} + + +/** + * Scans the environment for option overrides. + * + * @param pThis Pointer to the instance data. + * @param papszEnv The environment array. + * @param pszPrefix The variable prefix. + * + * @returns 0 on success, -1 and err*() on failure. + */ +int kBuildProtectionScanEnv(PKBUILDPROTECTION pThis, char **papszEnv, const char *pszPrefix) +{ + unsigned i; + const size_t cchPrefix = strlen(pszPrefix); + + for (i = 0; papszEnv[i]; i++) + { + const char *pszVar = papszEnv[i]; + if (!strncmp(pszVar, pszPrefix, cchPrefix)) + { + pszVar += cchPrefix; + if (!strncmp(pszVar, "PROTECTION_DEPTH=", sizeof("PROTECTION_DEPTH=") - 1)) + { + const char *pszVal = pszVar + sizeof("PROTECTION_DEPTH=") - 1; + if (kBuildProtectionSetDepth(pThis, pszVal)) + return -1; + } + else if (!strncmp(pszVar, "DISABLE_PROTECTION=", sizeof("DISABLE_PROTECTION=") - 1)) + pThis->afTypes[KBUILDPROTECTIONTYPE_RECURSIVE] &= ~1U; + else if (!strncmp(pszVar, "ENABLE_PROTECTION=", sizeof("ENABLE_PROTECTION=") - 1)) + pThis->afTypes[KBUILDPROTECTIONTYPE_RECURSIVE] |= 3; + else if (!strncmp(pszVar, "DISABLE_FULL_PROTECTION=", sizeof("DISABLE_FULL_PROTECTION=") - 1)) + pThis->afTypes[KBUILDPROTECTIONTYPE_FULL] &= ~1U; + else if (!strncmp(pszVar, "ENABLE_FULL_PROTECTION=", sizeof("ENABLE_FULL_PROTECTION=") - 1)) + pThis->afTypes[KBUILDPROTECTIONTYPE_FULL] |= 3; + } + } + return 0; +} + + +/** + * Protect the upper layers of the file system against accidental + * or malicious deletetion attempt from within a makefile. + * + * @param pszPath The path to check. + * @param required_depth The minimum number of components in the + * path counting from the root. + * + * @returns 0 on success. + * On failure an error is printed and -1 is returned. + */ +int kBuildProtectionEnforce(PCKBUILDPROTECTION pThis, KBUILDPROTECTIONTYPE enmType, const char *pszPath) +{ + assert(pThis->uMagic == KBUILD_PROTECTION_MAGIC); + assert(enmType < KBUILDPROTECTIONTYPE_MAX && enmType >= KBUILDPROTECTIONTYPE_FIRST); + + if ( (pThis->afTypes[enmType] & 3) + || (pThis->afTypes[KBUILDPROTECTIONTYPE_FULL] & 3)) + { + /* + * Count the path and compare it with the required depth. + */ + int cComponents = countPathComponents(pThis, pszPath); + if (cComponents < 0) + return -1; + if ((unsigned int)cComponents <= pThis->cProtectionDepth) + { + errx(pThis->pCtx, 1, "%s: protected", pszPath); + return -1; + } + } + return 0; +} + + +/** + * Retrieve the default path protection depth. + * + * @returns the default value. + */ +int kBuildProtectionDefaultDepth(void) +{ + return DEFAULT_PROTECTION_DEPTH; +} + |