summaryrefslogtreecommitdiffstats
path: root/src/utils/kxdpgun/popenve.c
blob: 116a2c2f9349507162149a09425d959e6948906f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*  Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>

#include "utils/kxdpgun/popenve.h"

#ifdef ENABLE_CAP_NG
#include <cap-ng.h>

static void drop_capabilities(void)
{
	/* Drop all capabilities. */
	if (capng_have_capability(CAPNG_EFFECTIVE, CAP_SETPCAP)) {
		capng_clear(CAPNG_SELECT_BOTH);
		capng_apply(CAPNG_SELECT_BOTH);
	}
}
#else /* ENABLE_CAP_NG */
static void drop_capabilities(void) {  }
#endif

int kpopenvef(const char *binfile, char *const args[], char *const env[], bool drop_cap)
{
	int pipefds[2];
	if (pipe(pipefds) < 0) {
		return -errno;
	}
	if (fcntl(pipefds[0], F_SETFD, FD_CLOEXEC) < 0) {
		int fcntlerrno = errno;
		close(pipefds[0]);
		close(pipefds[1]);
		return -fcntlerrno;
	}

	pid_t forkpid = fork();
	if (forkpid < 0) {
		int forkerrno = errno;
		close(pipefds[0]);
		close(pipefds[1]);
		return -forkerrno;
	}

	if (forkpid == 0) {
dup_stdout:
		if (dup2(pipefds[1], STDOUT_FILENO) < 0) {
			if (errno == EINTR) {
				goto dup_stdout;
			}
			perror("dup_stdout");
			close(pipefds[0]);
			close(pipefds[1]);
			exit(EXIT_FAILURE);
		}
		close(pipefds[1]);

		if (drop_cap) {
			drop_capabilities();
		}

		execve(binfile, args, env);
		perror("execve");
		exit(EXIT_FAILURE);
	}

	close(pipefds[1]);
	return pipefds[0];
}

FILE *kpopenve(const char *binfile, char *const args[], char *const env[], bool drop_cap)
{
	int p = kpopenvef(binfile, args, env, drop_cap);
	if (p < 0) {
		errno = -p;
		return NULL;
	}

	FILE *res = fdopen(p, "r");
	if (res == NULL) {
		int fdoerrno = errno;
		close(p);
		errno = fdoerrno;
	}
	return res;
}