Skip to content

Instantly share code, notes, and snippets.

@mitchellh
Created April 5, 2013 06:29
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save mitchellh/5317088 to your computer and use it in GitHub Desktop.
Save mitchellh/5317088 to your computer and use it in GitHub Desktop.
Sudo wrapper that ships with Fusion provider for Vagrant, rewritten in Go.
// This is a dead simple wrapper that can have setuid set on it so that
// the sudo helper is run as root. It is expected to run in the same CWD
// as the actual Ruby sudo helper. Any arguments to this script are forwarded
// to the Ruby sudo helper script.
package main
import (
"fmt"
"os"
"path/filepath"
"syscall"
)
const sudoHelperCommand = "vagrant_vmware_desktop_sudo_helper"
func main() {
// We need to be running as root to properly setuid and execute
// the sudo helper as root.
if os.Geteuid() != 0 {
fmt.Fprintf(os.Stderr, "sudo helper setuid-wrapper must run as root.\n")
os.Exit(1)
}
// We have to setuid here because suid bits only change the EUID,
// and when Ruby sees the EUID != RUID, it resets the EUID back to RUID,
// nullifying the change we actually want. This forces the script to
// run as root.
if err := syscall.Setuid(0); err != nil {
fmt.Fprintf(os.Stderr, "%s\n", err.Error())
os.Exit(1)
}
// Put together the complete path to the helper program.
var helperPath = fmt.Sprintf("%s/%s", filepath.Dir(os.Args[0]), sudoHelperCommand)
// Setup the argv array so that the arguments are preserved for the
// helper, while also adding our arguments so the helper is properly called.
var helperArgs = make([]string, len(os.Args)+1)
helperArgs[0] = "ruby"
helperArgs[1] = helperPath
copy(helperArgs[2:], os.Args[1:])
// Replace ourselves with the actual helper. This should terminate
// execution of this program, but if not we output and exit.
if err := syscall.Exec("ruby", helperArgs, os.Environ()); err != nil {
fmt.Fprintf(os.Stderr, "Exec error: %s\n", err.Error())
os.Exit(1)
}
panic("Reached unreachable code!")
}
/**
* Vagrant VMWare Fusion Sudo Helper Wrapper
*
* This is a dead simple wrapper that can have setuid set on it so that
* the sudo helper is run as root. It is expected to run in the same CWD
* as the actual Ruby sudo helper. Any arguments to this script are forwarded
* to the Ruby sudo helper script.
* */
#include <errno.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#define HELPER_PATH_MAX 4096
#define SUDO_HELPER_COMMAND "vagrant_vmware_fusion_sudo_helper"
int main(int argc, char *argv[]) {
// We need to be running as root to properly setuid and execute
// the sudo helper as root.
if (geteuid() != 0) {
fprintf(stderr, "sudo helper setuid-wrapper must run as root\n");
return 1;
}
// Put together the actual path to the Ruby script we want to
// execute. Using dirname(argv[0]) is safe because the VMware Fusion
// provider guarantees that a full path is given as the argv[0].
char helperPath[HELPER_PATH_MAX];
snprintf(helperPath, HELPER_PATH_MAX, "%s/%s",
dirname(argv[0]), SUDO_HELPER_COMMAND);
// Setup the argv array so that the arguments are preserved for the
// sudo helper.
char **helperArgs = malloc(sizeof(char *) * (argc + 1));;
memcpy(helperArgs + 2, argv + 1, sizeof(char *) * (argc - 1));
helperArgs[0] = "ruby";
helperArgs[1] = helperPath;
helperArgs[argc + 1] = NULL;
// You have to setuid here because suid bits only change the EUID,
// and when Ruby sees the EUID != RUID, it resets the EUID back to
// RUID. This forces the script to run as root.
setuid(0);
// Replace ourselves with the sudo helper. This should terminate
// execution of this file, but if not we output the errno and then
// quit.
execvp("ruby", helperArgs);
fprintf(stderr, "sudo helper setuid-wrapper failed: %d\n", errno);
return 1;
}
@AlekSi
Copy link

AlekSi commented Apr 5, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment