]>
Commit | Line | Data |
---|---|---|
a8f11321 LP |
1 | /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ |
2 | ||
3 | /*** | |
4 | This file is part of systemd. | |
5 | ||
6 | Copyright 2010 Lennart Poettering | |
7 | ||
8 | systemd is free software; you can redistribute it and/or modify it | |
5430f7f2 LP |
9 | under the terms of the GNU Lesser General Public License as published by |
10 | the Free Software Foundation; either version 2.1 of the License, or | |
a8f11321 LP |
11 | (at your option) any later version. |
12 | ||
13 | systemd is distributed in the hope that it will be useful, but | |
14 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
5430f7f2 | 16 | Lesser General Public License for more details. |
a8f11321 | 17 | |
5430f7f2 | 18 | You should have received a copy of the GNU Lesser General Public License |
a8f11321 LP |
19 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
20 | ***/ | |
21 | ||
22 | #include <sys/socket.h> | |
23 | #include <sys/un.h> | |
24 | #include <sys/types.h> | |
25 | #include <fcntl.h> | |
26 | #include <unistd.h> | |
27 | #include <string.h> | |
28 | #include <errno.h> | |
29 | #include <sys/epoll.h> | |
30 | #include <stddef.h> | |
31 | ||
32 | #include "log.h" | |
33 | #include "util.h" | |
34 | #include "socket-util.h" | |
35 | ||
36 | #define BUFFER_SIZE (64*1024) | |
37 | #define EXTRA_SIZE 16 | |
38 | ||
39 | static bool initial_nul = false; | |
40 | static bool auth_over = false; | |
41 | ||
42 | static void format_uid(char *buf, size_t l) { | |
43 | char text[20 + 1]; /* enough space for a 64bit integer plus NUL */ | |
44 | unsigned j; | |
45 | ||
46 | assert(l > 0); | |
47 | ||
48 | snprintf(text, sizeof(text)-1, "%llu", (unsigned long long) geteuid()); | |
49 | text[sizeof(text)-1] = 0; | |
50 | ||
51 | memset(buf, 0, l); | |
52 | ||
53 | for (j = 0; text[j] && j*2+2 < l; j++) { | |
54 | buf[j*2] = hexchar(text[j] >> 4); | |
55 | buf[j*2+1] = hexchar(text[j] & 0xF); | |
56 | } | |
57 | ||
58 | buf[j*2] = 0; | |
59 | } | |
60 | ||
61 | static size_t patch_in_line(char *line, size_t l, size_t left) { | |
62 | size_t r; | |
63 | ||
64 | if (line[0] == 0 && !initial_nul) { | |
65 | initial_nul = true; | |
66 | line += 1; | |
67 | l -= 1; | |
68 | r = 1; | |
69 | } else | |
70 | r = 0; | |
71 | ||
72 | if (l == 5 && strncmp(line, "BEGIN", 5) == 0) { | |
73 | r += l; | |
74 | auth_over = true; | |
75 | ||
76 | } else if (l == 17 && strncmp(line, "NEGOTIATE_UNIX_FD", 17) == 0) { | |
77 | memmove(line + 13, line + 17, left); | |
78 | memcpy(line, "NEGOTIATE_NOP", 13); | |
79 | r += 13; | |
80 | ||
81 | } else if (l >= 14 && strncmp(line, "AUTH EXTERNAL ", 14) == 0) { | |
82 | char uid[20*2 + 1]; | |
83 | size_t len; | |
84 | ||
85 | format_uid(uid, sizeof(uid)); | |
86 | len = strlen(uid); | |
87 | assert(len <= EXTRA_SIZE); | |
88 | ||
89 | memmove(line + 14 + len, line + l, left); | |
90 | memcpy(line + 14, uid, len); | |
91 | ||
92 | r += 14 + len; | |
93 | } else | |
94 | r += l; | |
95 | ||
96 | return r; | |
97 | } | |
98 | ||
99 | static size_t patch_in_buffer(char* in_buffer, size_t *in_buffer_full) { | |
100 | size_t i, good = 0; | |
101 | ||
102 | if (*in_buffer_full <= 0) | |
103 | return *in_buffer_full; | |
104 | ||
105 | /* If authentication is done, we don't touch anything anymore */ | |
106 | if (auth_over) | |
107 | return *in_buffer_full; | |
108 | ||
109 | if (*in_buffer_full < 2) | |
110 | return 0; | |
111 | ||
112 | for (i = 0; i <= *in_buffer_full - 2; i ++) { | |
113 | ||
114 | /* Fully lines can be send on */ | |
115 | if (in_buffer[i] == '\r' && in_buffer[i+1] == '\n') { | |
116 | if (i > good) { | |
117 | size_t old_length, new_length; | |
118 | ||
119 | old_length = i - good; | |
120 | new_length = patch_in_line(in_buffer+good, old_length, *in_buffer_full - i); | |
121 | *in_buffer_full = *in_buffer_full + new_length - old_length; | |
122 | ||
123 | good += new_length + 2; | |
124 | ||
125 | } else | |
126 | good = i+2; | |
127 | } | |
128 | ||
129 | if (auth_over) | |
130 | break; | |
131 | } | |
132 | ||
133 | return good; | |
134 | } | |
135 | ||
136 | int main(int argc, char *argv[]) { | |
137 | int r = EXIT_FAILURE, fd = -1, ep = -1; | |
138 | union sockaddr_union sa; | |
139 | char in_buffer[BUFFER_SIZE+EXTRA_SIZE], out_buffer[BUFFER_SIZE+EXTRA_SIZE]; | |
140 | size_t in_buffer_full = 0, out_buffer_full = 0; | |
141 | struct epoll_event stdin_ev, stdout_ev, fd_ev; | |
142 | bool stdin_readable = false, stdout_writable = false, fd_readable = false, fd_writable = false; | |
143 | bool stdin_rhup = false, stdout_whup = false, fd_rhup = false, fd_whup = false; | |
144 | ||
145 | if (argc > 1) { | |
146 | log_error("This program takes no argument."); | |
147 | return EXIT_FAILURE; | |
148 | } | |
149 | ||
4cfa2c99 | 150 | log_set_target(LOG_TARGET_JOURNAL_OR_KMSG); |
a8f11321 LP |
151 | log_parse_environment(); |
152 | log_open(); | |
153 | ||
154 | if ((fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) { | |
155 | log_error("Failed to create socket: %s", strerror(errno)); | |
156 | goto finish; | |
157 | } | |
158 | ||
159 | zero(sa); | |
160 | sa.un.sun_family = AF_UNIX; | |
db019b8d | 161 | strncpy(sa.un.sun_path, "/run/dbus/system_bus_socket", sizeof(sa.un.sun_path)); |
a8f11321 | 162 | |
3338b959 | 163 | if (connect(fd, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(sa.un.sun_path)) < 0) { |
a8f11321 LP |
164 | log_error("Failed to connect: %m"); |
165 | goto finish; | |
166 | } | |
167 | ||
168 | fd_nonblock(STDIN_FILENO, 1); | |
169 | fd_nonblock(STDOUT_FILENO, 1); | |
170 | ||
171 | if ((ep = epoll_create1(EPOLL_CLOEXEC)) < 0) { | |
172 | log_error("Failed to create epoll: %m"); | |
173 | goto finish; | |
174 | } | |
175 | ||
176 | zero(stdin_ev); | |
177 | stdin_ev.events = EPOLLIN|EPOLLET; | |
178 | stdin_ev.data.fd = STDIN_FILENO; | |
179 | ||
180 | zero(stdout_ev); | |
181 | stdout_ev.events = EPOLLOUT|EPOLLET; | |
182 | stdout_ev.data.fd = STDOUT_FILENO; | |
183 | ||
184 | zero(fd_ev); | |
185 | fd_ev.events = EPOLLIN|EPOLLOUT|EPOLLET; | |
186 | fd_ev.data.fd = fd; | |
187 | ||
188 | if (epoll_ctl(ep, EPOLL_CTL_ADD, STDIN_FILENO, &stdin_ev) < 0 || | |
189 | epoll_ctl(ep, EPOLL_CTL_ADD, STDOUT_FILENO, &stdout_ev) < 0 || | |
190 | epoll_ctl(ep, EPOLL_CTL_ADD, fd, &fd_ev) < 0) { | |
191 | log_error("Failed to regiser fds in epoll: %m"); | |
192 | goto finish; | |
193 | } | |
194 | ||
195 | do { | |
196 | struct epoll_event ev[16]; | |
197 | ssize_t k; | |
198 | int i, nfds; | |
199 | ||
200 | if ((nfds = epoll_wait(ep, ev, ELEMENTSOF(ev), -1)) < 0) { | |
201 | ||
202 | if (errno == EINTR || errno == EAGAIN) | |
203 | continue; | |
204 | ||
205 | log_error("epoll_wait(): %m"); | |
206 | goto finish; | |
207 | } | |
208 | ||
209 | assert(nfds >= 1); | |
210 | ||
211 | for (i = 0; i < nfds; i++) { | |
212 | if (ev[i].data.fd == STDIN_FILENO) { | |
213 | ||
214 | if (!stdin_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN))) | |
215 | stdin_readable = true; | |
216 | ||
217 | } else if (ev[i].data.fd == STDOUT_FILENO) { | |
218 | ||
219 | if (ev[i].events & EPOLLHUP) { | |
220 | stdout_writable = false; | |
221 | stdout_whup = true; | |
222 | } | |
223 | ||
224 | if (!stdout_whup && (ev[i].events & EPOLLOUT)) | |
225 | stdout_writable = true; | |
226 | ||
227 | } else if (ev[i].data.fd == fd) { | |
228 | ||
229 | if (ev[i].events & EPOLLHUP) { | |
230 | fd_writable = false; | |
231 | fd_whup = true; | |
232 | } | |
233 | ||
234 | if (!fd_rhup && (ev[i].events & (EPOLLHUP|EPOLLIN))) | |
235 | fd_readable = true; | |
236 | ||
237 | if (!fd_whup && (ev[i].events & EPOLLOUT)) | |
238 | fd_writable = true; | |
239 | } | |
240 | } | |
241 | ||
242 | while ((stdin_readable && in_buffer_full <= 0) || | |
243 | (fd_writable && patch_in_buffer(in_buffer, &in_buffer_full) > 0) || | |
244 | (fd_readable && out_buffer_full <= 0) || | |
245 | (stdout_writable && out_buffer_full > 0)) { | |
246 | ||
247 | size_t in_buffer_good = 0; | |
248 | ||
249 | if (stdin_readable && in_buffer_full < BUFFER_SIZE) { | |
250 | ||
251 | if ((k = read(STDIN_FILENO, in_buffer + in_buffer_full, BUFFER_SIZE - in_buffer_full)) < 0) { | |
252 | ||
253 | if (errno == EAGAIN) | |
254 | stdin_readable = false; | |
255 | else if (errno == EPIPE || errno == ECONNRESET) | |
256 | k = 0; | |
257 | else { | |
258 | log_error("read(): %m"); | |
259 | goto finish; | |
260 | } | |
261 | } else | |
262 | in_buffer_full += (size_t) k; | |
263 | ||
264 | if (k == 0) { | |
265 | stdin_rhup = true; | |
266 | stdin_readable = false; | |
267 | shutdown(STDIN_FILENO, SHUT_RD); | |
268 | close_nointr_nofail(STDIN_FILENO); | |
269 | } | |
270 | } | |
271 | ||
272 | in_buffer_good = patch_in_buffer(in_buffer, &in_buffer_full); | |
273 | ||
274 | if (fd_writable && in_buffer_good > 0) { | |
275 | ||
276 | if ((k = write(fd, in_buffer, in_buffer_good)) < 0) { | |
277 | ||
278 | if (errno == EAGAIN) | |
279 | fd_writable = false; | |
280 | else if (errno == EPIPE || errno == ECONNRESET) { | |
281 | fd_whup = true; | |
282 | fd_writable = false; | |
283 | shutdown(fd, SHUT_WR); | |
284 | } else { | |
285 | log_error("write(): %m"); | |
286 | goto finish; | |
287 | } | |
288 | ||
289 | } else { | |
290 | assert(in_buffer_full >= (size_t) k); | |
291 | memmove(in_buffer, in_buffer + k, in_buffer_full - k); | |
292 | in_buffer_full -= k; | |
293 | } | |
294 | } | |
295 | ||
296 | if (fd_readable && out_buffer_full < BUFFER_SIZE) { | |
297 | ||
298 | if ((k = read(fd, out_buffer + out_buffer_full, BUFFER_SIZE - out_buffer_full)) < 0) { | |
299 | ||
300 | if (errno == EAGAIN) | |
301 | fd_readable = false; | |
302 | else if (errno == EPIPE || errno == ECONNRESET) | |
303 | k = 0; | |
304 | else { | |
305 | log_error("read(): %m"); | |
306 | goto finish; | |
307 | } | |
308 | } else | |
309 | out_buffer_full += (size_t) k; | |
310 | ||
311 | if (k == 0) { | |
312 | fd_rhup = true; | |
313 | fd_readable = false; | |
314 | shutdown(fd, SHUT_RD); | |
315 | } | |
316 | } | |
317 | ||
318 | if (stdout_writable && out_buffer_full > 0) { | |
319 | ||
320 | if ((k = write(STDOUT_FILENO, out_buffer, out_buffer_full)) < 0) { | |
321 | ||
322 | if (errno == EAGAIN) | |
323 | stdout_writable = false; | |
324 | else if (errno == EPIPE || errno == ECONNRESET) { | |
325 | stdout_whup = true; | |
326 | stdout_writable = false; | |
327 | shutdown(STDOUT_FILENO, SHUT_WR); | |
328 | close_nointr(STDOUT_FILENO); | |
329 | } else { | |
330 | log_error("write(): %m"); | |
331 | goto finish; | |
332 | } | |
333 | ||
334 | } else { | |
335 | assert(out_buffer_full >= (size_t) k); | |
336 | memmove(out_buffer, out_buffer + k, out_buffer_full - k); | |
337 | out_buffer_full -= k; | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
342 | if (stdin_rhup && in_buffer_full <= 0 && !fd_whup) { | |
343 | fd_whup = true; | |
344 | fd_writable = false; | |
345 | shutdown(fd, SHUT_WR); | |
346 | } | |
347 | ||
348 | if (fd_rhup && out_buffer_full <= 0 && !stdout_whup) { | |
349 | stdout_whup = true; | |
350 | stdout_writable = false; | |
351 | shutdown(STDOUT_FILENO, SHUT_WR); | |
352 | close_nointr(STDOUT_FILENO); | |
353 | } | |
354 | ||
355 | } while (!stdout_whup || !fd_whup); | |
356 | ||
357 | r = EXIT_SUCCESS; | |
358 | ||
359 | finish: | |
360 | if (fd >= 0) | |
361 | close_nointr_nofail(fd); | |
362 | ||
363 | if (ep >= 0) | |
364 | close_nointr_nofail(ep); | |
365 | ||
366 | return r; | |
367 | } |