1 | /* |
---|
2 | * Protocol v2, server implementation. |
---|
3 | * |
---|
4 | * This is the server implementation of the new v2 protocol. |
---|
5 | * |
---|
6 | * Written by Russ Allbery <rra@stanford.edu> |
---|
7 | * Based on work by Anton Ushakov |
---|
8 | * Copyright 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 |
---|
9 | * Board of Trustees, Leland Stanford Jr. University |
---|
10 | * |
---|
11 | * See LICENSE for licensing terms. |
---|
12 | */ |
---|
13 | |
---|
14 | #include <config.h> |
---|
15 | #include <portable/system.h> |
---|
16 | #include <portable/gssapi.h> |
---|
17 | #include <portable/socket.h> |
---|
18 | #include <portable/uio.h> |
---|
19 | |
---|
20 | #include <server/internal.h> |
---|
21 | #include <util/util.h> |
---|
22 | |
---|
23 | |
---|
24 | /* |
---|
25 | * Given the client struct and the stream number the data is from, send a |
---|
26 | * protocol v2 output token to the client containing the data stored in the |
---|
27 | * buffer in the client struct. Returns true on success, false on failure |
---|
28 | * (and logs a message on failure). |
---|
29 | */ |
---|
30 | bool |
---|
31 | server_v2_send_output(struct client *client, int stream) |
---|
32 | { |
---|
33 | gss_buffer_desc token; |
---|
34 | char *p; |
---|
35 | OM_uint32 tmp, major, minor; |
---|
36 | int status; |
---|
37 | |
---|
38 | /* Allocate room for the total message. */ |
---|
39 | token.length = 1 + 1 + 1 + 4 + client->outlen; |
---|
40 | token.value = xmalloc(token.length); |
---|
41 | |
---|
42 | /* |
---|
43 | * Fill in the header (version, type, stream, length, and data) and then |
---|
44 | * the data. |
---|
45 | */ |
---|
46 | p = token.value; |
---|
47 | *p = 2; |
---|
48 | p++; |
---|
49 | *p = MESSAGE_OUTPUT; |
---|
50 | p++; |
---|
51 | *p = stream; |
---|
52 | p++; |
---|
53 | tmp = htonl(client->outlen); |
---|
54 | memcpy(p, &tmp, 4); |
---|
55 | p += 4; |
---|
56 | memcpy(p, client->output, client->outlen); |
---|
57 | |
---|
58 | /* Send the token. */ |
---|
59 | status = token_send_priv(client->fd, client->context, |
---|
60 | TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor); |
---|
61 | if (status != TOKEN_OK) { |
---|
62 | warn_token("sending output token", status, major, minor); |
---|
63 | free(token.value); |
---|
64 | client->fatal = true; |
---|
65 | return false; |
---|
66 | } |
---|
67 | free(token.value); |
---|
68 | return true; |
---|
69 | } |
---|
70 | |
---|
71 | |
---|
72 | /* |
---|
73 | * Given the client struct and the exit status, send a protocol v2 status |
---|
74 | * token to the client. Returns true on success, false on failure (and logs a |
---|
75 | * message on failure). |
---|
76 | */ |
---|
77 | bool |
---|
78 | server_v2_send_status(struct client *client, int exit_status) |
---|
79 | { |
---|
80 | gss_buffer_desc token; |
---|
81 | char buffer[1 + 1 + 1]; |
---|
82 | OM_uint32 major, minor; |
---|
83 | int status; |
---|
84 | |
---|
85 | /* Build the status token. */ |
---|
86 | token.length = 1 + 1 + 1; |
---|
87 | token.value = &buffer; |
---|
88 | buffer[0] = 2; |
---|
89 | buffer[1] = MESSAGE_STATUS; |
---|
90 | buffer[2] = exit_status; |
---|
91 | |
---|
92 | /* Send the token. */ |
---|
93 | status = token_send_priv(client->fd, client->context, |
---|
94 | TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor); |
---|
95 | if (status != TOKEN_OK) { |
---|
96 | warn_token("sending status token", status, major, minor); |
---|
97 | client->fatal = true; |
---|
98 | return false; |
---|
99 | } |
---|
100 | return true; |
---|
101 | } |
---|
102 | |
---|
103 | |
---|
104 | /* |
---|
105 | * Given the client struct, an error code, and an error message, send a |
---|
106 | * protocol v2 error token to the client. Returns true on success, false on |
---|
107 | * failure (and logs a message on failure). |
---|
108 | */ |
---|
109 | bool |
---|
110 | server_v2_send_error(struct client *client, enum error_codes code, |
---|
111 | const char *message) |
---|
112 | { |
---|
113 | gss_buffer_desc token; |
---|
114 | char *p; |
---|
115 | OM_uint32 tmp, major, minor; |
---|
116 | int status; |
---|
117 | |
---|
118 | /* Build the error token. */ |
---|
119 | token.length = 1 + 1 + 4 + 4 + strlen(message); |
---|
120 | token.value = xmalloc(token.length); |
---|
121 | p = token.value; |
---|
122 | *p = 2; |
---|
123 | p++; |
---|
124 | *p = MESSAGE_ERROR; |
---|
125 | p++; |
---|
126 | tmp = htonl(code); |
---|
127 | memcpy(p, &tmp, 4); |
---|
128 | p += 4; |
---|
129 | tmp = htonl(strlen(message)); |
---|
130 | memcpy(p, &tmp, 4); |
---|
131 | p += 4; |
---|
132 | memcpy(p, message, strlen(message)); |
---|
133 | |
---|
134 | /* Send the token. */ |
---|
135 | status = token_send_priv(client->fd, client->context, |
---|
136 | TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor); |
---|
137 | if (status != TOKEN_OK) { |
---|
138 | warn_token("sending error token", status, major, minor); |
---|
139 | free(token.value); |
---|
140 | client->fatal = true; |
---|
141 | return false; |
---|
142 | } |
---|
143 | free(token.value); |
---|
144 | return true; |
---|
145 | } |
---|
146 | |
---|
147 | |
---|
148 | /* |
---|
149 | * Given the client struct, send a protocol v2 version token to the client. |
---|
150 | * This is the response to a higher version number than we understand. |
---|
151 | * Returns true on success, false on failure (and logs a message on failure). |
---|
152 | */ |
---|
153 | static bool |
---|
154 | server_v2_send_version(struct client *client) |
---|
155 | { |
---|
156 | gss_buffer_desc token; |
---|
157 | char buffer[1 + 1 + 1]; |
---|
158 | OM_uint32 major, minor; |
---|
159 | int status; |
---|
160 | |
---|
161 | /* Build the version token. */ |
---|
162 | token.length = 1 + 1 + 1; |
---|
163 | token.value = &buffer; |
---|
164 | buffer[0] = 2; |
---|
165 | buffer[1] = MESSAGE_VERSION; |
---|
166 | buffer[2] = 2; |
---|
167 | |
---|
168 | /* Send the token. */ |
---|
169 | status = token_send_priv(client->fd, client->context, |
---|
170 | TOKEN_DATA | TOKEN_PROTOCOL, &token, &major, &minor); |
---|
171 | if (status != TOKEN_OK) { |
---|
172 | warn_token("sending version token", status, major, minor); |
---|
173 | client->fatal = true; |
---|
174 | return false; |
---|
175 | } |
---|
176 | return true; |
---|
177 | } |
---|
178 | |
---|
179 | |
---|
180 | /* |
---|
181 | * Receive a new token from the client, handling reporting of errors. Takes |
---|
182 | * the client struct and a pointer to storage for the token. Returns TOKEN_OK |
---|
183 | * on success, TOKEN_FAIL_EOF if the other end has gone away, and a different |
---|
184 | * error code on a recoverable error. |
---|
185 | */ |
---|
186 | static int |
---|
187 | server_v2_read_token(struct client *client, gss_buffer_t token) |
---|
188 | { |
---|
189 | OM_uint32 major, minor; |
---|
190 | int status, flags; |
---|
191 | |
---|
192 | status = token_recv_priv(client->fd, client->context, &flags, token, |
---|
193 | TOKEN_MAX_LENGTH, &major, &minor); |
---|
194 | if (status != TOKEN_OK) { |
---|
195 | warn_token("receiving command token", status, major, minor); |
---|
196 | if (status != TOKEN_FAIL_EOF) |
---|
197 | if (!server_send_error(client, ERROR_BAD_TOKEN, "Invalid token")) |
---|
198 | return TOKEN_FAIL_EOF; |
---|
199 | } |
---|
200 | return status; |
---|
201 | } |
---|
202 | |
---|
203 | |
---|
204 | /* |
---|
205 | * Handles a single token from the client, responding or running a command as |
---|
206 | * appropriate. Returns true if we should continue, false if an error |
---|
207 | * occurred or QUIT was received and we should stop processing tokens. |
---|
208 | */ |
---|
209 | static bool |
---|
210 | server_v2_handle_token(struct client *client, struct config *config, |
---|
211 | gss_buffer_t token) |
---|
212 | { |
---|
213 | char *p; |
---|
214 | size_t length, total; |
---|
215 | struct iovec **argv = NULL; |
---|
216 | char *buffer = NULL; |
---|
217 | int status; |
---|
218 | OM_uint32 minor; |
---|
219 | bool result = false; |
---|
220 | bool allocated = false; |
---|
221 | bool continued = false; |
---|
222 | |
---|
223 | /* |
---|
224 | * Loop on tokens until we have a complete command, allowing for continued |
---|
225 | * commands. We're going to accumulate the full command in buffer until |
---|
226 | * we've seen all of it. If the command isn't continued, we can use the |
---|
227 | * token as the buffer. |
---|
228 | */ |
---|
229 | total = 0; |
---|
230 | do { |
---|
231 | p = token->value; |
---|
232 | if (p[0] != 2) { |
---|
233 | result = server_v2_send_version(client); |
---|
234 | goto fail; |
---|
235 | } else if (p[1] == MESSAGE_QUIT) { |
---|
236 | debug("quit received, closing connection"); |
---|
237 | result = false; |
---|
238 | goto fail; |
---|
239 | } else if (p[1] != MESSAGE_COMMAND) { |
---|
240 | warn("unknown message type %d from client", (int) p[1]); |
---|
241 | result = server_send_error(client, ERROR_UNKNOWN_MESSAGE, |
---|
242 | "Unknown message"); |
---|
243 | goto fail; |
---|
244 | } |
---|
245 | p += 2; |
---|
246 | client->keepalive = p[0] ? true : false; |
---|
247 | |
---|
248 | /* Check the data size. */ |
---|
249 | if (token->length > TOKEN_MAX_DATA) { |
---|
250 | warn("command data length %lu exceeds 64KB", |
---|
251 | (unsigned long) token->length); |
---|
252 | result = server_send_error(client, ERROR_TOOMUCH_DATA, |
---|
253 | "Too much data"); |
---|
254 | goto fail; |
---|
255 | } |
---|
256 | |
---|
257 | /* Make sure the continuation is sane. */ |
---|
258 | if ((p[1] == 1 && continued) || (p[1] > 1 && !continued) || p[1] > 3) { |
---|
259 | warn("bad continue status %d", (int) p[1]); |
---|
260 | result = server_send_error(client, ERROR_BAD_COMMAND, |
---|
261 | "Invalid command token"); |
---|
262 | goto fail; |
---|
263 | } |
---|
264 | continued = (p[1] == 1 || p[1] == 2); |
---|
265 | |
---|
266 | /* |
---|
267 | * Read the token data. If the command is continued *or* if buffer is |
---|
268 | * non-NULL (meaning the command was previously continued), we copy |
---|
269 | * the data into the buffer. |
---|
270 | */ |
---|
271 | p += 2; |
---|
272 | length = token->length - (p - (char *) token->value); |
---|
273 | if (continued || buffer != NULL) { |
---|
274 | if (buffer == NULL) |
---|
275 | buffer = xmalloc(length); |
---|
276 | else |
---|
277 | buffer = xrealloc(buffer, total + length); |
---|
278 | allocated = true; |
---|
279 | memcpy(buffer + total, p, length); |
---|
280 | total += length; |
---|
281 | } |
---|
282 | |
---|
283 | /* |
---|
284 | * If the command was continued, we have to read the next token. |
---|
285 | * Otherwise, if buffer is NULL (no continuation), we just use this |
---|
286 | * token as the complete buffer. |
---|
287 | */ |
---|
288 | if (continued) { |
---|
289 | gss_release_buffer(&minor, token); |
---|
290 | status = server_v2_read_token(client, token); |
---|
291 | if (status == TOKEN_FAIL_EOF) |
---|
292 | result = false; |
---|
293 | else if (status != TOKEN_OK) |
---|
294 | result = true; |
---|
295 | if (status != TOKEN_OK) |
---|
296 | goto fail; |
---|
297 | } else if (buffer == NULL) { |
---|
298 | buffer = p; |
---|
299 | total = length; |
---|
300 | } |
---|
301 | } while (continued); |
---|
302 | |
---|
303 | /* |
---|
304 | * Okay, we now have a complete command that was possibly spread over |
---|
305 | * multiple tokens. Now we can parse it. |
---|
306 | */ |
---|
307 | argv = server_parse_command(client, buffer, total); |
---|
308 | if (allocated) |
---|
309 | free(buffer); |
---|
310 | if (argv == NULL) |
---|
311 | return !client->fatal; |
---|
312 | |
---|
313 | /* We have a command. Now do the heavy lifting. */ |
---|
314 | server_run_command(client, config, argv); |
---|
315 | server_free_command(argv); |
---|
316 | return !client->fatal; |
---|
317 | |
---|
318 | fail: |
---|
319 | if (allocated) |
---|
320 | free(buffer); |
---|
321 | return result; |
---|
322 | } |
---|
323 | |
---|
324 | |
---|
325 | /* |
---|
326 | * Takes the client struct and the server configuration and handles client |
---|
327 | * requests. Reads messages from the client, checking commands against the |
---|
328 | * ACLs and executing them when appropriate, until the connection is |
---|
329 | * terminated. |
---|
330 | */ |
---|
331 | void |
---|
332 | server_v2_handle_commands(struct client *client, struct config *config) |
---|
333 | { |
---|
334 | gss_buffer_desc token; |
---|
335 | OM_uint32 minor; |
---|
336 | int status; |
---|
337 | |
---|
338 | /* Loop receiving messages until we're finished. */ |
---|
339 | do { |
---|
340 | status = server_v2_read_token(client, &token); |
---|
341 | if (status == TOKEN_FAIL_EOF) |
---|
342 | break; |
---|
343 | else if (status != TOKEN_OK) |
---|
344 | continue; |
---|
345 | if (!server_v2_handle_token(client, config, &token)) { |
---|
346 | gss_release_buffer(&minor, &token); |
---|
347 | break; |
---|
348 | } |
---|
349 | gss_release_buffer(&minor, &token); |
---|
350 | } while (client->keepalive); |
---|
351 | } |
---|