coren has submitted this change and it was merged.

Change subject: Tool Labs Tools: add take.cc
......................................................................


Tool Labs Tools: add take.cc

Change-Id: I132b56ebdedfdaee83099d1a7b34215d06441f65
---
A src/take.cc
1 file changed, 165 insertions(+), 0 deletions(-)

Approvals:
  coren: Verified; Looks good to me, approved



diff --git a/src/take.cc b/src/take.cc
new file mode 100755
index 0000000..c2779df
--- /dev/null
+++ b/src/take.cc
@@ -0,0 +1,165 @@
+// Copyright © 2013 Marc-André Pelletier <mpellet...@wikimedia.org>
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+bool refuse(const char* file, const char* why)
+{
+       fprintf(stderr, "%s: %s\n", file, why);
+       return false;
+}
+
+bool error(const char* file)
+{
+       return refuse(file, strerror(errno));
+}
+
+// This is just convenience for autoclosing files and prevent fd leaks
+struct FD {
+       int             fd;
+
+                       FD(int f): fd(f)                { };
+                       ~FD()                           { if(fd >= 0) 
close(fd); };
+                       operator int (void) const       { return fd; };
+};
+
+gid_t  groups[256];
+int    ngroups;
+
+bool takeover(const char* path, bool trustpath)
+{
+       // We open the files and use the file descriptors exclusively
+       // to avoid trickery and race conditions
+       FD file = open(path, O_RDONLY|O_NOFOLLOW);
+
+       if(file < 0) {
+               if(errno == ELOOP)
+                       return refuse(path, "will not follow or touch 
symlinks");
+               return error(path);
+       }
+
+       struct stat     sfile;
+
+       if(fstat(file, &sfile))
+               return error(path);
+
+       if(!trustpath) {
+               char    dirname[strlen(path)+1];
+
+               strcpy(dirname, path);
+
+               if(path[0] && path[strlen(path)-1]=='/')
+                       dirname[strlen(path)-1] = 0;
+
+               if(char* slash = strrchr(dirname, '/'))
+                       *slash = 0;
+               else
+                       strcpy(dirname, ".");
+
+               FD dir  = open(dirname, O_RDONLY|O_NOFOLLOW);
+
+               if(dir < 0) {
+                       if(errno == ELOOP)
+                               return refuse(dirname, "will not follow or 
touch symlinks");
+                       return error(dirname);
+               }
+
+               struct stat     sdir;
+               if(fstat(dir, &sdir))
+                       return error(dirname);
+
+               if(!S_ISDIR(sdir.st_mode))
+                       return refuse(dirname, "containing directory doesn't 
appear to be... a directory?");
+               if(sdir.st_uid != getuid())
+                       return refuse(path, "you must own the containing 
directory");
+
+               // Here, we are being extremely paranoid, and check that the 
file is actually in the directory.
+               if(sdir.st_dev != sfile.st_dev)
+                       return refuse(path, "the file must be on the same 
filesystem as its directory");
+
+               bool found = false;
+               if(DIR* df = fdopendir(dir)) {
+                       while(dirent* d = readdir(df)) {
+                               if(sfile.st_ino == d->d_ino) {
+                                       found = true;
+                                       break;
+                               }
+                       }
+                       closedir(df);
+               } else
+                       return error(dirname);
+
+               if(!found)
+                       return refuse(path, "the file isn't in its directory?");
+       }
+
+       if(!S_ISDIR(sfile.st_mode) && sfile.st_nlink != 1)
+               return refuse(path, "will only touch files with no hardlinks");
+
+       // check that the group matches
+       bool found = false;
+       for(int i=0; i<ngroups; i++)
+               if(sfile.st_gid == groups[i]) {
+                       found = true;
+                       break;
+               }
+       if(!found)
+               return refuse(path, "You need to share a group with the file");
+
+       // everything checks out; do it.  Do it!  Doooo it!
+       if(fchown(file, getuid(), -1))
+               return error(path);
+
+       // recursion for fun and profit
+       if(S_ISDIR(sfile.st_mode)) {
+               if(DIR* df = fdopendir(file)) {
+                       bool    ok = true;
+
+                       while(dirent* d = readdir(df)) {
+                               if(d->d_name[0]=='.') {
+                                       if(d->d_name[1]==0)
+                                               continue;
+                                       if(d->d_name[1]=='.' && d->d_name[2]==0)
+                                               continue;
+                               }
+                               fchdir(file);
+                               ok &= takeover(d->d_name, true);
+                       }
+                       closedir(df);
+               } else
+                       return error(path);
+       }
+
+       return true;
+}
+
+
+int main(int argc, char** argv)
+{
+       ngroups = getgroups(sizeof(groups)/sizeof(groups[0]), groups);
+
+       bool ok = true;
+       for(int arg=1; arg<argc; arg++)
+               ok &= takeover(argv[arg], false);
+       return ok? 0: 1;
+}
+

-- 
To view, visit https://gerrit.wikimedia.org/r/69981
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I132b56ebdedfdaee83099d1a7b34215d06441f65
Gerrit-PatchSet: 2
Gerrit-Project: labs/toollabs
Gerrit-Branch: master
Gerrit-Owner: coren <mpellet...@wikimedia.org>
Gerrit-Reviewer: coren <mpellet...@wikimedia.org>

_______________________________________________
MediaWiki-commits mailing list
MediaWiki-commits@lists.wikimedia.org
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to