1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
| #define _GNU_SOURCE #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> #include <pwd.h> #include <grp.h> #include <string.h> #include <errno.h> #include <dirent.h> #include <time.h> #include <sys/xattr.h>
// 安全的所有权修改结构 typedef struct { char path[512]; uid_t current_uid; gid_t current_gid; uid_t target_uid; gid_t target_gid; mode_t current_mode; time_t modify_time; int success; char error_msg[128]; } ownership_change_record_t;
#define MAX_RECORDS 1000 ownership_change_record_t change_records[MAX_RECORDS]; int record_count = 0;
// 创建安全测试环境 int create_secure_test_environment() { printf("创建安全测试环境...\n"); // 创建测试目录 if (mkdir("secure_ownership_test", 0755) == -1 && errno != EEXIST) { perror("创建测试目录失败"); return -1; } // 创建敏感文件 const char* sensitive_files[] = { "secure_ownership_test/config.txt", "secure_ownership_test/private.key", "secure_ownership_test/database.db", "secure_ownership_test/logs/app.log" }; // 创建日志目录 if (mkdir("secure_ownership_test/logs", 0755) == -1 && errno != EEXIST) { perror("创建日志目录失败"); } // 创建敏感文件 for (int i = 0; i < 4; i++) { int fd = open(sensitive_files[i], O_CREAT | O_WRONLY | O_TRUNC, (i == 1) ? 0600 : 0644); // private.key使用更严格的权限 if (fd != -1) { const char* content = (i == 1) ? "PRIVATE KEY DATA" : "SENSITIVE DATA"; write(fd, content, strlen(content)); close(fd); printf("创建敏感文件: %s\n", sensitive_files[i]); } } // 创建符号链接 if (symlink("config.txt", "secure_ownership_test/link_to_config") == 0) { printf("创建符号链接\n"); } return 0; }
// 安全的所有权修改函数 int secure_chownat(int dirfd, const char* pathname, uid_t owner, gid_t group, int flags, const char* reason) { if (record_count >= MAX_RECORDS) { fprintf(stderr, "记录缓冲区已满\n"); return -1; } ownership_change_record_t* record = &change_records[record_count]; strncpy(record->path, pathname, sizeof(record->path) - 1); record->target_uid = owner; record->target_gid = group; record->modify_time = time(NULL); record->success = 0; record->error_msg[0] = '\0'; // 获取当前状态 struct stat current_stat; if (fstatat(dirfd, pathname, ¤t_stat, (flags & AT_SYMLINK_NOFOLLOW) ? AT_SYMLINK_NOFOLLOW : 0) == 0) { record->current_uid = current_stat.st_uid; record->current_gid = current_stat.st_gid; record->current_mode = current_stat.st_mode; } else { snprintf(record->error_msg, sizeof(record->error_msg), "获取当前状态失败: %s", strerror(errno)); record_count++; return -1; } // 执行所有权修改 if (fchownat(dirfd, pathname, owner, group, flags) == 0) { record->success = 1; printf("✓ 安全修改所有权: %s (%s)\n", pathname, reason ? reason : "无原因"); } else { snprintf(record->error_msg, sizeof(record->error_msg), "%s", strerror(errno)); printf("✗ 修改所有权失败: %s (%s) - %s\n", pathname, reason ? reason : "无原因", strerror(errno)); } record_count++; return record->success ? 0 : -1; }
// 验证所有权修改的安全性 int verify_ownership_change(int dirfd, const char* pathname, uid_t expected_uid, gid_t expected_gid) { struct stat verify_stat; if (fstatat(dirfd, pathname, &verify_stat, 0) == 0) { if (verify_stat.st_uid == expected_uid && verify_stat.st_gid == expected_gid) { printf("✓ 所有权验证通过: %s (UID:%d, GID:%d)\n", pathname, (int)expected_uid, (int)expected_gid); return 1; } else { printf("✗ 所有权验证失败: %s (期望UID:%d,GID:%d 实际UID:%d,GID:%d)\n", pathname, (int)expected_uid, (int)expected_gid, (int)verify_stat.st_uid, (int)verify_stat.st_gid); return 0; } } else { printf("✗ 验证失败,无法获取文件状态: %s\n", strerror(errno)); return 0; } }
// 显示修改记录 void show_change_records() { printf("\n=== 所有权修改记录 ===\n"); printf("%-30s %-8s %-8s %-8s %-8s %-10s %s\n", "路径", "原UID", "原GID", "新UID", "新GID", "状态", "时间"); printf("%-30s %-8s %-8s %-8s %-8s %-10s %s\n", "----", "------", "------", "------", "------", "----", "----"); for (int i = 0; i < record_count; i++) { const ownership_change_record_t* record = &change_records[i]; char time_str[32]; strftime(time_str, sizeof(time_str), "%H:%M:%S", localtime(&record->modify_time)); printf("%-30s %-8d %-8d %-8d %-8d %-10s %s\n", record->path, (int)record->current_uid, (int)record->current_gid, (int)record->target_uid, (int)record->target_gid, record->success ? "成功" : "失败", time_str); if (!record->success && record->error_msg[0] != '\0') { printf(" 错误信息: %s\n", record->error_msg); } } printf("========================\n\n"); }
// 模拟权限检查 int check_permission_for_chown(uid_t current_uid, uid_t file_uid) { // Root用户可以修改任何文件的所有权 if (current_uid == 0) { return 1; } // 文件所有者可以修改自己文件的组 if (current_uid == file_uid) { return 1; } // 需要CAP_CHOWN能力的其他情况 printf("警告: 非root用户修改他人文件所有权可能失败\n"); return 0; }
int main() { printf("=== 高级所有权管理和安全特性示例 ===\n"); uid_t current_uid = getuid(); printf("当前用户ID: %d (%s)\n", (int)current_uid, current_uid == 0 ? "root" : "非root"); // 创建安全测试环境 printf("\n1. 创建安全测试环境:\n"); if (create_secure_test_environment() == -1) { exit(EXIT_FAILURE); } // 打开测试目录 int test_dirfd = open("secure_ownership_test", O_RDONLY); if (test_dirfd == -1) { perror("打开测试目录失败"); exit(EXIT_FAILURE); } // 显示初始状态 printf("\n2. 初始文件状态:\n"); DIR* dir = fdopendir(dup(test_dirfd)); if (dir) { struct dirent* entry; while ((entry = readdir(dir)) != NULL) { if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) { struct stat file_stat; if (fstatat(test_dirfd, entry->d_name, &file_stat, AT_SYMLINK_NOFOLLOW) == 0) { struct passwd* pwd = getpwuid(file_stat.st_uid); struct group* grp = getgrgid(file_stat.st_gid); printf(" %s: UID=%d(%s) GID=%d(%s) 权限=%o\n", entry->d_name, (int)file_stat.st_uid, pwd ? pwd->pw_name : "unknown", (int)file_stat.st_gid, grp ? grp->gr_name : "unknown", file_stat.st_mode & 0777); } } } closedir(dir); } // 演示安全的所有权修改 printf("\n3. 安全所有权修改演示:\n"); // 获取测试用户 struct passwd* test_user1 = getpwnam("nobody"); struct passwd* test_user2 = getpwnam("daemon"); uid_t user1_uid = test_user1 ? test_user1->pw_uid : 65534; uid_t user2_uid = test_user2 ? test_user2->pw_uid : 1; struct group* test_group1 = getgrnam("nobody"); struct group* test_group2 = getgrnam("daemon"); gid_t group1_gid = test_group1 ? test_group1->gr_gid : 65534; gid_t group2_gid = test_group2 ? test_group2->gr_gid : 1; // 安全修改配置文件所有权 secure_chownat(test_dirfd, "config.txt", user1_uid, group1_gid, 0, "配置文件所有权转移"); verify_ownership_change(test_dirfd, "config.txt", user1_uid, group1_gid); // 安全修改日志文件所有权 secure_chownat(test_dirfd, "logs/app.log", user2_uid, group2_gid, 0, "日志文件所有权转移"); verify_ownership_change(test_dirfd, "logs/app.log", user2_uid, group2_gid); // 演示符号链接处理 printf("\n4. 符号链接处理演示:\n"); // 获取符号链接当前状态 struct stat link_stat, target_stat; if (fstatat(test_dirfd, "link_to_config", &link_stat, AT_SYMLINK_NOFOLLOW) == 0 && fstatat(test_dirfd, "link_to_config", &target_stat, 0) == 0) { printf("符号链接状态:\n"); printf(" 链接文件: UID=%d, GID=%d\n", (int)link_stat.st_uid, (int)link_stat.st_gid); printf(" 目标文件: UID=%d, GID=%d\n", (int)target_stat.st_uid, (int)target_stat.st_gid); } // 默认行为:修改目标文件 secure_chownat(test_dirfd, "link_to_config", user1_uid + 1, group1_gid + 1, 0, "修改符号链接目标"); verify_ownership_change(test_dirfd, "config.txt", user1_uid + 1, group1_gid + 1); // 使用AT_SYMLINK_NOFOLLOW:修改链接本身 secure_chownat(test_dirfd, "link_to_config", user2_uid + 1, group2_gid + 1, AT_SYMLINK_NOFOLLOW, "修改符号链接本身"); // 验证符号链接状态 if (fstatat(test_dirfd, "link_to_config", &link_stat, AT_SYMLINK_NOFOLLOW) == 0) { printf("修改后链接文件所有权: UID=%d, GID=%d\n", (int)link_stat.st_uid, (int)link_stat.st_gid); } // 演示错误处理和权限检查 printf("\n5. 错误处理和权限检查演示:\n"); // 尝试修改不存在的文件 secure_chownat(test_dirfd, "nonexistent.txt", 0, 0, 0, "测试不存在文件"); // 尝试使用无效的UID/GID secure_chownat(test_dirfd, "config.txt", 999999, 999999, 0, "测试无效UID/GID"); // 尝试在只读文件系统上修改(如果可能) // 这个测试需要特殊的环境设置 // 显示所有修改记录 show_change_records(); // 统计成功和失败的修改 int success_count = 0, failure_count = 0; for (int i = 0; i < record_count; i++) { if (change_records[i].success) { success_count++; } else { failure_count++; } } printf("修改统计:\n"); printf(" 成功: %d\n", success_count); printf(" 失败: %d\n", failure_count); printf(" 总计: %d\n", record_count); // 清理测试环境 printf("\n6. 清理测试环境:\n"); // 删除文件 const char* test_files[] = { "secure_ownership_test/link_to_config", "secure_ownership_test/config.txt", "secure_ownership_test/private.key", "secure_ownership_test/database.db", "secure_ownership_test/logs/app.log" }; for (int i = 0; i < 5; i++) { if (unlink(test_files[i]) == 0) { printf("删除文件: %s\n", test_files[i]); } } // 删除目录 rmdir("secure_ownership_test/logs"); rmdir("secure_ownership_test"); printf("删除测试目录\n"); close(test_dirfd); printf("\n=== 高级所有权管理演示完成 ===\n"); return 0; }
|