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