/* $OpenBSD: getpeereid_test.c,v 1.1 2006/10/23 15:18:47 espie Exp $ */
/* Written by Marc Espie in 2006 */
/* Public domain */
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <err.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>

char path[1024];
char *dir;

char *
check_id(int fd)
{
	uid_t sockuid, myuid;
	gid_t sockgid, mygid;
	static char problem[1024];

	if (getpeereid(fd, &sockuid, &sockgid) == -1) {
		snprintf(problem, sizeof problem, "getpeereid: %s", 
		    strerror(errno));
		return problem;
	}
	myuid = geteuid();
	mygid = getgid();
	if (myuid != sockuid) {
		snprintf(problem, sizeof problem, "uid discrepancy %ld vs %ld",
			(long)myuid, (long)sockuid);
		return problem;
	}
	if (mygid != sockgid) {
		snprintf(problem, sizeof problem, "gid discrepancy %ld vs %ld",
			(long)mygid, (long)sockgid);
		return problem;
	}
	return NULL;
}

void
client(struct sockaddr_un *sun)
{
	int s;
	int i;
	int r;
	char *problem;

	s = socket(AF_UNIX, SOCK_STREAM, 0);
	if (s == -1)
		err(1, "Bad socket");

	/* XXX make sure the server started alright */
	for (i = 0; i < 10; i++) {
		r = connect(s, (struct sockaddr *)sun, sizeof(*sun));
		if (r == 0) {
			problem = check_id(s);
			if (problem)
				errx(1, problem);
			exit(0);
		}
		sleep(5);
	}
	errx(1, "Could not connect after 10 tries");
}


void
server(struct sockaddr_un *sun)
{
	int s, fd;
	struct sockaddr_storage client_addr;
	socklen_t client_len;
	char *problem;

	s = socket(AF_UNIX, SOCK_STREAM, 0);
	if (s == -1)
		err(1, "Bad socket");

	if (bind(s, (struct sockaddr *)sun, sizeof(*sun)) != 0)
		err(1, "bind");
	if (listen(s, 5) != 0) {
		unlink(path);
		rmdir(dir);
		err(1, "listen");
	}
	fd = accept(s, (struct sockaddr *)&client_addr, &client_len);
	if (fd == -1) {
		unlink(path);
		rmdir(dir);
		err(1, "accept");
	}
	problem = check_id(fd);
	if (problem)  {
		unlink(path);
		rmdir(dir);
		errx(1, problem);
	}
	unlink(path);
	rmdir(dir);
}



int
main()
{
	pid_t pid;
	struct sockaddr_un sun;
	char dir_template[] = "/tmp/peer.XXXXXX";

	dir = mkdtemp(dir_template);
	if (!dir)
		err(1, "mkdtemp");
	snprintf(path, sizeof path, "%s/%s", dir, "socket");

	memset(&sun, 0, sizeof(struct sockaddr_un));
	if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
	    sizeof(sun.sun_path))
		errx(1, "Memory error");
	sun.sun_len = sizeof(sun);
	sun.sun_family = AF_UNIX;

	/* let's make those two rendez-vous, a bit artificial */
	pid = fork();
	if (pid == -1)
		err(1, "can't fork");
	if (pid == 0) {
		client(&sun);
		exit(0);
	} else {
		int status;

		server(&sun);
		waitpid(pid, &status, 0);
		if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
			printf("getpeereid test okay\n");
			exit(0);
		} else
			errx(1, "Problem with child\n");
	}
}