server/admin.c: Remove spurious `ping' in usage message.
[tripe] / server / privsep.c
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 *
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.
16 *
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.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
24 */
25
26 /*----- Header files ------------------------------------------------------*/
27
28 #include "tripe.h"
29 #include "priv.h"
30
31 /*----- Static variables --------------------------------------------------*/
32
33 static pid_t kid = -1;
34 static sig sig_chld;
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
48 int 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 }
107 done:
108 dstr_destroy(&d);
109 return (fd);
110
111 readlose:
112 a_warn("PRIVSEP", "helper-read-error", "?ERRNO", A_END);
113 lose:
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
131 static void reap(int sig, void *p)
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: Zero on success, @-1@ on failure.
167 *
168 * Use: Separates off the privileged tunnel-opening service from the
169 * rest of the server.
170 */
171
172 int ps_split(int detachp)
173 {
174 pid_t kid;
175 int fd[2];
176 mdup_fd md[1];
177 const char *helper;
178
179 if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
180 a_warn("PRIVSEP", "socketpair-create-failed", "?ERRNO", A_END);
181 return (-1);
182 }
183 helper = getenv("TRIPE_PRIVHELPER");
184 if (!helper) helper = PRIVSEP_HELPER;
185 fdflags(fd[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
186 fdflags(fd[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
187 sig_add(&sig_chld, SIGCHLD, reap, 0);
188 kid = fork();
189 if (kid == 0) {
190 signal(SIGCHLD, SIG_DFL);
191 if (detachp) detachtty();
192 close(fd[1]);
193 md[0].cur = fd[0]; md[0].want = STDIN_FILENO;
194 if (mdup(md, 1)) goto lose;
195 execl(helper, helper, (char *)0);
196 lose:
197 fprintf(stderr, "helper: failed to run helper: %s\n", strerror(errno));
198 _exit(127);
199 }
200 T( trace(T_PRIVSEP, "privsep: forked child successfully"); )
201 close(fd[0]);
202 pc_fd = fd[1];
203 return (0);
204 }
205
206 /* --- @ps_quit@ --- *
207 *
208 * Arguments: ---
209 *
210 * Returns: ---
211 *
212 * Use: Detaches from the helper process.
213 */
214
215 void ps_quit(void) { if (pc_fd != -1) close(pc_fd); }
216
217 /*----- That's all, folks -------------------------------------------------*/