Clean up vm CredentialPipe

This code is needlessly complicated for what it does.  Even with the
intent, which is secure buffer cleanup, it's trivial to encase all this
into a single class that accepts the strings by rvalue reference, then
cleans them up afterward.

Doing this also cleans up a potential lifetime problem, where if the
unix socket returned immediately, it would've invalidated the buffers
that were being sent.  It also moves to async_write, instead of
async_write_some.  The former could in theory fail if the socket blocks
(unlikely in this scenario) but it's good to handle anyway.

Tested: Need some help here.  There's no backend for this, so we might
just have to rely on inspection.

Change-Id: I9032d458f8eb7a0689bee575aae611641bacee26
Signed-off-by: Ed Tanous <edtanous@google.com>
diff --git a/include/credential_pipe.hpp b/include/credential_pipe.hpp
new file mode 100644
index 0000000..169d47c
--- /dev/null
+++ b/include/credential_pipe.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <boost/asio/buffer.hpp>
+#include <boost/asio/io_context.hpp>
+#include <boost/asio/write.hpp>
+#include <boost/process/async_pipe.hpp>
+
+#include <array>
+#include <string>
+
+// Wrapper for boost::async_pipe ensuring proper pipe cleanup
+class CredentialsPipe
+{
+  public:
+    explicit CredentialsPipe(boost::asio::io_context& io) : impl(io) {}
+
+    CredentialsPipe(const CredentialsPipe&) = delete;
+    CredentialsPipe(CredentialsPipe&&) = delete;
+    CredentialsPipe& operator=(const CredentialsPipe&) = delete;
+    CredentialsPipe& operator=(CredentialsPipe&&) = delete;
+
+    ~CredentialsPipe()
+    {
+        explicit_bzero(user.data(), user.capacity());
+        explicit_bzero(pass.data(), pass.capacity());
+    }
+
+    int fd() const
+    {
+        return impl.native_source();
+    }
+
+    template <typename WriteHandler>
+    void asyncWrite(std::string&& username, std::string&& password,
+                    WriteHandler&& handler)
+    {
+        user = std::move(username);
+        pass = std::move(password);
+
+        // Add +1 to ensure that the null terminator is included.
+        std::array<boost::asio::const_buffer, 2> buffer{
+            {{user.data(), user.size() + 1}, {pass.data(), pass.size() + 1}}};
+        boost::asio::async_write(impl, buffer,
+                                 std::forward<WriteHandler>(handler));
+    }
+
+    boost::process::async_pipe impl;
+
+  private:
+    std::string user;
+    std::string pass;
+};