/* SccsId is @(#)customize-linux-capNetBind.c	58.10 04/06/05
 *
 * Copyright (C) 1999-2003 Cincom Systems, Inc.
 * Copyright (C) 2002 Cees de Groot <cg@cdegroot.com>.
 * All rights reserved.
 *
 * This is a version of customize.c that demonstrates how to add the capability
 * to bind to low-numbered ports on linux, for example to implement a web
 * server.  See setCapability below.  This code is kindly contributed by
 * Cees de Groot.
 *
 * Capability hack Copyright (C) 2002 Cees de Groot <cg@cdegroot.com>.
 * All rights reserved.
 *
 * Permission to use and redistribute this code within the context of
 * Cincom licensing is hereby granted provided that this note and the
 * above copyright are left intact.
 */
#include "userprim.h"

static char sccsID[] = "@(#) VisualWorks(R) 7.3a, customize-linux-capNetBind.c 58.10";
char *versionString = "VisualWorks(R) 7.3a, Linux CAP_NET_BIND";


char *resourceClassName = "St80"; /* class name used to look up X resources */

static int setCapabilities(void);

int
#if OE_IS_UNICODE
wmain(int argc, wchar_t *argv[])
#else
main(int argc, char *argv[])
#endif
{
#if LOADABLE_GUI
	extern unsigned int guiSubsystemLinked;
	if (guiSubsystemLinked)
		showHerald = TRUE;
#endif
	/* Calling the C++  _main before main allows some platforms
	 * (currently confirmed for HPUX 11) to load C++ dynamic load libraries.
	 * If CPLUSPLUS is defined we call _main.
	 */
#ifdef CPLUSPLUS
	_main();
#endif
	if (setCapabilities())
		return 1;
	return oeMain(argc, argv, 0);
}

/*
 * UPInitLinkRegistry
 *
 * All statically linked procedures accessible to
 * the Objectkit Smalltalk C Programming kit
 * must be defined in this function using
 * UPRegisterSymbolAndHandle(char *, void *).
 */
void
UPInitLinkRegistry()
{
}

char* UPinstall() { return "No User Primitives"; }


/*
 * This is what it's all about. Here we set the capability to open
 * low-numbered ports, and then drop root privs to a hard-coded uid.
 * You either need to start it as root, or make the image setuid().
 *
 * The result is that you run as the hard-coded user, but with the
 * ability to bind to port 80 and other interesting places.
 */
#include <errno.h>
#include <malloc.h>
#include <unistd.h>
#include <linux/capability.h>
#include <sys/prctl.h>

int
setCapabilities()
{
	cap_user_header_t header;
	cap_user_data_t data;

	/* Keep capabilities across setuid() */
	if (prctl(PR_SET_KEEPCAPS, 1)) {
		perror("prctl");
		return errno;
	}

	/* Set CAP_NET_BIND_SERVICE */
	header = malloc(sizeof(*header));
	data = malloc(sizeof(*header));
	header->version = _LINUX_CAPABILITY_VERSION;
	header->pid = 0;

	if (capget(header, data)) {
		perror("capget 1");
		return errno;
	}
	data->inheritable |= 1 << CAP_NET_BIND_SERVICE;
	data->effective |= 1 << CAP_NET_BIND_SERVICE;
	data->permitted |= 1 << CAP_NET_BIND_SERVICE;
	if (capset(header, data)) {
		perror("capset 1");
		return errno;
	}

	/* Drop all privs */
	setgid(1006);
	setuid(842);

	/* Enable just the capability we want */
	data->effective = 1 << CAP_NET_BIND_SERVICE;
	data->permitted = 1 << CAP_NET_BIND_SERVICE;
	if (capset(header, data)) {
		perror("capset 1");
		return errno;
	}

#if 0
	// for checking
	if (capget(header, data)) { perror("capset 2"); return errno; }
	printf("inh: %lX\n", data->inheritable);
	printf("eff: %lX\n", data->effective);
	printf("prm: %lX\n", data->permitted);
#endif

	return 0;
}
