- Timestamp:
- Nov 3, 2010 5:18:35 PM (14 years ago)
- svn:sync-xref-src-repo-rev:
- 67370
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/HostDrivers/Support/SUPR3HardenedVerify.cpp
r33701 r33738 74 74 75 75 76 /******************************************************************************* 77 * Defined Constants And Macros * 78 *******************************************************************************/ 79 /** The max path length acceptable for a trusted path. */ 80 #define SUPR3HARDENED_MAX_PATH 260U 76 81 77 82 … … 748 753 749 754 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 */ 765 static 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 */ 797 static 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 */ 814 static 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 753 820 754 821 /** … … 760 827 * @param cbErr The size of the error buffer. 761 828 * @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 */ 830 static 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 */ 839 typedef 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. */ 860 typedef SUPR3HARDENEDPATHINFO *PSUPR3HARDENEDPATHINFO; 861 862 863 /** 864 * Verifies that the path is absolutely sane, it also parses the path. 777 865 * 778 866 * A sane path starts at the root (w/ drive letter on DOS derived systems) and 779 867 * 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 not781 * supported.868 * Sane paths are less or equal to SUPR3HARDENED_MAX_PATH bytes in length. UNC 869 * paths are not supported. 782 870 * 783 871 * @returns VBox status code. … … 785 873 * @param pszErr The error buffer. 786 874 * @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 */ 878 static 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. 795 885 */ 796 886 #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 803 897 #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; 807 903 #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, "'"); 809 914 810 915 /* 811 916 * Check each component. No parent references or double slashes. 812 917 */ 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]) 822 939 { 823 if (RTPATH_IS_SLASH(psz Path[0]))940 if (RTPATH_IS_SLASH(pszSrc[0])) 824 941 { 825 pszPath++; 942 pszSrc++; 943 if (*pszSrc) 944 *pszDst++ = RTPATH_SLASH; 945 else 946 pInfo->fDirSlash = true; 826 947 break; 827 948 } 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, "'"); 829 953 } 830 954 } 831 955 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 */ 973 typedef 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. */ 984 typedef SUPR3HARDENEDFSOBJSTATE *PSUPR3HARDENEDFSOBJSTATE; 985 /** Pointer to a const file system object state. */ 986 typedef SUPR3HARDENEDFSOBJSTATE const *PCSUPR3HARDENEDFSOBJSTATE; 987 988 989 /** 990 * Query information about a file system object by path. 850 991 * 851 992 * @returns VBox status code, error buffer filled on failure. 852 993 * @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. 854 995 * @param pszErr The error buffer. 855 996 * @param cbErr The size of the error buffer. 856 997 */ 857 static int supR3HardenedVerifyFsObject(char *pszPath, bool fDir, char *pszErr, size_t cbErr) 998 static int supR3HardenedQueryFsObjectByPath(char const *pszPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState, 999 char *pszErr, size_t cbErr) 858 1000 { 859 1001 #if defined(RT_OS_WINDOWS) 860 1002 /** @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 */ 1041 static 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 */ 1079 static 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 */ 1126 static 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. */ 861 1131 return VINF_SUCCESS; 862 1132 … … 867 1137 #else 868 1138 /* 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? */ 882 1161 #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 */ 1220 static 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; 883 1228 } 884 1229 … … 894 1239 * - 1 and beyond is scratch space. 895 1240 * @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. 896 1246 * @param fRecursive Whether to recurse into subdirectories. 897 * @param fCheckFiles Whether to check files.898 1247 * @param pszErr The error buffer. 899 1248 * @param cbErr The size of the error buffer. 900 1249 */ 901 static int supR3HardenedVerifyDirRecursive(char *pszDirPath, size_t cchDirPath, bool fRecursive, bool fCheckFiles,902 char *pszErr, size_t cbErr)1250 static int supR3HardenedVerifyDirRecursive(char *pszDirPath, size_t cchDirPath, PSUPR3HARDENEDFSOBJSTATE pFsObjState, 1251 bool fRecursive, char *pszErr, size_t cbErr) 903 1252 { 904 1253 #if defined(RT_OS_WINDOWS) … … 911 1260 912 1261 #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 */ 913 1267 DIR *pDir = opendir(pszDirPath); 914 1268 if (!pDir) … … 917 1271 if (errno == EACCES) 918 1272 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 */ 922 1289 int rc = VINF_SUCCESS; 923 1290 for (;;) 924 1291 { 1292 pszDirPath[cchDirPath] = '\0'; /* for error messages. */ 1293 925 1294 struct dirent Entry; 926 1295 struct dirent *pEntry; … … 928 1297 if (iErr) 929 1298 { 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, "'"); 931 1301 break; 932 1302 } … … 939 1309 */ 940 1310 size_t cchName = strlen(pEntry->d_name); 941 if (cchName + cchDirPath > 260)1311 if (cchName + cchDirPath > SUPR3HARDENED_MAX_PATH) 942 1312 { 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, "'"); 944 1315 break; 945 1316 } 946 1317 memcpy(&pszDirPath[cchName], pEntry->d_name, cchName + 1); 947 1318 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)) 955 1326 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; 957 1331 958 1332 /* 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. 982 1334 */ 983 1335 if ( fRecursive 984 && S_ISDIR( st.st_mode)1336 && S_ISDIR(pFsObjState->Stat.st_mode) 985 1337 && strcmp(pEntry->d_name, ".") 986 1338 && strcmp(pEntry->d_name, "..")) … … 989 1341 pszDirPath[cchDirPath + cchName + 1] = '\0'; 990 1342 991 rc = supR3HardenedVerifyDirRecursive(pszDirPath, cchDirPath + cchName + 1, fRecursive, fCheckFiles, pszErr, cbErr); 1343 rc = supR3HardenedVerifyDirRecursive(pszDirPath, cchDirPath + cchName + 1, pFsObjState, 1344 fRecursive, pszErr, cbErr); 992 1345 if (RT_FAILURE(rc)) 993 1346 break; … … 1014 1367 { 1015 1368 /* 1016 * Validate the input so we can be lazy when parsingit.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); 1020 1373 if (RT_FAILURE(rc)) 1021 1374 return rc; 1022 1375 1023 1376 /* 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); 1044 1388 if (RT_FAILURE(rc)) 1045 1389 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. 1053 1395 */ 1054 1396 if (fCheckFiles || fRecursive) 1055 1397 { 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); 1062 1402 } 1063 1403 … … 1071 1411 * @returns VBox status code, error buffer filled on failure. 1072 1412 * @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. 1073 1416 * @param pszErr The error buffer. 1074 1417 * @param cbErr The size of the error buffer. 1075 1418 */ 1076 DECLHIDDEN(int) supR3HardenedVerifyFile(const char *pszFilename, char *pszErr, size_t cbErr)1077 { 1078 /* 1079 * Validate the input so we can be lazy when parsingit.1080 */ 1081 size_t cchFilename;1082 int rc = supR3HardenedVerifyPathSanity(pszFilename, pszErr, cbErr, & cchFilename);1419 DECLHIDDEN(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); 1083 1426 if (RT_FAILURE(rc)) 1084 1427 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); 1107 1445 if (RT_FAILURE(rc)) 1108 1446 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); 1114 1455 return VINF_SUCCESS; 1115 1456 }
Note:
See TracChangeset
for help on using the changeset viewer.