Add support for passing pre-bound file descriptor

Add API support to accept pre-bound file descriptor from a
privileged process for creating a handle. Also clean-up
the handle without close the passing file descriptor. This
paves the path for privilege separation.

Bug:34361337
Test: Compiles
Change-Id: Iec6a1208e88ff70c52f08d5eddbce02e3dbf0b26
diff --git a/include/libnfnetlink/libnfnetlink.h b/include/libnfnetlink/libnfnetlink.h
index 1d8c49d..90c5672 100644
--- a/include/libnfnetlink/libnfnetlink.h
+++ b/include/libnfnetlink/libnfnetlink.h
@@ -18,6 +18,7 @@
 #include <sys/socket.h>	/* for sa_family_t */
 #include <linux/netlink.h>
 #include <libnfnetlink/linux_nfnetlink.h>
+#include <stdbool.h>
 
 #ifndef NETLINK_NETFILTER
 #define NETLINK_NETFILTER 12
@@ -66,11 +67,17 @@
 
 /* get a new library handle */
 extern struct nfnl_handle *nfnl_open(void);
+extern struct nfnl_handle *nfnl_open2(int fd, bool bind);
 extern int nfnl_close(struct nfnl_handle *);
+extern int nfnl_close2(struct nfnl_handle *);
 
 extern struct nfnl_subsys_handle *nfnl_subsys_open(struct nfnl_handle *, 
 						   u_int8_t, u_int8_t, 
 						   unsigned int);
+extern struct nfnl_subsys_handle *nfnl_subsys_open2(struct nfnl_handle *,
+						   u_int8_t, u_int8_t,
+						   unsigned int,
+						   bool bind);
 extern void nfnl_subsys_close(struct nfnl_subsys_handle *);
 
 /* set and unset sequence tracking */
diff --git a/src/libnfnetlink.c b/src/libnfnetlink.c
index 4b2bcd0..5759308 100644
--- a/src/libnfnetlink.c
+++ b/src/libnfnetlink.c
@@ -161,6 +161,18 @@
 	return 0;
 }
 
+static void recalc_subscriptions(struct nfnl_handle *nfnlh)
+{
+	int i;
+	u_int32_t new_subscriptions = nfnlh->subscriptions;
+
+	for (i = 0; i < NFNL_MAX_SUBSYS; i++)
+		new_subscriptions |= nfnlh->subsys[i].subscriptions;
+
+	nfnlh->local.nl_groups = new_subscriptions;
+	nfnlh->subscriptions = new_subscriptions;
+}
+
 /**
  * nfnl_open - open a nfnetlink handler
  *
@@ -173,16 +185,42 @@
 struct nfnl_handle *nfnl_open(void)
 {
 	struct nfnl_handle *nfnlh;
+	int fd;
+
+	fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
+	if (fd == -1)
+		return NULL;
+	nfnlh = nfnl_open2(fd, true);
+	if (nfnlh == NULL)
+		close(fd);
+	return nfnlh;
+}
+
+/**
+ * nfnl_open2 - open a nfnetlink handler
+ * @fd: passing file descriptor
+ * @bind:  indicate the passing fd needs to be binded or not
+ *
+ * This function creates a nfnetlink handler, this is required to establish
+ * a communication between the userspace and the nfnetlink system.
+ *
+ * On success, a valid address that points to a nfnl_handle structure
+ * is returned. On error, NULL is returned and errno is set approapiately.
+ */
+struct nfnl_handle *nfnl_open2(int fd, bool bind)
+{
+	struct nfnl_handle *nfnlh;
 	unsigned int addr_len;
 
+	if (fd < 0)
+		goto err;
+
 	nfnlh = malloc(sizeof(*nfnlh));
 	if (!nfnlh)
 		return NULL;
 
 	memset(nfnlh, 0, sizeof(*nfnlh));
-	nfnlh->fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_NETFILTER);
-	if (nfnlh->fd == -1)
-		goto err_free;
+	nfnlh->fd = fd;
 
 	nfnlh->local.nl_family = AF_NETLINK;
 	nfnlh->peer.nl_family = AF_NETLINK;
@@ -191,11 +229,11 @@
 	getsockname(nfnlh->fd, (struct sockaddr *)&nfnlh->local, &addr_len);
 	if (addr_len != sizeof(nfnlh->local)) {
 		errno = EINVAL;
-		goto err_close;
+		goto err_free;
 	}
 	if (nfnlh->local.nl_family != AF_NETLINK) {
 		errno = EINVAL;
-		goto err_close;
+		goto err_free;
 	}
 	nfnlh->seq = time(NULL);
 	nfnlh->rcv_buffer_size = NFNL_BUFFSIZE;
