#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define TMP "/tmp/sido.XXXXXXXXXXX"

static int tmp_create_and_open(char *template) {
    mode_t old_mode;
    int fd;
    
    old_mode = umask(077);
    fd = mkstemp(template);
    assert(fd >= 0);
    umask(old_mode);
    return fd;
}

static void fd_copy(int dst, int src, int limit) {
    char buf[1];
    int ret;

    for (;;) {
	if (limit-- <= 0) {
	    assert(0);
	}
	ret = read(src, buf, 1);
	if (ret == 1) {
	    ret = write(dst, buf, 1);
	    assert(ret == 1);
	} else {
	    break;
	}
    }
}

int main(int argc, char *argv[], char *envp[]) {
    char sig[] = TMP;
    char cmd[] = TMP;
    int ret, fd, fd2, i;

    if (argc < 4) {
	printf("usage: %s KEYRING SIGNATURE CMD\n"
	       "run CMD only if it is signed with SIGNATURE made with a key in KEYRING\n", argv[0]);
	exit(1);
    }
    
    fd = tmp_create_and_open(cmd);

    for (i = 3; i < argc; i++) {
	int len = strlen(argv[i]) + 1;
	ret = write(fd, argv[i], len);
	assert(ret == len);
    }
    ret = close(fd);
    assert(ret == 0);

    fd = tmp_create_and_open(sig);

    if (getuid() == 0) {
	char *uids, *gids;
	int uid, gid;
	
	uids = getenv("SUDO_UID");
	gids = getenv("SUDO_GID");

	assert(uids && gids);

	uid = atoi(uids);
	gid = atoi(gids);
	ret = seteuid(uid);
	assert(ret == 0);
	setegid(gid);
	assert(ret == 0);
    }
    
    fd2 = open(argv[2], O_RDONLY | O_NOCTTY | O_NOFOLLOW);
    assert(fd2);

    fd_copy(fd, fd2, 8192);

    ret = close(fd2);
    /* assert(ret == 0); */

    if (getuid() == 0) {
	ret = seteuid(0);
	assert(ret == 0);
	ret = setegid(0);
	assert(ret == 0);
    }
    
    ret = close(fd);
    assert(ret == 0);
    
    {
	char buf[512];

	ret = snprintf(buf, 512, "/usr/bin/gpgv --quiet --keyring %s %s %s", argv[1], sig, cmd);
	assert(ret < 512);

	ret = system(buf);
	if (ret != 0) {
	    fprintf(stderr, "Signature verification failed with %d\n", ret);
	    return 1;
	}
    }

    unlink(sig);
    unlink(cmd);
        
    ret = execve(argv[3], argv + 3, envp);
    fprintf(stderr, "execve failed with %d\n", ret);
    return 1;
}
