VirtualBox

Changeset 33738 in vbox for trunk/src


Ignore:
Timestamp:
Nov 3, 2010 5:18:35 PM (14 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
67370
Message:

SUPR3HardenedVerify.cpp: Rewrote the new code a bit.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp

    r33701 r33738  
    7474
    7575
     76/*******************************************************************************
     77*   Defined Constants And Macros                                               *
     78*******************************************************************************/
     79/** The max path length acceptable for a trusted path. */
     80#define SUPR3HARDENED_MAX_PATH      260U
    7681
    7782
     
    748753
    749754
    750 /** Wrapper macro that adds the message length argument. */
    751 #define supR3HardenedSetError(rc, pszErr, cbErr, szMsg)  \
    752     supR3HardenedSetErrorInt(rc, pszErr, cbErr, szMsg, sizeof(szMsg) - 1)
     755/**
     756 * Copies the N messages into the error buffer and returns @a rc.
     757 *
     758 * @returns Returns @a rc
     759 * @param   rc                  The return code.
     760 * @param   pszErr              The error buffer.
     761 * @param   cbErr               The size of the error buffer.
     762 * @param   cMsgs               The number of messages in the ellipsis.
     763 * @param   ...                 Message parts.
     764 */
     765static int supR3HardenedSetErrorN(int rc, char *pszErr, size_t cbErr, unsigned cMsgs, ...)
     766{
     767    va_list va;
     768    va_start(va, cMsgs);
     769    while (cMsgs-- > 0 && cbErr > 0)
     770    {
     771        const char *pszMsg = va_arg(va,  const char *);
     772        size_t cchMsg = VALID_PTR(pszMsg) ? strlen(pszMsg) : 0;
     773        if (cchMsg >= cbErr)
     774            cchMsg = cbErr - 1;
     775        memcpy(pszErr, pszMsg, cchMsg);
     776        pszErr[cchMsg] = '\0';
     777        pszErr += cchMsg;
     778        cbErr -= cchMsg;
     779    }
     780    va_end(va);
     781
     782    return rc;
     783}
     784
     785
     786/**
     787 * Copies the three messages into the error buffer and returns @a rc.
     788 *
     789 * @returns Returns @a rc
     790 * @param   rc                  The return code.
     791 * @param   pszErr              The error buffer.
     792 * @param   cbErr               The size of the error buffer.
     793 * @param   pszMsg1             The first message part.
     794 * @param   pszMsg2             The second message part.
     795 * @param   pszMsg3             The third message part.
     796 */
     797static int supR3HardenedSetError3(int rc, char *pszErr, size_t cbErr, const char *pszMsg1,
     798                                  const char *pszMsg2, const char *pszMsg3)
     799{
     800    return supR3HardenedSetErrorN(rc, pszErr, cbErr, 3, pszMsg1, pszMsg2, pszMsg3);
     801}
     802
     803
     804/**
     805 * Copies the two messages into the error buffer and returns @a rc.
     806 *
     807 * @returns Returns @a rc
     808 * @param   rc                  The return code.
     809 * @param   pszErr              The error buffer.
     810 * @param   cbErr               The size of the error buffer.
     811 * @param   pszMsg1             The first message part.
     812 * @param   pszMsg2             The second message part.
     813 */
     814static int supR3HardenedSetError2(int rc, char *pszErr, size_t cbErr, const char *pszMsg1,
     815                                  const char *pszMsg2)
     816{
     817    return supR3HardenedSetErrorN(rc, pszErr, cbErr, 2, pszMsg1, pszMsg2);
     818}
     819
    753820
    754821/**
     
    760827 * @param   cbErr               The size of the error buffer.
    761828 * @param   pszMsg              The message.
    762  * @param   cchMsg              The length of the message text.
    763  */
    764 static int supR3HardenedSetErrorInt(int rc, char *pszErr, size_t cbErr, const char *pszMsg, size_t cchMsg)
    765 {
    766     if (cbErr <= cchMsg)
    767         cchMsg = cbErr - 1;
    768     memcpy(pszErr, pszMsg, cchMsg);
    769     pszErr[cchMsg] = '\0';
    770 
    771     return rc;
    772 }
    773 
    774 
    775 /**
    776  * Verifies that the path is absolutely sane.
     829 */
     830static int supR3HardenedSetError(int rc, char *pszErr, size_t cbErr, const char *pszMsg)
     831{
     832    return supR3HardenedSetErrorN(rc, pszErr, cbErr, 1, pszMsg);
     833}
     834
     835
     836/**
     837 * Output from a successfull supR3HardenedVerifyPathSanity call.
     838 */
     839typedef struct SUPR3HARDENEDPATHINFO
     840{
     841    /** The length of the path in szCopy. */
     842    uint16_t        cch;
     843    /** The number of path components. */
     844    uint16_t        cComponents;
     845    /** Set if the path ends with slash, indicating that it's a directory
     846     * reference and not a file reference.  The slash has been removed from
     847     * the copy. */
     848    bool            fDirSlash;
     849    /** The offset where each path component starts, i.e. the char after the
     850     * slash.  The array has cComponents + 1 entries, where the final one is
     851     * cch + 1 so that one can always terminate the current component by
     852     * szPath[aoffComponent[i] - 1] = '\0'. */
     853    uint16_t        aoffComponents[32+1];
     854    /** A normalized copy of the path.
     855     * Reserve some extra space so we can be more relaxed about overflow
     856     * checks and terminator paddings, especially when recursing. */
     857    char            szPath[SUPR3HARDENED_MAX_PATH * 2];
     858} SUPR3HARDENEDPATHINFO;
     859/** Pointer to a parsed path. */
     860typedef SUPR3HARDENEDPATHINFO *PSUPR3HARDENEDPATHINFO;
     861
     862
     863/**
     864 * Verifies that the path is absolutely sane, it also parses the path.
    777865 *
    778866 * A sane path starts at the root (w/ drive letter on DOS derived systems) and
    779867 * does not have any relative bits (/../) or unnecessary slashes (/bin//ls).
    780  * Sane paths are less or equal to 260 bytes in length.  UNC paths are not
    781  * supported.
     868 * Sane paths are less or equal to SUPR3HARDENED_MAX_PATH bytes in length.  UNC
     869 * paths are not supported.
    782870 *
    783871 * @returns VBox status code.
     
    785873 * @param   pszErr              The error buffer.
    786874 * @param   cbErr               The size of the error buffer.
    787  * @param   pcchPath            Where to return the path length.
    788  */
    789 static int supR3HardenedVerifyPathSanity(const char *pszPath, char *pszErr, size_t cbErr, size_t *pcchPath)
    790 {
    791     const char * const pszPathStart = pszPath;
    792 
    793     /*
    794      * Check that it's an absolute path.
     875 * @param   pInfo               Where to return a copy of the path along with
     876 *                              parsing information.
     877 */
     878static int supR3HardenedVerifyPathSanity(const char *pszPath, char *pszErr, size_t cbErr, PSUPR3HARDENEDPATHINFO pInfo)
     879{
     880    const char *pszSrc = pszPath;
     881    char       *pszDst = pInfo->szPath;
     882
     883    /*
     884     * Check that it's an absolute path and copy the volume/root specifier.
    795885     */
    796886#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    797     if (   !(   (pszPath[0] >= 'A' && pszPath[0] <= 'Z')
    798              || (pszPath[0] >= 'a' && pszPath[0] <= 'z'))
    799         || pszPath[1] != ':'
    800         || !RTPATH_IS_SLASH(pszPath[2]))
    801         return supR3HardenedSetError(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pszErr, cbErr, "The path is not absolute");
    802     size_t const cchRootSpec = 3;
     887    if (   RT_C_IS_ALPHA(pszSrc[0])
     888        || pszSrc[1] != ':'
     889        || !RTPATH_IS_SLASH(pszSrc[2]))
     890        return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pszErr, cbErr, "The path is not absolute: '", pszPath, "'");
     891
     892    *pszDst++ = RT_C_TO_UPPER(pszSrc[0]);
     893    *pszDst++ = ':';
     894    *pszDst++ = RTPATH_SLASH;
     895    pszSrc += 3;
     896
    803897#else
    804     if (!RTPATH_IS_SLASH(pszPath[0]))
    805         return supR3HardenedSetError(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pszErr, cbErr, "The path is not absolute");
    806     size_t const cchRootSpec = 1;
     898    if (!RTPATH_IS_SLASH(pszSrc[0]))
     899        return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pszErr, cbErr, "The path is not absolute: '", pszPath, "'");
     900
     901    *pszDst++ = RTPATH_SLASH;
     902    pszSrc += 1;
    807903#endif
    808     pszPath += cchRootSpec;
     904
     905    /*
     906     * No path specifying the root or something very shortly thereafter will
     907     * be approved of.
     908     */
     909    if (pszSrc[0] == '\0')
     910        return supR3HardenedSetError3(VERR_SUPLIB_PATH_IS_ROOT, pszErr, cbErr, "The path is root: '", pszPath, "'");
     911    if (   pszSrc[1] == '\0'
     912        || pszSrc[2] == '\0')
     913        return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_SHORT, pszErr, cbErr, "The path is too short: '", pszPath, "'");
    809914
    810915    /*
    811916     * Check each component.  No parent references or double slashes.
    812917     */
    813     while (pszPath[0])
    814     {
    815         if (   pszPath[0] == '.'
    816             && pszPath[1] == '.'
    817             && RTPATH_IS_SLASH(pszPath[2]))
    818             return supR3HardenedSetError(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pszErr, cbErr, "The path is not absolute");
    819         if (RTPATH_IS_SLASH(pszPath[0]))
    820             return supR3HardenedSetError(VERR_SUPLIB_PATH_NOT_CLEAN, pszErr, cbErr, "The path is not clean of double slashes");
    821         while (pszPath[0])
     918    pInfo->fDirSlash = false;
     919    while (pszSrc[0])
     920    {
     921        /* Sanity checks. */
     922        if (RTPATH_IS_SLASH(pszSrc[0])) /* can be relaxed if we care. */
     923            return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_CLEAN, pszErr, cbErr,
     924                                          "The path is not clean of double slashes: '", pszPath, "'");
     925        if (   pszSrc[0] == '.'
     926            && pszSrc[1] == '.'
     927            && RTPATH_IS_SLASH(pszSrc[2]))
     928            return supR3HardenedSetError3(VERR_SUPLIB_PATH_NOT_ABSOLUTE, pszErr, cbErr,
     929                                          "The path is not absolute: '", pszPath, "'");
     930
     931        /* Record the start of the component. */
     932        if (pInfo->cComponents >= RT_ELEMENTS(pInfo->aoffComponents) - 1)
     933            return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_MANY_COMPONENTS, pszErr, cbErr,
     934                                          "The path has too many components: '", pszPath, "'");
     935        pInfo->aoffComponents[pInfo->cComponents++] = pszDst - &pInfo->szPath[0];
     936
     937        /* Traverse to the end of the component, copying it as we go along. */
     938        while (pszSrc[0])
    822939        {
    823             if (RTPATH_IS_SLASH(pszPath[0]))
     940            if (RTPATH_IS_SLASH(pszSrc[0]))
    824941            {
    825                 pszPath++;
     942                pszSrc++;
     943                if (*pszSrc)
     944                    *pszDst++ = RTPATH_SLASH;
     945                else
     946                    pInfo->fDirSlash = true;
    826947                break;
    827948            }
    828             pszPath++;
     949            *pszDst++ = *pszSrc++;
     950            if (pszDst - &pInfo->szPath[0] >= SUPR3HARDENED_MAX_PATH)
     951                return supR3HardenedSetError3(VERR_SUPLIB_PATH_TOO_LONG, pszErr, cbErr,
     952                                              "The path is too long: '", pszPath, "'");
    829953        }
    830954    }
    831955
    832     /*
    833      * Check the path length, root specifications are not permitted and we
    834      * think paths longer than 260 bytes for are insane.
    835      */
    836     size_t cchPath = pszPath - pszPathStart;
    837     if (cchPath <= cchRootSpec)
    838         return supR3HardenedSetError(VERR_SUPLIB_PATH_IS_ROOT, pszErr, cbErr, "The path is too root");
    839     if (cchPath > 260)
    840         return supR3HardenedSetError(VERR_SUPLIB_PATH_TOO_LONG, pszErr, cbErr, "The path is too long");
    841 
    842     if (pcchPath)
    843         *pcchPath = (size_t)(pszPath - pszPathStart);
    844     return VINF_SUCCESS;
    845 }
    846 
    847 
    848 /**
    849  * Verifies on file system object (file or directory).
     956    /* Terminate the string and enter its length. */
     957    pszDst[0] = '\0';
     958    pszDst[1] = '\0';                   /* for aoffComponents */
     959    pInfo->cch = (uint16_t)(pszDst - &pInfo->szPath[0]);
     960    pInfo->aoffComponents[pInfo->cComponents] = pInfo->cch + 1;
     961
     962    return VINF_SUCCESS;
     963}
     964
     965
     966/**
     967 * The state information collected by supR3HardenedVerifyFsObject.
     968 *
     969 * This can be used to verify that a directory we've opened for enumeration is
     970 * the same as the one that supR3HardenedVerifyFsObject just verified.  It can
     971 * equally be used to verify a native specfied by the user.
     972 */
     973typedef struct SUPR3HARDENEDFSOBJSTATE
     974{
     975#ifdef RT_OS_WINDOWS
     976    /** Not implemented for windows yet. */
     977    char            chTodo;
     978#else
     979    /** The stat output. */
     980    struct stat     Stat;
     981#endif
     982} SUPR3HARDENEDFSOBJSTATE;
     983/** Pointer to a file system object state. */
     984typedef SUPR3HARDENEDFSOBJSTATE *PSUPR3HARDENEDFSOBJSTATE;
     985/** Pointer to a const file system object state. */
     986typedef SUPR3HARDENEDFSOBJSTATE const *PCSUPR3HARDENEDFSOBJSTATE;
     987
     988
     989/**
     990 * Query information about a file system object by path.
    850991 *
    851992 * @returns VBox status code, error buffer filled on failure.
    852993 * @param   pszPath             The path to the object.
    853  * @param   fDir                Whether this is a directory or a file.
     994 * @param   pFsObjState         Where to return the state information.
    854995 * @param   pszErr              The error buffer.
    855996 * @param   cbErr               The size of the error buffer.
    856997 */
    857 static int supR3HardenedVerifyFsObject(char *pszPath, bool fDir, char *pszErr, size_t cbErr)
     998static int supR3HardenedQueryFsObjectByPath(char const *pszPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
     999                                            char *pszErr, size_t cbErr)
    8581000{
    8591001#if defined(RT_OS_WINDOWS)
    8601002    /** @todo Windows hardening. */
     1003    pFsObjState->chTodo = 0;
     1004    return VINF_SUCCESS;
     1005
     1006#else
     1007    /*
     1008     * Stat the object, do not follow links.
     1009     */
     1010    if (lstat(pszPath, &pFsObjState->Stat) != 0)
     1011    {
     1012        /* Ignore access errors */
     1013        if (errno != EACCES)
     1014            return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pszErr, cbErr,
     1015                                          5, "stat failed with ", strerror(errno), " on: '", pszPath, "'");
     1016    }
     1017
     1018    /*
     1019     * Read ACLs.
     1020     */
     1021    /** @todo */
     1022
     1023    return VINF_SUCCESS;
     1024#endif
     1025}
     1026
     1027
     1028/**
     1029 * Query information about a file system object by native handle.
     1030 *
     1031 * @returns VBox status code, error buffer filled on failure.
     1032 * @param   hNative             The native handle to the object @a pszPath
     1033 *                              specifies and this should be verified to be the
     1034 *                              same file system object.
     1035 * @param   pFsObjState         Where to return the state information.
     1036 * @param   pszPath             The path to the object. (For the error message
     1037 *                              only.)
     1038 * @param   pszErr              The error buffer.
     1039 * @param   cbErr               The size of the error buffer.
     1040 */
     1041static int supR3HardenedQueryFsObjectByHandle(RTHCUINTPTR hNative, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
     1042                                              char const *pszPath, char *pszErr, size_t cbErr)
     1043{
     1044#if defined(RT_OS_WINDOWS)
     1045    /** @todo Windows hardening. */
     1046    pFsObjState->chTodo = 0;
     1047    return VINF_SUCCESS;
     1048
     1049#else
     1050    /*
     1051     * Stat the object, do not follow links.
     1052     */
     1053    if (fstat((int)hNative, &pFsObjState->Stat) != 0)
     1054        return supR3HardenedSetErrorN(VERR_SUPLIB_STAT_FAILED, pszErr, cbErr,
     1055                                      5, "fstat failed with ", strerror(errno), " on '", pszPath, "'");
     1056
     1057    /*
     1058     * Read ACLs.
     1059     */
     1060    /** @todo */
     1061
     1062    return VINF_SUCCESS;
     1063#endif
     1064}
     1065
     1066
     1067/**
     1068 * Verifies that the file system object indicated by the native handle is the
     1069 * same as the one @a pFsObjState indicates.
     1070 *
     1071 * @returns VBox status code, error buffer filled on failure.
     1072 * @param   pFsObjState1        File system object information/state by path.
     1073 * @param   pFsObjState2        File system object information/state by handle.
     1074 * @param   pszPath             The path to the object @a pFsObjState
     1075 *                              describes.  (For the error message.)
     1076 * @param   pszErr              The error buffer.
     1077 * @param   cbErr               The size of the error buffer.
     1078 */
     1079static int supR3HardenedIsSameFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState1, PCSUPR3HARDENEDFSOBJSTATE pFsObjState2,
     1080                                       const char *pszPath, char *pszErr, size_t cbErr)
     1081{
     1082#if defined(RT_OS_WINDOWS)
     1083    /** @todo Windows hardening. */
     1084    return VINF_SUCCESS;
     1085
     1086#elif defined(RT_OS_OS2)
     1087    return VINF_SUCCESS;
     1088
     1089#else
     1090    /*
     1091     * Compare the ino+dev, then the uid+gid and finally the important mode
     1092     * bits.  Technically the first one should be enough, but we're paranoid.
     1093     */
     1094    if (   pFsObjState1->Stat.st_ino != pFsObjState2->Stat.st_ino
     1095        || pFsObjState1->Stat.st_dev != pFsObjState2->Stat.st_dev)
     1096        return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pszErr, cbErr,
     1097                                      "The native handle is not the same as '", pszPath, "' (ino/dev)");
     1098    if (   pFsObjState1->Stat.st_uid != pFsObjState2->Stat.st_uid
     1099        || pFsObjState1->Stat.st_gid != pFsObjState2->Stat.st_gid)
     1100        return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pszErr, cbErr,
     1101                                      "The native handle is not the same as '", pszPath, "' (uid/gid)");
     1102    if (   (pFsObjState1->Stat.st_mode & (S_IFMT | S_IWUSR | S_IWGRP | S_IWOTH))
     1103        != (pFsObjState2->Stat.st_mode & (S_IFMT | S_IWUSR | S_IWGRP | S_IWOTH)))
     1104        return supR3HardenedSetError3(VERR_SUPLIB_NOT_SAME_OBJECT, pszErr, cbErr,
     1105                                      "The native handle is not the same as '", pszPath, "' (mode)");
     1106    return VINF_SUCCESS;
     1107#endif
     1108}
     1109
     1110
     1111/**
     1112 * Verifies a file system object (file or directory).
     1113 *
     1114 * @returns VBox status code, error buffer filled on failure.
     1115 * @param   pFsObjState         The file system object information/state to be
     1116 *                              verified.
     1117 * @param   fDir                Whether this is a directory or a file.
     1118 * @param   fRelaxed            Whether we can be more relaxed about this
     1119 *                              directory (only used for grand parent
     1120 *                              directories).
     1121 * @param   pszPath             The path to the object. (For error messages
     1122 *                              only.)
     1123 * @param   pszErr              The error buffer.
     1124 * @param   cbErr               The size of the error buffer.
     1125 */
     1126static int supR3HardenedVerifyFsObject(PCSUPR3HARDENEDFSOBJSTATE pFsObjState, bool fDir, bool fRelaxed,
     1127                                       const char *pszPath, char *pszErr, size_t cbErr)
     1128{
     1129#if defined(RT_OS_WINDOWS)
     1130    /** @todo Windows hardening. */
    8611131    return VINF_SUCCESS;
    8621132
     
    8671137#else
    8681138    /*
    869      * Stat the file and check that only root can modify it.
    870      */
    871     struct stat st;
    872     if (stat(pszPath, &st) != 0)
    873     {
    874         /* Ignore access errors */
    875         if (errno != EACCES)
    876             return supR3HardenedSetError(VERR_SUPLIB_STAT_FAILED, pszErr, cbErr, pszPath);
    877     }
    878 
    879     /** @todo continue here tomorrow. */
    880 
    881     return VINF_SUCCESS;
     1139     * The owner must be root.
     1140     *
     1141     * This can be extended to include predefined system users if necessary.
     1142     */
     1143    if (pFsObjState->Stat.st_uid != 0)
     1144        return supR3HardenedSetError3(VERR_SUPLIB_OWNER_NOT_ROOT, pszErr, cbErr, "The owner is not root: '", pszPath, "'");
     1145
     1146    /*
     1147     * The group does not matter if it does not have write access, if it has
     1148     * write access it must be group 0 (root/wheel/whatever).
     1149     *
     1150     * This can be extended to include predefined system groups or groups that
     1151     * only root is member of.
     1152     */
     1153    if (   (pFsObjState->Stat.st_mode & S_IWGRP)
     1154        && pFsObjState->Stat.st_gid != 0)
     1155    {
     1156#ifdef RT_OS_DARWIN
     1157        /* HACK ALERT: On Darwin /Applications is root:admin with admin having
     1158           full access. So, to work around we relax the hardening a bit and
     1159           permit grand parents and beyond to be group writable by admin. */
     1160        if (pFsObjState->Stat.st_gid != 80 /*admin*/) /** @todo dynamically resolve the admin group? */
    8821161#endif
     1162            return supR3HardenedSetError3(VERR_SUPLIB_WRITE_NON_SYS_GROUP, pszErr, cbErr,
     1163                                          "The group is not a system group and it has write access to '", pszPath, "'");
     1164    }
     1165
     1166    /*
     1167     * World must not have write access.  There is no relaxing this rule.
     1168     */
     1169    if (pFsObjState->Stat.st_mode & S_IWOTH)
     1170        return supR3HardenedSetError3(VERR_SUPLIB_WORLD_WRITABLE, pszErr, cbErr,
     1171                                      "World writable: '", pszPath, "'");
     1172
     1173    /*
     1174     * Check the ACLs.
     1175     */
     1176    /** @todo */
     1177
     1178    /*
     1179     * Check the object type.
     1180     */
     1181    if (   !S_ISDIR(pFsObjState->Stat.st_mode)
     1182        && !S_ISREG(pFsObjState->Stat.st_mode))
     1183    {
     1184        if (S_ISLNK(pFsObjState->Stat.st_mode))
     1185            return supR3HardenedSetError3(VERR_SUPLIB_SYMLINKS_ARE_NOT_PERMITTED, pszErr, cbErr,
     1186                                          "Symlinks are not permitted: '", pszPath, "'");
     1187        return supR3HardenedSetError3(VERR_SUPLIB_NOT_DIR_NOT_FILE, pszErr, cbErr,
     1188                                      "Not regular file or directory: '", pszPath, "'");
     1189    }
     1190    if (fDir != !!S_ISDIR(pFsObjState->Stat.st_mode))
     1191    {
     1192        if (S_ISDIR(pFsObjState->Stat.st_mode))
     1193            return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pszErr, cbErr,
     1194                                          "Expected file but found directory: '", pszPath, "'");
     1195        return supR3HardenedSetError3(VERR_SUPLIB_IS_FILE, pszErr, cbErr,
     1196                                      "Expected directory but found file: '", pszPath, "'");
     1197    }
     1198
     1199    return VINF_SUCCESS;
     1200#endif
     1201
     1202}
     1203
     1204
     1205/**
     1206 * Verifies that the file system object indicated by the native handle is the
     1207 * same as the one @a pFsObjState indicates.
     1208 *
     1209 * @returns VBox status code, error buffer filled on failure.
     1210 * @param   hNative             The native handle to the object @a pszPath
     1211 *                              specifies and this should be verified to be the
     1212 *                              same file system object.
     1213 * @param   pFsObjState         The information/state returned by a previous
     1214 *                              query call.
     1215 * @param   pszPath             The path to the object @a pFsObjState
     1216 *                              describes.  (For the error message.)
     1217 * @param   pszErr              The error buffer.
     1218 * @param   cbErr               The size of the error buffer.
     1219 */
     1220static int supR3HardenedVerifySameFsObject(RTHCUINTPTR hNative, PCSUPR3HARDENEDFSOBJSTATE pFsObjState,
     1221                                           const char *pszPath, char *pszErr, size_t cbErr)
     1222{
     1223    SUPR3HARDENEDFSOBJSTATE FsObjState2;
     1224    int rc = supR3HardenedQueryFsObjectByHandle(hNative, &FsObjState2, pszPath, pszErr, cbErr);
     1225    if (RT_SUCCESS(rc))
     1226        rc = supR3HardenedIsSameFsObject(pFsObjState, &FsObjState2, pszPath, pszErr, cbErr);
     1227    return rc;
    8831228}
    8841229
     
    8941239 *                              - 1 and beyond is scratch space.
    8951240 * @param   cchDirPath          The length of the directory path + slash.
     1241 * @param   pFsObjState         Pointer to the file system object state buffer.
     1242 *                              On input this will hold the stats for
     1243 *                              the directory @a pszDirPath indicates and will
     1244 *                              be used to verified that we're opening the same
     1245 *                              thing.
    8961246 * @param   fRecursive          Whether to recurse into subdirectories.
    897  * @param   fCheckFiles         Whether to check files.
    8981247 * @param   pszErr              The error buffer.
    8991248 * @param   cbErr               The size of the error buffer.
    9001249 */
    901 static int supR3HardenedVerifyDirRecursive(char *pszDirPath, size_t cchDirPath, bool fRecursive, bool fCheckFiles,
    902                                            char *pszErr, size_t cbErr)
     1250static int supR3HardenedVerifyDirRecursive(char *pszDirPath, size_t cchDirPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState,
     1251                                           bool fRecursive, char *pszErr, size_t cbErr)
    9031252{
    9041253#if defined(RT_OS_WINDOWS)
     
    9111260
    9121261#else
     1262    /*
     1263     * Open the directory.  Now, we could probably eliminate opendir here
     1264     * and go down on kernel API level (open + getdents for instance), however
     1265     * that's not very portable and hopefully not necessary.
     1266     */
    9131267    DIR *pDir = opendir(pszDirPath);
    9141268    if (!pDir)
     
    9171271        if (errno == EACCES)
    9181272            return VINF_SUCCESS;
    919         return supR3HardenedSetError(VERR_SUPLIB_DIR_ENUM_FAILED, pszErr, cbErr, pszDirPath);
    920     }
    921 
     1273        return supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pszErr, cbErr,
     1274                                      5, "opendir failed with ", strerror(errno), " on '", pszDirPath, "'");
     1275    }
     1276    if (dirfd(pDir) != -1)
     1277    {
     1278        int rc = supR3HardenedVerifySameFsObject(dirfd(pDir), pFsObjState, pszDirPath, pszErr, cbErr);
     1279        if (RT_FAILURE(rc))
     1280        {
     1281            closedir(pDir);
     1282            return rc;
     1283        }
     1284    }
     1285
     1286    /*
     1287     * Enumerate the directory, check all the requested bits.
     1288     */
    9221289    int rc = VINF_SUCCESS;
    9231290    for (;;)
    9241291    {
     1292        pszDirPath[cchDirPath] = '\0';  /* for error messages. */
     1293
    9251294        struct dirent Entry;
    9261295        struct dirent *pEntry;
     
    9281297        if (iErr)
    9291298        {
    930             rc = supR3HardenedSetError(VERR_SUPLIB_DIR_ENUM_FAILED, pszErr, cbErr, pszDirPath);
     1299            rc = supR3HardenedSetErrorN(VERR_SUPLIB_DIR_ENUM_FAILED, pszErr, cbErr,
     1300                                        5, "readdir_r failed with ", strerror(iErr), " in '", pszDirPath, "'");
    9311301            break;
    9321302        }
     
    9391309         */
    9401310        size_t cchName = strlen(pEntry->d_name);
    941         if (cchName + cchDirPath > 260)
     1311        if (cchName + cchDirPath > SUPR3HARDENED_MAX_PATH)
    9421312        {
    943             rc = supR3HardenedSetError(VERR_SUPLIB_PATH_TOO_LONG, pszErr, cbErr, pszDirPath);
     1313            rc = supR3HardenedSetErrorN(VERR_SUPLIB_PATH_TOO_LONG, pszErr, cbErr,
     1314                                        4, "Path grew too long during recursion: '", pszDirPath, pEntry->d_name, "'");
    9441315            break;
    9451316        }
    9461317        memcpy(&pszDirPath[cchName], pEntry->d_name, cchName + 1);
    9471318
    948         struct stat st;
    949         if (stat(pszDirPath, &st) != 0)
    950         {
    951             /* Ignore access errors */
    952             if (errno == EACCES)
    953                 continue;
    954             rc = supR3HardenedSetError(VERR_SUPLIB_STAT_ENUM_FAILED, pszErr, cbErr, pszDirPath);
     1319        /*
     1320         * Query the information about the entry and verify it.
     1321         * (We don't bother skipping '.' and '..' at this point, a little bit
     1322         * of extra checks doesn't hurt and neither requires relaxed handling.)
     1323         */
     1324        rc = supR3HardenedQueryFsObjectByPath(pszDirPath, pFsObjState, pszErr, cbErr);
     1325        if (RT_SUCCESS(rc))
    9551326            break;
    956         }
     1327        rc = supR3HardenedVerifyFsObject(pFsObjState, S_ISDIR(pFsObjState->Stat.st_mode), false /*fRelaxed*/,
     1328                                         pszDirPath, pszErr, cbErr);
     1329        if (RT_FAILURE(rc))
     1330            break;
    9571331
    9581332        /*
    959          * Only files and directories are allowed.
    960          */
    961         if (!S_ISDIR(st.st_mode) && S_ISREG(st.st_mode))
    962         {
    963             if (S_ISLNK(st.st_mode))
    964                 rc = supR3HardenedSetError(VERR_SUPLIB_SYMLINKS_ARE_NOT_PERMITTED, pszErr, cbErr, pszDirPath);
    965             rc = supR3HardenedSetError(VERR_SUPLIB_NOT_DIR_NOT_FILE, pszErr, cbErr, pszDirPath);
    966             break;
    967         }
    968 
    969         /*
    970          * Verify it.
    971          */
    972         if (   S_ISDIR(st.st_mode)
    973             || fCheckFiles)
    974         {
    975             rc = supR3HardenedVerifyFsObject(pszDirPath, S_ISDIR(st.st_mode), pszErr, cbErr);
    976             if (RT_FAILURE(rc))
    977                 break;
    978         }
    979 
    980         /*
    981          * Recurse.
     1333         * Recurse into subdirectories if requested.
    9821334         */
    9831335        if (    fRecursive
    984             &&  S_ISDIR(st.st_mode)
     1336            &&  S_ISDIR(pFsObjState->Stat.st_mode)
    9851337            &&  strcmp(pEntry->d_name, ".")
    9861338            &&  strcmp(pEntry->d_name, ".."))
     
    9891341            pszDirPath[cchDirPath + cchName + 1] = '\0';
    9901342
    991             rc = supR3HardenedVerifyDirRecursive(pszDirPath, cchDirPath + cchName + 1, fRecursive, fCheckFiles, pszErr, cbErr);
     1343            rc = supR3HardenedVerifyDirRecursive(pszDirPath, cchDirPath + cchName + 1, pFsObjState,
     1344                                                 fRecursive, pszErr, cbErr);
    9921345            if (RT_FAILURE(rc))
    9931346                break;
     
    10141367{
    10151368    /*
    1016      * Validate the input so we can be lazy when parsing it.
    1017      */
    1018     size_t cchDirPath;
    1019     int rc = supR3HardenedVerifyPathSanity(pszDirPath, pszErr, cbErr, &cchDirPath);
     1369     * Validate the input path and parse it.
     1370     */
     1371    SUPR3HARDENEDPATHINFO Info;
     1372    int rc = supR3HardenedVerifyPathSanity(pszDirPath, pszErr, cbErr, &Info);
    10201373    if (RT_FAILURE(rc))
    10211374        return rc;
    10221375
    10231376    /*
    1024      * Make a copy of the input path and verify each component from the
    1025      * root and up.  This is the same as for supR3HardenedVerifyFile.
    1026      */
    1027     char szPath[RTPATH_MAX];
    1028     memcpy(szPath, pszDirPath, cchDirPath + 1);
    1029 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    1030     size_t off = 3;
    1031 #else
    1032     size_t off = 1;
    1033 #endif
    1034     while (off < cchDirPath)
    1035     {
    1036         size_t offSlash = off + 1;
    1037         while (szPath[offSlash] && !RTPATH_IS_SLASH(szPath[offSlash]))
    1038             offSlash++;
    1039 
    1040         char chSaved = szPath[offSlash];
    1041         szPath[offSlash] = '\0';
    1042 
    1043         rc = supR3HardenedVerifyFsObject(szPath, true /*fDir*/, pszErr, cbErr);
     1377     * Verify each component from the root up.
     1378     */
     1379    SUPR3HARDENEDFSOBJSTATE FsObjState;
     1380    uint32_t const          cComponents = Info.cComponents;
     1381    for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
     1382    {
     1383        bool fRelaxed = iComponent + 2 < cComponents;
     1384        Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
     1385        rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pszErr, cbErr);
     1386        if (RT_SUCCESS(rc))
     1387            rc = supR3HardenedVerifyFsObject(&FsObjState,true /*fDir*/, fRelaxed, Info.szPath, pszErr, cbErr);
    10441388        if (RT_FAILURE(rc))
    10451389            return rc;
    1046 
    1047         szPath[offSlash] = chSaved;
    1048         off = offSlash + 1;
    1049     }
    1050 
    1051     /*
    1052      * Do we need to check files or/and recurse into subdirectories?
     1390        Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = iComponent + 1 == cComponents ? RTPATH_SLASH : '\0';
     1391    }
     1392
     1393    /*
     1394     * Check files and subdirectories if requested.
    10531395     */
    10541396    if (fCheckFiles || fRecursive)
    10551397    {
    1056         if (!RTPATH_IS_SLASH(szPath[cchDirPath - 1]))
    1057         {
    1058             szPath[cchDirPath++] = RTPATH_SLASH;
    1059             szPath[cchDirPath] = '\0';
    1060         }
    1061         return supR3HardenedVerifyDirRecursive(szPath, cchDirPath, fRecursive, fCheckFiles, pszErr, cbErr);
     1398        Info.szPath[Info.cch]     = RTPATH_SLASH;
     1399        Info.szPath[Info.cch + 1] = '\0';
     1400        return supR3HardenedVerifyDirRecursive(Info.szPath, Info.cch + 1, &FsObjState,
     1401                                               fRecursive, pszErr, cbErr);
    10621402    }
    10631403
     
    10711411 * @returns VBox status code, error buffer filled on failure.
    10721412 * @param   pszFilename         The file to verify.
     1413 * @param   hNativeFile         Handle to the file, verify that it's the same
     1414 *                              as we ended up with when verifying the path.
     1415 *                              RTHCUINTPTR_MAX means NIL here.
    10731416 * @param   pszErr              The error buffer.
    10741417 * @param   cbErr               The size of the error buffer.
    10751418 */
    1076 DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, char *pszErr, size_t cbErr)
    1077 {
    1078     /*
    1079      * Validate the input so we can be lazy when parsing it.
    1080      */
    1081     size_t cchFilename;
    1082     int rc = supR3HardenedVerifyPathSanity(pszFilename, pszErr, cbErr, &cchFilename);
     1419DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, RTHCUINTPTR hNativeFile, char *pszErr, size_t cbErr)
     1420{
     1421    /*
     1422     * Validate the input path and parse it.
     1423     */
     1424    SUPR3HARDENEDPATHINFO Info;
     1425    int rc = supR3HardenedVerifyPathSanity(pszFilename, pszErr, cbErr, &Info);
    10831426    if (RT_FAILURE(rc))
    10841427        return rc;
    1085 
    1086     /*
    1087      * Make a copy of the input filename and verify each component from the
    1088      * root and up.
    1089      */
    1090     char szPath[RTPATH_MAX];
    1091     memcpy(szPath, pszFilename, cchFilename + 1);
    1092 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
    1093     size_t off = 3;
    1094 #else
    1095     size_t off = 1;
    1096 #endif
    1097     while (off < cchFilename)
    1098     {
    1099         size_t offSlash = off + 1;
    1100         while (szPath[offSlash] && !RTPATH_IS_SLASH(szPath[offSlash]))
    1101             offSlash++;
    1102 
    1103         char chSaved = szPath[offSlash];
    1104         szPath[offSlash] = '\0';
    1105 
    1106         rc = supR3HardenedVerifyFsObject(szPath, offSlash < cchFilename /*fDir*/, pszErr, cbErr);
     1428    if (Info.fDirSlash)
     1429        return supR3HardenedSetError3(VERR_SUPLIB_IS_DIRECTORY, pszErr, cbErr,
     1430                                      "The file path specifies a directory: '", pszFilename, "'");
     1431
     1432    /*
     1433     * Verify each component from the root up.
     1434     */
     1435    SUPR3HARDENEDFSOBJSTATE FsObjState;
     1436    uint32_t const          cComponents = Info.cComponents;
     1437    for (uint32_t iComponent = 0; iComponent < cComponents; iComponent++)
     1438    {
     1439        bool fFinal   = iComponent + 1 == cComponents;
     1440        bool fRelaxed = iComponent + 2 < cComponents;
     1441        Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = '\0';
     1442        rc = supR3HardenedQueryFsObjectByPath(Info.szPath, &FsObjState, pszErr, cbErr);
     1443        if (RT_SUCCESS(rc))
     1444            rc = supR3HardenedVerifyFsObject(&FsObjState, !fFinal /*fDir*/, fRelaxed, Info.szPath, pszErr, cbErr);
    11071445        if (RT_FAILURE(rc))
    11081446            return rc;
    1109 
    1110         szPath[offSlash] = chSaved;
    1111         off = offSlash + 1;
    1112     }
    1113 
     1447        Info.szPath[Info.aoffComponents[iComponent + 1] - 1] = fFinal ? RTPATH_SLASH : '\0';
     1448    }
     1449
     1450    /*
     1451     * Verify the file.
     1452     */
     1453    if (hNativeFile != RTHCUINTPTR_MAX)
     1454        return supR3HardenedVerifySameFsObject(hNativeFile, &FsObjState, Info.szPath, pszErr, cbErr);
    11141455    return VINF_SUCCESS;
    11151456}
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette