summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--file.c176
1 files changed, 156 insertions, 20 deletions
diff --git a/file.c b/file.c
index 067d365..40ed9b9 100644
--- a/file.c
+++ b/file.c
@@ -70,25 +70,32 @@ struct rpc_file_exec_context {
static struct blob_buf buf;
+static char *canonpath;
enum {
RPC_F_R_PATH,
+ RPC_F_R_SESSION,
__RPC_F_R_MAX,
};
static const struct blobmsg_policy rpc_file_r_policy[__RPC_F_R_MAX] = {
- [RPC_F_R_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
+ [RPC_F_R_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
+ [RPC_F_R_SESSION] = { .name = "ubus_rpc_session",
+ .type = BLOBMSG_TYPE_STRING },
};
enum {
RPC_F_RB_PATH,
RPC_F_RB_BASE64,
+ RPC_F_RB_SESSION,
__RPC_F_RB_MAX,
};
static const struct blobmsg_policy rpc_file_rb_policy[__RPC_F_RB_MAX] = {
- [RPC_F_RB_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
- [RPC_F_RB_BASE64] = { .name = "base64", .type = BLOBMSG_TYPE_BOOL },
+ [RPC_F_RB_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
+ [RPC_F_RB_BASE64] = { .name = "base64", .type = BLOBMSG_TYPE_BOOL },
+ [RPC_F_RB_SESSION] = { .name = "ubus_rpc_session",
+ .type = BLOBMSG_TYPE_STRING },
};
enum {
@@ -97,28 +104,34 @@ enum {
RPC_F_RW_APPEND,
RPC_F_RW_MODE,
RPC_F_RW_BASE64,
+ RPC_F_RW_SESSION,
__RPC_F_RW_MAX,
};
static const struct blobmsg_policy rpc_file_rw_policy[__RPC_F_RW_MAX] = {
- [RPC_F_RW_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
- [RPC_F_RW_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
- [RPC_F_RW_APPEND] = { .name = "append", .type = BLOBMSG_TYPE_BOOL },
- [RPC_F_RW_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_INT32 },
- [RPC_F_RW_BASE64] = { .name = "base64", .type = BLOBMSG_TYPE_BOOL },
+ [RPC_F_RW_PATH] = { .name = "path", .type = BLOBMSG_TYPE_STRING },
+ [RPC_F_RW_DATA] = { .name = "data", .type = BLOBMSG_TYPE_STRING },
+ [RPC_F_RW_APPEND] = { .name = "append", .type = BLOBMSG_TYPE_BOOL },
+ [RPC_F_RW_MODE] = { .name = "mode", .type = BLOBMSG_TYPE_INT32 },
+ [RPC_F_RW_BASE64] = { .name = "base64", .type = BLOBMSG_TYPE_BOOL },
+ [RPC_F_RW_SESSION] = { .name = "ubus_rpc_session",
+ .type = BLOBMSG_TYPE_STRING },
};
enum {
RPC_E_CMD,
RPC_E_PARM,
RPC_E_ENV,
+ RPC_E_SESSION,
__RPC_E_MAX,
};
static const struct blobmsg_policy rpc_exec_policy[__RPC_E_MAX] = {
- [RPC_E_CMD] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
- [RPC_E_PARM] = { .name = "params", .type = BLOBMSG_TYPE_ARRAY },
- [RPC_E_ENV] = { .name = "env", .type = BLOBMSG_TYPE_TABLE },
+ [RPC_E_CMD] = { .name = "command", .type = BLOBMSG_TYPE_STRING },
+ [RPC_E_PARM] = { .name = "params", .type = BLOBMSG_TYPE_ARRAY },
+ [RPC_E_ENV] = { .name = "env", .type = BLOBMSG_TYPE_TABLE },
+ [RPC_E_SESSION] = { .name = "ubus_rpc_session",
+ .type = BLOBMSG_TYPE_STRING },
};
static const char *d_types[] = {
@@ -155,6 +168,94 @@ rpc_errno_status(void)
}
}
+static bool
+rpc_file_read_access(const struct blob_attr *sid, const char *path)
+{
+ if (!sid)
+ return true;
+
+ return ops->session_access(blobmsg_data(sid), "file", path, "read");
+}
+
+static bool
+rpc_file_write_access(const struct blob_attr *sid, const char *path)
+{
+ if (!sid)
+ return true;
+
+ return ops->session_access(blobmsg_data(sid), "file", path, "write");
+}
+
+static bool
+rpc_file_exec_access(const struct blob_attr *sid, const char *path)
+{
+ if (!sid)
+ return true;
+
+ return ops->session_access(blobmsg_data(sid), "file", path, "exec");
+}
+
+static char *
+rpc_canonicalize_path(const char *path)
+{
+ char *cp;
+ const char *p;
+
+ if (path == NULL || *path == '\0')
+ return NULL;
+
+ if (canonpath != NULL)
+ free(canonpath);
+
+ canonpath = strdup(path);
+
+ if (canonpath == NULL)
+ return NULL;
+
+ /* normalize */
+ for (cp = canonpath, p = path; *p != '\0'; ) {
+ if (*p != '/')
+ goto next;
+
+ /* skip repeating / */
+ if (p[1] == '/') {
+ p++;
+ continue;
+ }
+
+ /* /./ or /../ */
+ if (p[1] == '.') {
+ /* skip /./ */
+ if ((p[2] == '\0') || (p[2] == '/')) {
+ p += 2;
+ continue;
+ }
+
+ /* collapse /x/../ */
+ if ((p[2] == '.') && ((p[3] == '\0') || (p[3] == '/'))) {
+ while ((cp > canonpath) && (*--cp != '/'))
+ ;
+
+ p += 3;
+ continue;
+ }
+ }
+
+next:
+ *cp++ = *p++;
+ }
+
+ /* remove trailing slash if not root / */
+ if ((cp > canonpath + 1) && (cp[-1] == '/'))
+ cp--;
+ else if (cp == canonpath)
+ *cp++ = '/';
+
+ *cp = '\0';
+
+ return canonpath;
+}
+
static struct blob_attr **
rpc_check_path(struct blob_attr *msg, char **path, struct stat *s)
{
@@ -168,7 +269,19 @@ rpc_check_path(struct blob_attr *msg, char **path, struct stat *s)
return NULL;
}
- *path = blobmsg_data(tb[RPC_F_R_PATH]);
+ *path = rpc_canonicalize_path(blobmsg_get_string(tb[RPC_F_R_PATH]));
+
+ if (*path == NULL)
+ {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ if (!rpc_file_read_access(tb[RPC_F_R_SESSION], *path))
+ {
+ errno = EACCES;
+ return NULL;
+ }
if (stat(*path, s))
return NULL;
@@ -194,7 +307,13 @@ rpc_file_read(struct ubus_context *ctx, struct ubus_object *obj,
if (!tb[RPC_F_RB_PATH])
return rpc_errno_status();
- path = blobmsg_data(tb[RPC_F_RB_PATH]);
+ path = rpc_canonicalize_path(blobmsg_get_string(tb[RPC_F_RB_PATH]));
+
+ if (path == NULL)
+ return UBUS_STATUS_UNKNOWN_ERROR;
+
+ if (!rpc_file_read_access(tb[RPC_F_RB_SESSION], path))
+ return UBUS_STATUS_PERMISSION_DENIED;
if (stat(path, &s))
return rpc_errno_status();
@@ -271,6 +390,7 @@ rpc_file_write(struct ubus_context *ctx, struct ubus_object *obj,
int append = O_TRUNC;
mode_t prev_mode, mode = 0666;
int fd, rv = 0;
+ char *path = NULL;
void *data = NULL;
ssize_t data_len = 0;
@@ -280,6 +400,14 @@ rpc_file_write(struct ubus_context *ctx, struct ubus_object *obj,
if (!tb[RPC_F_RW_PATH] || !tb[RPC_F_RW_DATA])
return UBUS_STATUS_INVALID_ARGUMENT;
+ path = rpc_canonicalize_path(blobmsg_get_string(tb[RPC_F_RW_PATH]));
+
+ if (path == NULL)
+ return UBUS_STATUS_UNKNOWN_ERROR;
+
+ if (!rpc_file_write_access(tb[RPC_F_RW_SESSION], path))
+ return UBUS_STATUS_PERMISSION_DENIED;
+
data = blobmsg_data(tb[RPC_F_RW_DATA]);
data_len = blobmsg_data_len(tb[RPC_F_RW_DATA]) - 1;
@@ -290,7 +418,7 @@ rpc_file_write(struct ubus_context *ctx, struct ubus_object *obj,
mode = blobmsg_get_u32(tb[RPC_F_RW_MODE]);
prev_mode = umask(0);
- fd = open(blobmsg_data(tb[RPC_F_RW_PATH]), O_CREAT | O_WRONLY | append, mode);
+ fd = open(path, O_CREAT | O_WRONLY | append, mode);
umask(prev_mode);
if (fd < 0)
return rpc_errno_status();
@@ -615,7 +743,7 @@ rpc_fdclose(int fd)
}
static int
-rpc_file_exec_run(const char *cmd,
+rpc_file_exec_run(const char *cmd, const struct blob_attr *sid,
const struct blob_attr *arg, const struct blob_attr *env,
struct ubus_context *ctx, struct ubus_request_data *req)
{
@@ -629,7 +757,7 @@ rpc_file_exec_run(const char *cmd,
struct blob_attr *cur;
uint8_t arglen;
- char **args, **tmp;
+ char *executable, **args, **tmp;
struct rpc_file_exec_context *c;
@@ -638,6 +766,14 @@ rpc_file_exec_run(const char *cmd,
if (!cmd)
return UBUS_STATUS_NOT_FOUND;
+ executable = rpc_canonicalize_path(cmd);
+
+ if (executable == NULL)
+ return UBUS_STATUS_UNKNOWN_ERROR;
+
+ if (!rpc_file_exec_access(sid, executable))
+ return UBUS_STATUS_PERMISSION_DENIED;
+
c = malloc(sizeof(*c));
if (!c)
@@ -675,7 +811,7 @@ rpc_file_exec_run(const char *cmd,
if (!args)
return UBUS_STATUS_UNKNOWN_ERROR;
- args[0] = (char *)cmd;
+ args[0] = (char *)executable;
args[1] = NULL;
if (arg)
@@ -717,7 +853,7 @@ rpc_file_exec_run(const char *cmd,
}
}
- if (execv(cmd, args))
+ if (execv(executable, args))
return rpc_errno_status();
default:
@@ -756,8 +892,8 @@ rpc_file_exec(struct ubus_context *ctx, struct ubus_object *obj,
if (!tb[RPC_E_CMD])
return UBUS_STATUS_INVALID_ARGUMENT;
- return rpc_file_exec_run(blobmsg_data(tb[RPC_E_CMD]),
- tb[RPC_E_PARM], tb[RPC_E_ENV], ctx, req);
+ return rpc_file_exec_run(blobmsg_data(tb[RPC_E_CMD]), tb[RPC_E_SESSION],
+ tb[RPC_E_PARM], tb[RPC_E_ENV], ctx, req);
}