Symlink-race-safe variant of do_mkdir() for receiver-side use. See the comment on do_chmod_at() for the threat model and design rationale. mkdir() resolves parent symlinks at every component, so a parent- component swap can place an attacker-named directory outside the module. Defence: open the parent of fname under secure_relative_open() and call mkdirat() against that dirfd. Mutate
| 1029 | parent and absolute-path cases. |
| 1030 | */ |
| 1031 | int do_mkdir_at(char *path, mode_t mode) |
| 1032 | { |
| 1033 | #ifdef AT_FDCWD |
| 1034 | extern int am_daemon, am_chrooted; |
| 1035 | char dirpath[MAXPATHLEN]; |
| 1036 | const char *bname; |
| 1037 | const char *slash; |
| 1038 | int dfd, ret, e; |
| 1039 | size_t dlen; |
| 1040 | |
| 1041 | if (dry_run) return 0; |
| 1042 | RETURN_ERROR_IF_RO_OR_LO; |
| 1043 | trim_trailing_slashes(path); |
| 1044 | |
| 1045 | if (!am_daemon || am_chrooted) |
| 1046 | return mkdir(path, mode); |
| 1047 | |
| 1048 | if (!path || !*path || *path == '/') |
| 1049 | return mkdir(path, mode); |
| 1050 | |
| 1051 | slash = strrchr(path, '/'); |
| 1052 | if (!slash) |
| 1053 | return mkdir(path, mode); |
| 1054 | |
| 1055 | dlen = slash - path; |
| 1056 | if (dlen >= sizeof dirpath) { |
| 1057 | errno = ENAMETOOLONG; |
| 1058 | return -1; |
| 1059 | } |
| 1060 | memcpy(dirpath, path, dlen); |
| 1061 | dirpath[dlen] = '\0'; |
| 1062 | bname = slash + 1; |
| 1063 | |
| 1064 | dfd = secure_relative_open(NULL, dirpath, O_RDONLY | O_DIRECTORY, 0); |
| 1065 | if (dfd < 0) |
| 1066 | return -1; |
| 1067 | |
| 1068 | ret = mkdirat(dfd, bname, mode); |
| 1069 | e = errno; |
| 1070 | close(dfd); |
| 1071 | errno = e; |
| 1072 | return ret; |
| 1073 | #else |
| 1074 | return do_mkdir(path, mode); |
| 1075 | #endif |
| 1076 | } |
| 1077 | |
| 1078 | /* like mkstemp but forces permissions */ |
| 1079 | int do_mkstemp(char *template, mode_t perms) |
no test coverage detected