2 * Copyright (C) 2009 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 /* This code parses and executes the commands sent by guests. It
20 * is therefore particularly security sensitive. The protocol is
21 * documented in hostinfo-protocol(5).
37 #include <apr_general.h>
38 #include <apr_pools.h>
40 #include <apr_tables.h>
41 #include <apr_strings.h>
43 #include "hostinfod.h"
44 #include "apr12-compat.h"
46 #define PROTOCOL_VERSION "1.0"
49 apr_hash_t *commands = NULL;
51 static const char *string_of_arg_type (enum arg_type);
52 static int not_printable (const char *str);
53 static int contains_crlf (const char *str);
54 static char *parse_c_string (struct guest_description *hval, const char *str, size_t *posn);
55 static int parse_long (struct guest_description *hval, const char *str, size_t *posn, long *ret);
58 execute_command (const struct timespec *now,
59 struct guest_description *hval, const char *command)
64 apr_array_header_t *args;
69 struct timespec *last;
71 debug ("%s: %s", hval->name, command);
73 /* Create a new pool for allocation during the lifetime of the
74 * request/response. **NB** the hval->reply field is allocated
75 * from this pool, which is why it gets nulled below.
78 apr_pool_destroy (hval->rpool);
81 hval->reply_size = hval->reply_posn = 0;
82 apr_pool_create (&hval->rpool, hval->pool);
84 /* Split up the command. Commands have a very narrowly-defined
85 * format, and we reject any malformed commands with a 400 error.
87 * NB. A lot of the code below assumes 7 bit, printable ASCII,
88 * and this is only safe because of the call to 'not_printable'
91 len = strlen (command);
92 if (len < 1 || len > 4094 || not_printable (command)) {
93 warning ("%s: command too short, too long or contained unprintable chars (len = %zu)",
95 send_error (hval, 400);
99 /* Command is alphanumeric string, non-zero length. */
101 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
102 "abcdefghijklmnopqrstuvwxyz"
105 warning ("%s: no command part in request", hval->name);
106 send_error (hval, 400);
110 cmd = apr_pstrmemdup (hval->rpool, command, i);
111 for (j = 0; j < i; ++j)
112 cmd[j] = tolower (cmd[j]);
114 args = apr_array_make (hval->rpool, 0, sizeof (struct arg));
116 while (command[i]) { /* command[i] is the space before the next arg */
117 if (command[i] != ' ') {
118 warning ("%s: there must be a single space between command and each argument",
120 send_error (hval, 400);
127 switch (command[i]) {
129 warning ("%s: trailing space after command", hval->name);
130 send_error (hval, 400);
133 case '"': /* string literal */
134 arg.type = arg_type_string;
135 arg.u.str = parse_c_string (hval, command, &i);
137 send_error (hval, 400);
142 case '-': /* integer literal */
154 arg.type = arg_type_int;
155 if (parse_long (hval, command, &i, &arg.u.i) == -1) {
156 send_error (hval, 400);
161 case 't': /* boolean */
165 arg.type = arg_type_bool;
166 if (strncasecmp (&command[i], "true", 4) == 0) {
169 } else if (strncasecmp (&command[i], "false", 5) == 0) {
178 warning ("%s: unknown or malformed argument starting at position %zu ('%c')",
179 hval->name, i, command[i]);
180 send_error (hval, 400);
184 APR_ARRAY_PUSH (args, struct arg) = arg;
188 /* Debug output what the guest sent / what we decoded. */
189 debug ("%s: command '%s' with %d args", hval->name, cmd, args->nelts);
190 for (i = 0; i < args->nelts; ++i) {
191 struct arg arg = APR_ARRAY_IDX (args, i, struct arg);
196 debug ("%s: arg %zu : %s = %ld",
197 hval->name, i, string_of_arg_type (arg.type), arg.u.i);
199 case arg_type_string:
200 debug ("%s: arg %zu : %s = %s",
201 hval->name, i, string_of_arg_type (arg.type), arg.u.str);
207 /* Know about this command? */
208 fn = apr_hash_get (commands, cmd, APR_HASH_KEY_STRING);
210 send_error (hval, 404);
214 /* Before dispatching the command, check the command is enabled
215 * and guest is not calling it too frequently.
217 check_guests_file (hval, cmd, &interval, &enabled);
220 warning ("%s: guest tried disabled command '%s'", hval->name, cmd);
221 send_error (hval, 401);
225 last = apr_hash_get (hval->lasttime, cmd, APR_HASH_KEY_STRING);
227 struct timespec timediff;
228 double interval_int, interval_frac;
229 struct timespec interval_ts;
231 diff_timespec (&timediff, now, last);
233 interval_frac = modf (interval, &interval_int);
234 interval_ts.tv_sec = interval_int;
235 interval_ts.tv_nsec = interval_frac * 1000000000;
237 debug ("%s: %s: interval %ds %ldns, time since last %ds %ldns",
239 (int) interval_ts.tv_sec, interval_ts.tv_nsec,
240 (int) timediff.tv_sec, timediff.tv_nsec);
242 if (interval_ts.tv_sec > timediff.tv_sec ||
243 (interval_ts.tv_sec == timediff.tv_sec &&
244 interval_ts.tv_nsec > timediff.tv_nsec)) {
245 warning ("%s: command '%s' exceeded interval allowed", hval->name, cmd);
246 send_error (hval, 406);
251 last = apr_pmemdup (hval->pool, now, sizeof *now);
252 apr_hash_set (hval->lasttime, cmd, APR_HASH_KEY_STRING, last);
254 /* Dispatch the command. */
255 fn (hval, cmd, args);
258 /* All commands must consist only of printable 7 bit ASCII.
259 * NB. Don't use isprint(3).
262 not_printable (const char *str)
266 while ((c = *(unsigned char *)str)) {
267 if (c < 32 || c > 126)
275 contains_crlf (const char *str)
279 while ((c = *(unsigned char *)str)) {
280 if (c == '\r' || c == '\n')
291 case '0': case '1': case '2': case '3': case '4':
292 case '5': case '6': case '7': case '8': case '9':
294 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
296 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
304 parse_c_string (struct guest_description *hval, const char *str, size_t *posn)
306 apr_array_header_t *r;
309 r = apr_array_make (hval->rpool, 0, 1);
310 #define APPEND(c) APR_ARRAY_PUSH (r,char) = (c);
312 assert (str[*posn] == '"');
315 while (str[*posn] != '"') {
316 if (str[*posn] == '\\') { /* Start of \escape sequence. */
317 switch (str[*posn+1]) {
318 case '0': case '1': case '2': case '3':
319 if ((str[*posn+2] >= '0' && str[*posn+2] <= '7') &&
320 (str[*posn+3] >= '0' && str[*posn+3] <= '7')) {
321 c = (str[*posn+1] - '0') * 0100;
322 c += (str[*posn+2] - '0') * 010;
323 c += str[*posn+3] - '0';
328 warning ("%s: \\0 cannot appear in string literal", hval->name);
333 warning ("%s: invalid octal sequence in string literal", hval->name);
337 if (isxdigit (str[*posn+2]) && isxdigit (str[*posn+3])) {
338 c = hexval (str[*posn+2]) * 0x10;
339 c += hexval (str[*posn+3]);
344 warning ("%s: \\0 cannot appear in string literal", hval->name);
349 warning ("%s: invalid hex sequence in string literal", hval->name);
390 warning ("%s: unterminated escape sequence in string literal",
394 } else if (str[*posn] == '\0') { /* Unterminated string literal. */
395 warning ("%s: unterminated string literal in request", hval->name);
397 } else { /* Ordinary character. */
403 /* Finish off the string and return it. */
405 (*posn)++; /* Skips over the final quote. */
411 parse_long (struct guest_description *hval,
412 const char *str, size_t *posn, long *ret)
414 error ("XXXXXXX parse_long not implemented XXXXXXX");
418 /* For single line replies. */
420 send_reply (struct guest_description *hval, int code, const char *fs, ...)
425 /* All success codes must be 2xx. */
426 assert (code >= 200 && code < 300);
429 msg = apr_pvsprintf (hval->rpool, fs, args);
432 /* The result string must not contain any CR or LF characters. If
433 * not it's an internal error in the command, or else the caller has
434 * (somehow) managed to pass a bad string through.
436 if (contains_crlf (msg)) {
437 error ("%s: send_reply: refusing the send a reply message containing CR/LF characters. This is a serious internal error in the current command.",
439 send_error (hval, 500);
445 apr_psprintf (hval->rpool, PROTOCOL_VERSION " %03d %s" CRLF,
447 hval->reply_size = strlen (hval->reply);
448 hval->reply_posn = 0;
449 hval->state = guest_state_reply;
453 send_error (struct guest_description *hval, int code)
457 /* All errors must be 4xx or 5xx. */
458 assert (code >= 400 && code < 600);
460 /* NB: If you add a code, update COMMON STATUS CODES section
461 * in hostinfo-protocol.pod.
464 case 400: msg = "Bad request"; break;
465 case 401: msg = "Command disabled"; break;
466 case 404: msg = "Command not found"; break;
467 case 406: msg = "Too frequent"; break;
468 case 500: msg = "Internal server error"; break;
469 default: msg = "Unknown error"; break;
472 /* Construct the reply. */
474 apr_psprintf (hval->rpool, PROTOCOL_VERSION " %03d %s" CRLF,
476 hval->reply_size = strlen (hval->reply);
477 hval->reply_posn = 0;
478 hval->state = guest_state_reply;
480 /* Penalty is always increased on errors, to ensure the guest
481 * cannot flood us with invalid commands.
487 string_of_arg_type (enum arg_type t)
490 case arg_type_string: return "string";
491 case arg_type_int: return "int";
492 case arg_type_bool: return "bool";
498 get_args (apr_array_header_t *args, const char *argfs, ...)
503 va_start (vargs, argfs);
508 if (i >= args->nelts) {
513 arg = APR_ARRAY_IDX (args, i, struct arg);
517 if (arg.type == arg_type_string)
518 * va_arg (vargs, const char **) = arg.u.str;
526 if (arg.type == arg_type_int)
527 * va_arg (vargs, int *) = arg.u.i;
535 if (arg.type == arg_type_bool)
536 * va_arg (vargs, int *) = arg.u.i;
544 error ("get_args: invalid character '%c'", *argfs);