mirror of
https://github.com/xen-project/xen.git
synced 2026-10-29 10:40:03 +01:00
It is possible to send a zero-string message body to xenstore's XS_CONTROL handling function. Then the number of strings is used for an array allocation. This leads to a crash in strcmp() in a CONTROL sub-command invocation loop. The output of xs_count_string() should be verified and all 0 or negative values should be rejected with an EINVAL. At least the sub-command name must be specified. The xenstore crash can only be triggered from within dom0 (there is a check in do_control() rejecting all non-dom0 requests with an EACCES). Testing: reproduced with the following command: python -c 'print 16*"\x00"' | nc -U $XENSTORED_RUNDIR/socket Signed-off-by: Pawel Wieczorkiewicz <wipawel@amazon.de> Reviewed-by: Martin Pohlack <mpohlack@amazon.de> Acked-by: Ian Jackson <ian.jackson@eu.citrix.com> Reviewed-by: Juergen Gross <jgross@suse.com> Acked-by: Wei Liu <wei.liu2@citrix.com> Release-acked-by: Julien Grall <julien.grall@linaro.org>
201 lines
4.1 KiB
C
201 lines
4.1 KiB
C
/*
|
|
Interactive commands for Xen Store Daemon.
|
|
Copyright (C) 2017 Juergen Gross, SUSE Linux GmbH
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "utils.h"
|
|
#include "talloc.h"
|
|
#include "xenstored_core.h"
|
|
#include "xenstored_control.h"
|
|
|
|
struct cmd_s {
|
|
char *cmd;
|
|
int (*func)(void *, struct connection *, char **, int);
|
|
char *pars;
|
|
};
|
|
|
|
static int do_control_check(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num)
|
|
return EINVAL;
|
|
|
|
check_store();
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
static int do_control_log(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num != 1)
|
|
return EINVAL;
|
|
|
|
if (!strcmp(vec[0], "on"))
|
|
reopen_log();
|
|
else if (!strcmp(vec[0], "off"))
|
|
close_log();
|
|
else
|
|
return EINVAL;
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
static int do_control_logfile(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num != 1)
|
|
return EINVAL;
|
|
|
|
close_log();
|
|
talloc_free(tracefile);
|
|
tracefile = talloc_strdup(NULL, vec[0]);
|
|
reopen_log();
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
static int do_control_memreport(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
FILE *fp;
|
|
int fd;
|
|
|
|
if (num > 1)
|
|
return EINVAL;
|
|
|
|
if (num == 0) {
|
|
if (tracefd < 0) {
|
|
if (!tracefile)
|
|
return EBADF;
|
|
fp = fopen(tracefile, "a");
|
|
} else {
|
|
/*
|
|
* Use dup() in order to avoid closing the file later
|
|
* with fclose() which will release stream resources.
|
|
*/
|
|
fd = dup(tracefd);
|
|
if (fd < 0)
|
|
return EBADF;
|
|
fp = fdopen(fd, "a");
|
|
if (!fp)
|
|
close(fd);
|
|
}
|
|
} else
|
|
fp = fopen(vec[0], "a");
|
|
|
|
if (!fp)
|
|
return EBADF;
|
|
|
|
talloc_report_full(NULL, fp);
|
|
fclose(fp);
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
static int do_control_print(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
if (num != 1)
|
|
return EINVAL;
|
|
|
|
xprintf("control: %s", vec[0]);
|
|
|
|
send_ack(conn, XS_CONTROL);
|
|
return 0;
|
|
}
|
|
|
|
static int do_control_help(void *, struct connection *, char **, int);
|
|
|
|
static struct cmd_s cmds[] = {
|
|
{ "check", do_control_check, "" },
|
|
{ "log", do_control_log, "on|off" },
|
|
{ "logfile", do_control_logfile, "<file>" },
|
|
{ "memreport", do_control_memreport, "[<file>]" },
|
|
{ "print", do_control_print, "<string>" },
|
|
{ "help", do_control_help, "" },
|
|
};
|
|
|
|
static int do_control_help(void *ctx, struct connection *conn,
|
|
char **vec, int num)
|
|
{
|
|
int cmd, len = 0;
|
|
char *resp;
|
|
|
|
if (num)
|
|
return EINVAL;
|
|
|
|
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
|
|
len += strlen(cmds[cmd].cmd) + 1;
|
|
len += strlen(cmds[cmd].pars) + 1;
|
|
}
|
|
len++;
|
|
|
|
resp = talloc_array(ctx, char, len);
|
|
if (!resp)
|
|
return ENOMEM;
|
|
|
|
len = 0;
|
|
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++) {
|
|
strcpy(resp + len, cmds[cmd].cmd);
|
|
len += strlen(cmds[cmd].cmd);
|
|
resp[len] = '\t';
|
|
len++;
|
|
strcpy(resp + len, cmds[cmd].pars);
|
|
len += strlen(cmds[cmd].pars);
|
|
resp[len] = '\n';
|
|
len++;
|
|
}
|
|
resp[len] = 0;
|
|
|
|
send_reply(conn, XS_CONTROL, resp, len);
|
|
return 0;
|
|
}
|
|
|
|
int do_control(struct connection *conn, struct buffered_data *in)
|
|
{
|
|
int num;
|
|
int cmd;
|
|
char **vec;
|
|
|
|
if (conn->id != 0)
|
|
return EACCES;
|
|
|
|
num = xs_count_strings(in->buffer, in->used);
|
|
if (num < 1)
|
|
return EINVAL;
|
|
vec = talloc_array(in, char *, num);
|
|
if (!vec)
|
|
return ENOMEM;
|
|
if (get_strings(in, vec, num) != num)
|
|
return EIO;
|
|
|
|
for (cmd = 0; cmd < ARRAY_SIZE(cmds); cmd++)
|
|
if (streq(vec[0], cmds[cmd].cmd))
|
|
return cmds[cmd].func(in, conn, vec + 1, num - 1);
|
|
|
|
return EINVAL;
|
|
}
|