cryptohome: Add new platform functions

This is first in a series of CLs to add opencryptoki initialization to cryptohome.
This ones adds new platform functions for
1) changing file/path ownership recursively.
2) creating symlinks.
3) getting group id on a path.
4) executing a binary as a specific effective uid/gid.

(Original patchset by fes@chromium.org. This ones fixes some
minor bugs and contains documentation fixes.)

Change-Id: I424ca11fcb74ca5d3b040dccf0cf82ed8da766cc

BUG=chromium-os:12295
TEST=emerge-x86-mario chromeos-base/chromeos-cryptohome

Review URL: http://codereview.chromium.org/6729012
diff --git a/mock_platform.h b/mock_platform.h
index e8a9be3..31ee451 100644
--- a/mock_platform.h
+++ b/mock_platform.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -36,8 +36,13 @@
   MOCK_METHOD2(TerminatePidsWithOpenFiles, bool(const std::string&, bool));
   MOCK_METHOD2(TerminatePidsForUser, bool(const uid_t, bool));
   MOCK_METHOD3(SetOwnership, bool(const std::string&, uid_t, gid_t));
+  MOCK_METHOD3(SetOwnershipRecursive, bool(const std::string&, uid_t, gid_t));
   MOCK_METHOD3(GetUserId, bool(const std::string&, uid_t*, gid_t*));
+  MOCK_METHOD3(GetGroupId, bool(const std::string&, gid_t*));
   MOCK_CONST_METHOD1(AmountOfFreeDiskSpace, int64(const std::string&));
+  MOCK_METHOD2(Symlink, bool(const std::string&, const std::string&));
+  MOCK_METHOD4(Exec, bool(const std::string&, const std::vector<std::string>&,
+                          uid_t, gid_t));
 
  private:
   bool MockGetUserId(const std::string& user, uid_t* user_id, gid_t* group_id) {
diff --git a/platform.cc b/platform.cc
index 7e9155f..22db6b6 100644
--- a/platform.cc
+++ b/platform.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -7,6 +7,7 @@
 #include "platform.h"
 
 #include <errno.h>
+#include <grp.h>
 #include <limits.h>
 #include <pwd.h>
 #include <signal.h>
@@ -14,6 +15,7 @@
 #include <sys/stat.h>
 #include <sys/statvfs.h>
 #include <sys/types.h>
+#include <sys/wait.h>
 
 #include <base/file_util.h>
 #include <base/string_util.h>
@@ -337,14 +339,55 @@
   }
 }
 
