cp: make "cp file /dev/node" special case; explained in comments
authorDenis Vlasenko <vda.linux@googlemail.com>
Tue, 11 Sep 2007 10:39:13 +0000 (10:39 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Tue, 11 Sep 2007 10:39:13 +0000 (10:39 -0000)
function                                             old     new   delta
copy_file                                           1487    1538     +51
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/0 up/down: 51/0)               Total: 51 bytes
   text    data     bss     dec     hex filename
 772502    1051   10724  784277   bf795 busybox_old
 772554    1051   10724  784329   bf7c9 busybox_unstripped

libbb/copy_file.c

index efb969faad8457f4a44c9dd7947df499ff623eb4..86449f27c853b0b654939a07d0243479e4ce1db6 100644 (file)
@@ -19,6 +19,9 @@
 // (or fail, if it points to dir/nonexistent location/etc).
 // This is strange, but POSIX-correct.
 // coreutils cp has --remove-destination to override this...
+//
+// NB: we have special code which still allows for "cp file /dev/node"
+// to work POSIX-ly (the only realistic case where it makes sense)
 
 #define DO_POSIX_CP 0  /* 1 - POSIX behavior, 0 - safe behavior */
 
@@ -243,13 +246,18 @@ int copy_file(const char *source, const char *dest, int flags)
                if (src_fd < 0)
                        return -1;
 
-#if DO_POSIX_CP  /* POSIX way (a security problem versus symlink attacks!): */
-               dst_fd = open(dest, (flags & FILEUTILS_INTERACTIVE)
-                               ? O_WRONLY|O_CREAT|O_EXCL
-                               : O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode);
-#else  /* safe way: */
-               dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode);
-#endif
+               /* POSIX way is a security problem versus symlink attacks,
+                * we do it only for dest's which are device nodes,
+                * and only for non-recursive, non-interactive cp. NB: it is still racy
+                * for "cp file /home/bad_user/device_node" case
+                * (user can rm device_node and create link to /etc/passwd) */
+               if (DO_POSIX_CP
+                || (dest_exists && !(flags & (FILEUTILS_RECUR|FILEUTILS_INTERACTIVE))
+                    && (S_ISBLK(dest_stat.st_mode) || S_ISCHR(dest_stat.st_mode)))
+               ) {
+                       dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, source_stat.st_mode);
+               } else  /* safe way: */
+                       dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, source_stat.st_mode);
                if (dst_fd == -1) {
                        ovr = ask_and_unlink(dest, flags);
                        if (ovr <= 0) {