Symlink-race-safe variants of do_stat() / do_lstat() for receiver- side use. See the comment on do_chmod_at() for the threat model. stat() and lstat() resolve parent components, so a parent-symlink swap can make the receiver's stat see attributes of a victim file outside the module -- which then drives later behaviour (e.g. "this isn't a directory, delete it" -> attacker-controlled unlin
| 1142 | that dirfd. Same fall-through gating as the other wrappers. |
| 1143 | */ |
| 1144 | static int do_xstat_at(const char *path, STRUCT_STAT *st, int at_flags, int (*fallback)(const char *, STRUCT_STAT *)) |
| 1145 | { |
| 1146 | #ifdef AT_FDCWD |
| 1147 | extern int am_daemon, am_chrooted; |
| 1148 | char dirpath[MAXPATHLEN]; |
| 1149 | const char *bname; |
| 1150 | const char *slash; |
| 1151 | int dfd, ret, e; |
| 1152 | size_t dlen; |
| 1153 | |
| 1154 | if (!am_daemon || am_chrooted) |
| 1155 | return fallback(path, st); |
| 1156 | |
| 1157 | if (!path || !*path || *path == '/') |
| 1158 | return fallback(path, st); |
| 1159 | |
| 1160 | slash = strrchr(path, '/'); |
| 1161 | if (!slash) |
| 1162 | return fallback(path, st); |
| 1163 | |
| 1164 | dlen = slash - path; |
| 1165 | if (dlen >= sizeof dirpath) { |
| 1166 | errno = ENAMETOOLONG; |
| 1167 | return -1; |
| 1168 | } |
| 1169 | memcpy(dirpath, path, dlen); |
| 1170 | dirpath[dlen] = '\0'; |
| 1171 | bname = slash + 1; |
| 1172 | |
| 1173 | dfd = secure_relative_open(NULL, dirpath, O_RDONLY | O_DIRECTORY, 0); |
| 1174 | if (dfd < 0) |
| 1175 | return -1; |
| 1176 | |
| 1177 | ret = fstatat(dfd, bname, st, at_flags); |
| 1178 | e = errno; |
| 1179 | close(dfd); |
| 1180 | errno = e; |
| 1181 | return ret; |
| 1182 | #else |
| 1183 | return fallback(path, st); |
| 1184 | #endif |
| 1185 | } |
| 1186 | |
| 1187 | int do_stat_at(const char *path, STRUCT_STAT *st) |
| 1188 | { |
no test coverage detected