-bool Platform::SetOwnership(const std::string& directory, uid_t user_id,
+bool Platform::SetOwnership(const std::string& path, uid_t user_id,
                             gid_t group_id) {
-  if (chown(directory.c_str(), user_id, group_id)) {
+  if (chown(path.c_str(), user_id, group_id)) {
     return false;
   }
   return true;
 }
 
+bool Platform::SetOwnershipRecursive(const std::string& directory,
+                                     uid_t user_id,
+                                     gid_t group_id) {
+  std::vector<std::string> to_recurse;
+  to_recurse.push_back(directory);
+  while (to_recurse.size()) {
+    std::string current_dir = to_recurse.back();
+    to_recurse.pop_back();
+
+    FilePath next_path;
+
+    // Push the subdirectories to the back of the vector
+    file_util::FileEnumerator dir_enumerator(
+        FilePath(current_dir),
+        false,  // do not recurse into subdirectories.
+        file_util::FileEnumerator::DIRECTORIES);
+    while (!(next_path = dir_enumerator.Next()).empty()) {
+      to_recurse.push_back(next_path.value());
+    }
+
+    // Handle the files
+    file_util::FileEnumerator file_enumerator(FilePath(current_dir), false,
+                                              file_util::FileEnumerator::FILES);
+    while (!(next_path = file_enumerator.Next()).empty()) {
+      if (!SetOwnership(next_path.value(), user_id, group_id)) {
+        LOG(ERROR) << "Couldn't change owner (" << user_id << ":" << group_id
+                   << ") of path: " << next_path.value().c_str();
+        return false;
+      }
+    }
+
+    // Set permissions on the directory itself
+    if (!SetOwnership(current_dir, user_id, group_id)) {
+      LOG(ERROR) << "Couldn't change owner (" << user_id << ":" << group_id
+                 << ") of path: " << current_dir.c_str();
+      return false;
+    }
+  }
+  return true;
+}
+
 int Platform::SetMask(int new_mask) {
   return umask(new_mask);
 }
@@ -353,7 +396,7 @@
                          gid_t* group_id) {
   // Load the passwd entry
   long user_name_length = sysconf(_SC_GETPW_R_SIZE_MAX);
-  if(user_name_length == -1) {
+  if (user_name_length == -1) {
     user_name_length = kDefaultPwnameLength;
   }
   struct passwd user_info, *user_infop;
@@ -367,6 +410,22 @@
   return true;
 }
 
+bool Platform::GetGroupId(const std::string& group, gid_t* group_id) {
+  // Load the group entry
+  long group_name_length = sysconf(_SC_GETGR_R_SIZE_MAX);
+  if (group_name_length == -1) {
+    group_name_length = kDefaultPwnameLength;
+  }
+  struct group group_info, *group_infop;
+  std::vector<char> group_name_buf(group_name_length);
+  if (getgrnam_r(group.c_str(), &group_info, &group_name_buf[0],
+                group_name_length, &group_infop)) {
+    return false;
+  }
+  *group_id = group_info.gr_gid;
+  return true;
+}
+
 int64 Platform::AmountOfFreeDiskSpace(const string& path) const {
   struct statvfs stats;
   if (statvfs(path.c_str(), &stats) != 0) {
@@ -379,4 +438,61 @@
   keyctl(KEYCTL_CLEAR, KEY_SPEC_USER_KEYRING);
 }
 
+bool Platform::Symlink(const std::string& from, const std::string& to) {
+  int rc = symlink(from.c_str(), to.c_str());
+  if (rc && rc != EEXIST) {
+    PLOG(ERROR) << "Error creating symbolic link from " << from << " to " << to
+                << ".";
+    return false;
+  }
+  return true;
+}
+
+bool Platform::Exec(const std::string& command,
+                    const std::vector<std::string>& args,
+                    uid_t uid,
+                    gid_t gid) {
+  pid_t child_pid = -1;
+  child_pid = vfork();
+  if (child_pid == 0) {
+    if (gid != static_cast<gid_t>(-1)) {
+      if (setresgid(gid, gid, gid)) {
+        _exit(2);
+      }
+    }
+    if (uid != static_cast<uid_t>(-1)) {
+      if (setresuid(uid, uid, uid)) {
+        _exit(1);
+      }
+    }
+    const char** local_args = (const char**) calloc(args.size() + 1,
+                                                    sizeof(char*));
+    int index = 0;
+    std::vector<std::string>::const_iterator it;
+    for (it = args.begin(); it != args.end(); ++it, ++index) {
+      local_args[index] = const_cast<char*>(it->c_str());
+    }
+    execve(command.c_str(), const_cast<char* const*>(local_args), NULL);
+    PLOG(ERROR) << "Couldn't start the command subprocess.";
+    _exit(3);
+  } else if (child_pid != -1) {
+    int status = 0;
+    do {
+      pid_t term_pid = waitpid(child_pid, &status, WUNTRACED | WCONTINUED);
+      if (term_pid == -1) {
+        return false;
+      }
+    } while (!WIFEXITED(status) && !WIFSIGNALED(status));
+    if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+      return true;
+    }
+    PLOG(ERROR) << "Command subprocess exited with a non-zero status. ("
+                << "status = " << WEXITSTATUS(status) << " )";
+  }
+  else {
+    PLOG(ERROR) << "Couldn't spawn a subprocess for command execution.";
+  }
+  return false;
+}
+
 } // namespace cryptohome
diff --git a/platform.h b/platform.h
index ee6d862..fad1144 100644
--- a/platform.h
+++ b/platform.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
@@ -130,14 +130,26 @@
   //   pids (OUT) - the list of PIDs
   void GetPidsForUser(uid_t uid, std::vector<pid_t>* pids);
 
+  // Calls the platform chown() function on the given path.
+  //
+  // The path may be a directory or a file.
+  //
+  // Parameters
+  //   path - The path to set ownership on
+  //   user_id - The user_id to assign ownership to
+  //   group_id - The group_id to assign ownership to
+  virtual bool SetOwnership(const std::string& directory, uid_t user_id,
+                            gid_t group_id);
+
   // Calls the platform chown() function recursively on the directory
   //
   // Parameters
   //   directory - The directory to set ownership on
   //   user_id - The user_id to assign ownership to
   //   group_id - The group_id to assign ownership to
-  virtual bool SetOwnership(const std::string& directory, uid_t user_id,
-                            gid_t group_id);
+  virtual bool SetOwnershipRecursive(const std::string& directory,
+                                     uid_t user_id,
+                                     gid_t group_id);
 
   // Sets the current umask, returning the old mask
   //
@@ -154,6 +166,13 @@
   virtual bool GetUserId(const std::string& user, uid_t* user_id,
                          gid_t* group_id);
 
+  // Returns the group id for a group
+  //
+  // Parameters
+  //   group - The group name to query for
+  //   group_id (OUT) - The group ID on success
+  virtual bool GetGroupId(const std::string& group, gid_t* group_id);
+
   // Return the available disk space in bytes on the volume containing |path|,
   // or -1 on failure.
   // Code duplicated from Chrome's base::SysInfo::AmountOfFreeDiskSpace().
@@ -165,6 +184,25 @@
   // Clears the user keyring
   static void ClearUserKeyring();
 
+  // Creates a symbolic link from one path to the other
+  //
+  // Parameters
+  //  from - source path that the symlink points to
+  //  to - symlink to create which points to the source path
+  virtual bool Symlink(const std::string& from, const std::string& to);
+
+  // Executes a command with the specified arguments and waits for it to finish
+  //
+  // Parameters
+  //  command - string containing the filename of the binary to execute
+  //  args - list of arguments to pass to the run the command with
+  //  uid - effective user id to run the command with
+  //  gid - effective group id to run the command with
+  bool Exec(const std::string& command,
+            const std::vector<std::string>& args,
+            uid_t uid,
+            gid_t gid);
+
   // Overrides the default mount options
   void set_mount_options(int value) {
     mount_options_ = value;