2005-05-11 Dmitry V. Levin Fix race conditions in code which creates regular files, creates directories and devices or sets its permissions, in copy-in and copy-pass modes. * src/copyin.c (try_existing_file): Add argument: existing_mode. When `*existing_dir' is set to true, store file mode to `*existing_mode'. (create_final_defers, copyin_regular_file): Change open(2) calls to use O_EXCL. (copyin_directory): Change mkdir(2) and mknod(2) calls to use safe permissions. Add argument: existing_mode. When reusing already existing directory, no_chown_flag is not set and directory mode is too permissive, chmod it to safe mode before chown(2) call. (copyin_file): Pass existing_mode from try_existing_file() to copyin_directory(). * src/copypass.c (process_copy_pass): Change open(2) calls to use O_EXCL. Change mkdir(2) and mknod(2) calls to use safe permissions. When reusing already existing directory, no_chown_flag is not set and directory mode is too permissive, chmod it to safe mode before chown(2) call. diff -uprk.orig cpio-2.6.orig/src/copyin.c cpio-2.6/src/copyin.c --- cpio-2.6.orig/src/copyin.c 2005-05-11 12:20:17 +0000 +++ cpio-2.6/src/copyin.c 2005-05-11 13:39:03 +0000 @@ -185,11 +185,12 @@ list_file(struct new_cpio_header* file_h static int try_existing_file(struct new_cpio_header* file_hdr, int in_file_des, - int *existing_dir) + int *existing_dir, mode_t *existing_mode) { struct stat file_stat; *existing_dir = false; + *existing_mode = 0; if (lstat (file_hdr->c_name, &file_stat) == 0) { if (S_ISDIR (file_stat.st_mode) @@ -199,6 +200,7 @@ try_existing_file(struct new_cpio_header we are trying to create, don't complain about it. */ *existing_dir = true; + *existing_mode = file_stat.st_mode; return 0; } else if (!unconditional_flag @@ -375,12 +377,12 @@ create_final_defers () continue; } out_file_des = open (d->header.c_name, - O_CREAT | O_WRONLY | O_BINARY, 0600); + O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0600); if (out_file_des < 0 && create_dir_flag) { create_all_directories (d->header.c_name); out_file_des = open (d->header.c_name, - O_CREAT | O_WRONLY | O_BINARY, + O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0600); } if (out_file_des < 0) @@ -497,13 +499,13 @@ copyin_regular_file (struct new_cpio_hea /* If not linked, copy the contents of the file. */ out_file_des = open (file_hdr->c_name, - O_CREAT | O_WRONLY | O_BINARY, 0600); + O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0600); if (out_file_des < 0 && create_dir_flag) { create_all_directories (file_hdr->c_name); out_file_des = open (file_hdr->c_name, - O_CREAT | O_WRONLY | O_BINARY, + O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0600); } @@ -604,7 +606,8 @@ copyin_regular_file (struct new_cpio_hea } static void -copyin_directory(struct new_cpio_header* file_hdr, int existing_dir) +copyin_directory (struct new_cpio_header *file_hdr, int existing_dir, + mode_t existing_mode) { int res; /* Result of various function calls. */ #ifdef HPUX_CDF @@ -647,14 +650,22 @@ copyin_directory(struct new_cpio_header* cdf_flag = 1; } #endif - res = mkdir (file_hdr->c_name, file_hdr->c_mode); + res = mkdir (file_hdr->c_name, file_hdr->c_mode & ~077); } else - res = 0; + { + if (!no_chown_flag && (existing_mode & 077) != 0 + && chmod (file_hdr->c_name, existing_mode & 07700) < 0) + { + error (0, errno, "%s: chmod", file_hdr->c_name); + return; + } + res = 0; + } if (res < 0 && create_dir_flag) { create_all_directories (file_hdr->c_name); - res = mkdir (file_hdr->c_name, file_hdr->c_mode); + res = mkdir (file_hdr->c_name, file_hdr->c_mode & ~077); } if (res < 0) { @@ -743,12 +754,12 @@ copyin_device(struct new_cpio_header* fi return; } - res = mknod (file_hdr->c_name, file_hdr->c_mode, + res = mknod (file_hdr->c_name, file_hdr->c_mode & ~077, makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); if (res < 0 && create_dir_flag) { create_all_directories (file_hdr->c_name); - res = mknod (file_hdr->c_name, file_hdr->c_mode, + res = mknod (file_hdr->c_name, file_hdr->c_mode & ~077, makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); } if (res < 0) @@ -827,9 +838,11 @@ static void copyin_file (struct new_cpio_header* file_hdr, int in_file_des) { int existing_dir; + mode_t existing_mode; if (!to_stdout_option - && try_existing_file (file_hdr, in_file_des, &existing_dir) < 0) + && try_existing_file (file_hdr, in_file_des, &existing_dir, + &existing_mode) < 0) return; /* Do the real copy or link. */ @@ -840,7 +853,7 @@ copyin_file (struct new_cpio_header* fil break; case CP_IFDIR: - copyin_directory(file_hdr, existing_dir); + copyin_directory(file_hdr, existing_dir, existing_mode); break; case CP_IFCHR: diff -uprk.orig cpio-2.6.orig/src/copypass.c cpio-2.6/src/copypass.c --- cpio-2.6.orig/src/copypass.c 2005-05-11 12:20:17 +0000 +++ cpio-2.6/src/copypass.c 2005-05-11 13:44:28 +0000 @@ -157,12 +157,12 @@ process_copy_pass () continue; } out_file_des = open (output_name.ds_string, - O_CREAT | O_WRONLY | O_BINARY, 0600); + O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0600); if (out_file_des < 0 && create_dir_flag) { create_all_directories (output_name.ds_string); out_file_des = open (output_name.ds_string, - O_CREAT | O_WRONLY | O_BINARY, 0600); + O_CREAT | O_EXCL | O_WRONLY | O_BINARY, 0600); } if (out_file_des < 0) { @@ -243,15 +243,23 @@ process_copy_pass () cdf_flag = 1; } #endif - res = mkdir (output_name.ds_string, in_file_stat.st_mode); + res = mkdir (output_name.ds_string, in_file_stat.st_mode & ~077); } else - res = 0; + { + if (!no_chown_flag && (out_file_stat.st_mode & 077) != 0 + && chmod (output_name.ds_string, out_file_stat.st_mode & 07700) < 0) + { + error (0, errno, "%s: chmod", output_name.ds_string); + continue; + } + res = 0; + } if (res < 0 && create_dir_flag) { create_all_directories (output_name.ds_string); - res = mkdir (output_name.ds_string, in_file_stat.st_mode); + res = mkdir (output_name.ds_string, in_file_stat.st_mode & ~077); } if (res < 0) { @@ -314,12 +322,12 @@ process_copy_pass () if (link_res < 0) { - res = mknod (output_name.ds_string, in_file_stat.st_mode, + res = mknod (output_name.ds_string, in_file_stat.st_mode & ~077, in_file_stat.st_rdev); if (res < 0 && create_dir_flag) { create_all_directories (output_name.ds_string); - res = mknod (output_name.ds_string, in_file_stat.st_mode, + res = mknod (output_name.ds_string, in_file_stat.st_mode & ~077, in_file_stat.st_rdev); } if (res < 0)