@@ -204,25 +242,28 @@
 	 * binding to pid '0' will default */
 
 	/* let us do the initial bind */
-	if (recalc_rebind_subscriptions(nfnlh) < 0)
-		goto err_close;
+	if (bind) {
+		if (recalc_rebind_subscriptions(nfnlh) < 0)
+			goto err_free;
+	} else {
+		recalc_subscriptions(nfnlh);
+	}
 
 	/* use getsockname to get the netlink pid that the kernel assigned us */
 	addr_len = sizeof(nfnlh->local);
 	getsockname(nfnlh->fd, (struct sockaddr *)&nfnlh->local, &addr_len);
 	if (addr_len != sizeof(nfnlh->local)) {
 		errno = EINVAL;
-		goto err_close;
+		goto err_free;
 	}
 	/* sequence tracking enabled by default */
 	nfnlh->flags |= NFNL_F_SEQTRACK_ENABLED;
 
 	return nfnlh;
 
-err_close:
-	close(nfnlh->fd);
 err_free:
 	free(nfnlh);
+err:
 	return NULL;
 }
 
@@ -276,7 +317,31 @@
 nfnl_subsys_open(struct nfnl_handle *nfnlh, u_int8_t subsys_id,
 		 u_int8_t cb_count, u_int32_t subscriptions)
 {
+	return nfnl_subsys_open2 (nfnlh, subsys_id, cb_count, subscriptions, true);
+}
+
+/**
+ * nfnl_subsys_open2 - open a netlink subsystem
+ * @nfnlh: libnfnetlink handle
+ * @subsys_id: which nfnetlink subsystem we are interested in
+ * @cb_count: number of callbacks that are used maximum.
+ * @subscriptions: netlink groups we want to be subscribed to
+ * @bind: indicate the passing fd needs to be binded or not
+ *
+ * This function creates a subsystem handler that contains the set of
+ * callbacks that handle certain types of messages coming from a netfilter
+ * subsystem. Initially the callback set is empty, you can register callbacks
+ * via nfnl_callback_register().
+ *
+ * On error, NULL is returned and errno is set appropiately. On success,
+ * a valid address that points to a nfnl_subsys_handle structure is returned.
+ */
+struct nfnl_subsys_handle *
+nfnl_subsys_open2(struct nfnl_handle *nfnlh, u_int8_t subsys_id,
+		 u_int8_t cb_count, u_int32_t subscriptions, bool bind)
+{
 	struct nfnl_subsys_handle *ssh;
+	int err = 0;
 
 	assert(nfnlh);
 
@@ -302,12 +367,15 @@
 
 	/* although now we have nfnl_join to subscribe to certain
 	 * groups, just keep this to ensure compatibility */
-	if (recalc_rebind_subscriptions(nfnlh) < 0) {
+	if (bind)
+		err = recalc_rebind_subscriptions(nfnlh);
+	else
+		recalc_subscriptions(nfnlh);
+	if (err < 0) {
 		free(ssh->cb);
 		ssh->cb = NULL;
 		return NULL;
 	}
-	
 	return ssh;
 }
 
@@ -338,17 +406,31 @@
  */
 int nfnl_close(struct nfnl_handle *nfnlh)
 {
-	int i, ret;
+	int ret;
+
+	assert(nfnlh);
+	ret = close(nfnlh->fd);
+	if (ret < 0)
+		return ret;
+	return nfnl_close2(nfnlh);
+}
+
+/**
+ * nfnl_close2 - close a nfnetlink handler but keep fd
+ * @nfnlh: nfnetlink handler
+ *
+ * This function closes the nfnetlink handler. On success, 0 is returned.
+ * On error, -1 is returned and errno is set appropiately.
+ */
+int nfnl_close2(struct nfnl_handle *nfnlh)
+{
+	int i;
 
 	assert(nfnlh);
 
 	for (i = 0; i < NFNL_MAX_SUBSYS; i++)
 		nfnl_subsys_close(&nfnlh->subsys[i]);
 
-	ret = close(nfnlh->fd);
-	if (ret < 0)
-		return ret;
-
 	free(nfnlh);
 
 	return 0;