chiark / gitweb /
Upgrade licence to GPLv3+.
[tripe] / server / privsep.c
CommitLineData
388e0319
MW
1/* -*-c-*-
2 *
3 * Privilege separation communication protocol
4 *
5 * (c) 2008 Straylight/Edgeware
6 */
7
8/*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Trivial IP Encryption (TrIPE).
11 *
11ad66c2
MW
12 * TrIPE is free software: you can redistribute it and/or modify it under
13 * the terms of the GNU General Public License as published by the Free
14 * Software Foundation; either version 3 of the License, or (at your
15 * option) any later version.
388e0319 16 *
11ad66c2
MW
17 * TrIPE is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * for more details.
388e0319
MW
21 *
22 * You should have received a copy of the GNU General Public License
11ad66c2 23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
388e0319
MW
24 */
25
26/*----- Header files ------------------------------------------------------*/
27
28#include "tripe.h"
29#include "priv.h"
30
31/*----- Static variables --------------------------------------------------*/
32
33static pid_t kid = -1;
e533c005 34static sig sig_chld;
388e0319
MW
35
36/*----- Fetching a tunnel file descriptor ---------------------------------*/
37
38/* --- @ps_tunfd@ --- *
39 *
40 * Arguments: @const tunnel_ops *tops@ = pointer to tunnel operations
41 * @char **ifn@ = where to put the interface name
42 *
43 * Returns: The file descriptor, or @-1@ on error.
44 *
45 * Use: Fetches a file descriptor for a tunnel driver.
46 */
47
48int ps_tunfd(const tunnel_ops *tops, char **ifn)
49{
50 unsigned code;
51 ssize_t n;
52 dstr d = DSTR_INIT;
53 int fd;
54
55 if (pc_fd == -1) {
56 a_warn("PRIVSEP", "helper-died", A_END);
57 return (-1);
58 }
59 T( trace(T_PRIVSEP,
60 "privsep: requesting descriptor for %s tunnel",
61 tops->name); )
62 if (pc_putuint(PS_TUNRQ) || pc_putstring(tops->name)) {
63 a_warn("PRIVSEP", "helper-write-error", "?ERRNO", A_END);
64 goto lose;
65 }
66 for (;;) {
67 n = fdpass_recv(pc_fd, &fd, &code, sizeof(code));
68 if (n < 0) goto readlose;
69 if (n < sizeof(code)) {
70 a_warn("PRIVSEP", "helper-short-read", A_END);
71 goto lose;
72 }
73 switch (code) {
74 case PS_TUNFD:
75 if (fd == -1) {
76 a_warn("PRIVSEP", "no-fd-from-helper", A_END);
77 goto lose;
78 }
79 if (pc_getstring(&d)) { close(fd); goto readlose; }
80 *ifn = xstrdup(d.buf);
81 T( trace(T_PRIVSEP,
82 "privsep: received winning descriptor for %s",
83 *ifn); )
84 goto done;
85 case PS_TUNERR:
86 if (pc_geterr(&errno)) goto readlose;
87 T( trace(T_PRIVSEP, "privsep: helper lost: %s", strerror(errno)); )
88 fd = -1;
89 goto done;
90#ifndef NTRACE
91 case PS_TRACE:
92 if (pc_getuint(&code) || pc_getstring(&d)) goto readlose;
93 trace(code, "%s", d.buf);
94 DRESET(&d);
95 break;
96#endif
97 case PS_WARN:
98 if (pc_getstring(&d)) goto readlose;
99 a_warn("*%s", d.buf, A_END);
100 DRESET(&d);
101 break;
102 default:
103 a_warn("PRIVSEP", "unknown-response-code", "%u", code, A_END);
104 goto lose;
105 }
106 }
107done:
108 dstr_destroy(&d);
109 return (fd);
110
111readlose:
112 a_warn("PRIVSEP", "helper-read-error", "?ERRNO", A_END);
113lose:
114 dstr_destroy(&d);
115 close(pc_fd);
116 pc_fd = -1;
117 return (-1);
118}
119
120/*----- Main code ---------------------------------------------------------*/
121
122/* --- @reap@ --- *
123 *
124 * Arguments: @int sig@ = signal number (always @SIGCHLD@; ignored)
125 *
126 * Returns: ---
127 *
128 * Use: Notices and reports child process death.
129 */
130
e533c005 131static void reap(int sig, void *p)
388e0319
MW
132{
133 pid_t k;
134 int st;
135
136 for (;;) {
137 k = waitpid(-1, &st, WNOHANG);
138 if (k < 0) {
139 switch (errno) {
140 case EINTR:
141 break;
142 default:
143 a_warn("SERVER", "waitpid-error", "?ERRNO", A_END);
144 case ECHILD:
145 return;
146 }
147 }
148 if (!k)
149 return;
150 if (k == kid) {
151 if (WIFEXITED(st))
152 a_warn("PRIVSEP", "child-exited", "%d", WEXITSTATUS(st), A_END);
153 else if (WIFSIGNALED(st))
154 a_warn("PRIVSEP", "child-killed", "%d", WTERMSIG(st), A_END);
155 else
156 a_warn("PRIVSEP", "child-died", "%d", st, A_END);
157 kid = -1;
158 }
159 }
160}
161
162/* --- @ps_split@ --- *
163 *
164 * Arguments: @int detachp@ = whether to detach the child from its terminal
165 *
166 * Returns: ---
167 *
168 * Use: Separates off the privileged tunnel-opening service from the
169 * rest of the server.
170 */
171
172void ps_split(int detachp)
173{
174 pid_t kid;
175 int fd[2];
b9537f3b 176 mdup_fd md[1];
388e0319
MW
177 const char *helper;
178
179 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
180 die(EXIT_FAILURE,
181 "failed to create socket pair for privilege separation: %s",
182 strerror(errno));
183 }
184 helper = getenv("TRIPE_PRIVHELPER");
185 if (!helper) helper = PRIVSEP_HELPER;
186 fdflags(fd[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
187 fdflags(fd[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
e533c005 188 sig_add(&sig_chld, SIGCHLD, reap, 0);
388e0319
MW
189 kid = fork();
190 if (kid == 0) {
191 signal(SIGCHLD, SIG_DFL);
192 if (detachp) detachtty();
b9537f3b
MW
193 close(fd[1]);
194 md[0].cur = fd[0]; md[0].want = STDIN_FILENO;
195 if (mdup(md, 1)) goto lose;
388e0319
MW
196 execl(helper, helper, (char *)0);
197 lose:
198 fprintf(stderr, "helper: failed to run helper: %s\n", strerror(errno));
199 _exit(127);
200 }
201 T( trace(T_PRIVSEP, "privsep: forked child successfully"); )
202 close(fd[0]);
203 pc_fd = fd[1];
204}
205
206/* --- @ps_quit@ --- *
207 *
208 * Arguments: ---
209 *
210 * Returns: ---
211 *
212 * Use: Detaches from the helper process.
213 */
214
215void ps_quit(void) { if (pc_fd != -1) close(pc_fd); }
216
217/*----- That's all, folks -------------------------------------------------*/