X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=file.c;h=1e5b2f4106f8fd3456e79eac8d9bfa83446d456d;hb=HEAD;hp=40ed9b99b7e2d830b4d1f8de3401f6732287c9e2;hpb=821045f6ce341fdc39fc8dafc765411b0f4954d1;p=project%2Frpcd.git diff --git a/file.c b/file.c index 40ed9b9..1e5b2f4 100644 --- a/file.c +++ b/file.c @@ -38,11 +38,14 @@ #include /* limit of sys & proc files */ -#define RPC_FILE_MIN_SIZE (128) +#define RPC_FILE_MIN_SIZE (4096) /* limit of regular files and command output data */ #define RPC_FILE_MAX_SIZE (4096 * 64) +/* limit of command line length for exec acl checks */ +#define RPC_CMDLINE_MAX_SIZE (1024) + #define ustream_for_each_read_buffer(stream, ptr, len) \ for (ptr = ustream_get_read_buf(stream, &len); \ ptr != NULL && len > 0; \ @@ -71,6 +74,7 @@ struct rpc_file_exec_context { static struct blob_buf buf; static char *canonpath; +static char cmdstr[RPC_CMDLINE_MAX_SIZE]; enum { RPC_F_R_PATH, @@ -78,7 +82,7 @@ enum { __RPC_F_R_MAX, }; -static const struct blobmsg_policy rpc_file_r_policy[__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_SESSION] = { .name = "ubus_rpc_session", .type = BLOBMSG_TYPE_STRING }, @@ -91,7 +95,7 @@ enum { __RPC_F_RB_MAX, }; -static const struct blobmsg_policy rpc_file_rb_policy[__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_SESSION] = { .name = "ubus_rpc_session", @@ -108,7 +112,7 @@ enum { __RPC_F_RW_MAX, }; -static const struct blobmsg_policy rpc_file_rw_policy[__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 }, @@ -169,30 +173,13 @@ rpc_errno_status(void) } static bool -rpc_file_read_access(const struct blob_attr *sid, const char *path) +rpc_file_access(const struct blob_attr *sid, + const char *path, const char *perm) { 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"); + return ops->session_access(blobmsg_data(sid), "file", path, perm); } static char * @@ -257,19 +244,21 @@ next: } static struct blob_attr ** -rpc_check_path(struct blob_attr *msg, char **path, struct stat *s) +__rpc_check_path(const struct blobmsg_policy *policy, size_t policy_len, + int policy_path_idx, int policy_sid_idx, const char *perm, + struct blob_attr *msg, char **path, struct stat *s) { - static struct blob_attr *tb[__RPC_F_R_MAX]; + static struct blob_attr *tb[__RPC_F_RW_MAX]; /* largest _MAX constant */ - blobmsg_parse(rpc_file_r_policy, __RPC_F_R_MAX, tb, blob_data(msg), blob_len(msg)); + blobmsg_parse(policy, policy_len, tb, blob_data(msg), blob_len(msg)); - if (!tb[RPC_F_R_PATH]) + if (!tb[policy_path_idx]) { errno = EINVAL; return NULL; } - *path = rpc_canonicalize_path(blobmsg_get_string(tb[RPC_F_R_PATH])); + *path = rpc_canonicalize_path(blobmsg_get_string(tb[policy_path_idx])); if (*path == NULL) { @@ -277,24 +266,31 @@ rpc_check_path(struct blob_attr *msg, char **path, struct stat *s) return NULL; } - if (!rpc_file_read_access(tb[RPC_F_R_SESSION], *path)) + if (!rpc_file_access(tb[policy_sid_idx], *path, perm)) { errno = EACCES; return NULL; } - if (stat(*path, s)) + if (s != NULL && stat(*path, s) != 0) return NULL; return tb; } +#define rpc_check_path(msg, policy_selector, perm, path, s) \ + __rpc_check_path(rpc_file_ ## policy_selector ## _policy, \ + ARRAY_SIZE(rpc_file_ ## policy_selector ## _policy), \ + RPC_F_ ## policy_selector ## _PATH, \ + RPC_F_ ## policy_selector ## _SESSION, \ + perm, msg, path, s) + static int rpc_file_read(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { - static struct blob_attr *tb[__RPC_F_RB_MAX]; + struct blob_attr **tb; bool base64 = false; int fd, rv; ssize_t len; @@ -302,20 +298,9 @@ rpc_file_read(struct ubus_context *ctx, struct ubus_object *obj, struct stat s; char *wbuf; - blobmsg_parse(rpc_file_rb_policy, __RPC_F_RB_MAX, tb, blob_data(msg), blob_len(msg)); - - if (!tb[RPC_F_RB_PATH]) - return rpc_errno_status(); - - path = rpc_canonicalize_path(blobmsg_get_string(tb[RPC_F_RB_PATH])); + tb = rpc_check_path(msg, RB, "read", &path, &s); - 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)) + if (tb == NULL) return rpc_errno_status(); if (s.st_size >= RPC_FILE_MAX_SIZE) @@ -386,7 +371,7 @@ rpc_file_write(struct ubus_context *ctx, struct ubus_object *obj, struct ubus_request_data *req, const char *method, struct blob_attr *msg) { - struct blob_attr *tb[__RPC_F_RW_MAX]; + struct blob_attr **tb; int append = O_TRUNC; mode_t prev_mode, mode = 0666; int fd, rv = 0; @@ -394,19 +379,13 @@ rpc_file_write(struct ubus_context *ctx, struct ubus_object *obj, void *data = NULL; ssize_t data_len = 0; - blobmsg_parse(rpc_file_rw_policy, __RPC_F_RW_MAX, tb, - blob_data(msg), blob_len(msg)); + tb = rpc_check_path(msg, RW, "write", &path, NULL); - 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 (tb == NULL) + return rpc_errno_status(); - if (!rpc_file_write_access(tb[RPC_F_RW_SESSION], path)) - return UBUS_STATUS_PERMISSION_DENIED; + if (!tb[RPC_F_RW_DATA]) + return UBUS_STATUS_INVALID_ARGUMENT; data = blobmsg_data(tb[RPC_F_RW_DATA]); data_len = blobmsg_data_len(tb[RPC_F_RW_DATA]) - 1; @@ -460,7 +439,7 @@ rpc_file_md5(struct ubus_context *ctx, struct ubus_object *obj, uint8_t md5[16]; char *wbuf; - if (!rpc_check_path(msg, &path, &s)) + if (!rpc_check_path(msg, R, "read", &path, &s)) return rpc_errno_status(); if (!S_ISREG(s.st_mode)) @@ -518,7 +497,7 @@ rpc_file_list(struct ubus_context *ctx, struct ubus_object *obj, struct dirent *e; char *path, *entrypath; - if (!rpc_check_path(msg, &path, &s)) + if (!rpc_check_path(msg, R, "list", &path, NULL)) return rpc_errno_status(); if ((fd = opendir(path)) == NULL) @@ -563,7 +542,7 @@ rpc_file_stat(struct ubus_context *ctx, struct ubus_object *obj, char *path; struct stat s; - if (!rpc_check_path(msg, &path, &s)) + if (!rpc_check_path(msg, R, "list", &path, &s)) return rpc_errno_status(); blob_buf_init(&buf, 0); @@ -577,6 +556,75 @@ rpc_file_stat(struct ubus_context *ctx, struct ubus_object *obj, return 0; } +static int +rpc_file_remove_recursive(const char *path); + +static int +rpc_file_remove_recursive(const char *path) +{ + DIR *fd; + int err = 0; + struct stat s; + struct dirent *e; + char *entrypath; + + if ((fd = opendir(path)) == NULL) + return rpc_errno_status(); + + for (e = readdir(fd); e != NULL && err == 0; e = readdir(fd)) + { + if (!strcmp(e->d_name, ".") || !strcmp(e->d_name, "..")) + continue; + + if (asprintf(&entrypath, "%s/%s", path, e->d_name) >= 0) + { + if (!lstat(entrypath, &s)) + { + if (S_ISDIR(s.st_mode)) + err = rpc_file_remove_recursive(entrypath); + else if (unlink(entrypath)) + err = rpc_errno_status(); + } + + free(entrypath); + } + else + { + err = UBUS_STATUS_UNKNOWN_ERROR; + } + } + + closedir(fd); + + if (!err && rmdir(path)) + return rpc_errno_status(); + + return err; +} + +static int +rpc_file_remove(struct ubus_context *ctx, struct ubus_object *obj, + struct ubus_request_data *req, const char *method, + struct blob_attr *msg) +{ + struct stat s; + char *path = NULL; + + if (!rpc_check_path(msg, R, "write", &path, NULL)) + return rpc_errno_status(); + + if (lstat(path, &s)) + return rpc_errno_status(); + + if (S_ISDIR(s.st_mode)) + return rpc_file_remove_recursive(path); + + if (unlink(path)) + return rpc_errno_status(); + + return 0; +} + static const char * rpc_file_exec_lookup(const char *cmd) { @@ -757,10 +805,13 @@ rpc_file_exec_run(const char *cmd, const struct blob_attr *sid, struct blob_attr *cur; uint8_t arglen; - char *executable, **args, **tmp; + char *executable, **args, **tmp, *p; struct rpc_file_exec_context *c; + if (sid && env) + return UBUS_STATUS_PERMISSION_DENIED; + cmd = rpc_file_exec_lookup(cmd); if (!cmd) @@ -771,21 +822,46 @@ rpc_file_exec_run(const char *cmd, const struct blob_attr *sid, if (executable == NULL) return UBUS_STATUS_UNKNOWN_ERROR; - if (!rpc_file_exec_access(sid, executable)) - return UBUS_STATUS_PERMISSION_DENIED; + if (!rpc_file_access(sid, executable, "exec")) + { + if (arg == NULL || strlen(executable) >= sizeof(cmdstr)) + return UBUS_STATUS_PERMISSION_DENIED; + + arglen = 2; + p = cmdstr + sprintf(cmdstr, "%s", executable); + + blobmsg_for_each_attr(cur, arg, rem) + { + if (blobmsg_type(cur) != BLOBMSG_TYPE_STRING) + continue; + + if (arglen == 255 || + p + blobmsg_data_len(cur) >= cmdstr + sizeof(cmdstr)) + return UBUS_STATUS_PERMISSION_DENIED; + + p += sprintf(p, " %s", blobmsg_get_string(cur)); + arglen++; + } + + if (!rpc_file_access(sid, cmdstr, "exec")) + return UBUS_STATUS_PERMISSION_DENIED; + } c = malloc(sizeof(*c)); if (!c) return UBUS_STATUS_UNKNOWN_ERROR; - if (pipe(opipe) || pipe(epipe)) - return rpc_errno_status(); + if (pipe(opipe)) + goto fail_opipe; + + if (pipe(epipe)) + goto fail_epipe; switch ((pid = fork())) { case -1: - return rpc_errno_status(); + goto fail_fork; case 0: uloop_done(); @@ -877,6 +953,18 @@ rpc_file_exec_run(const char *cmd, const struct blob_attr *sid, } return UBUS_STATUS_OK; + +fail_fork: + close(epipe[0]); + close(epipe[1]); + +fail_epipe: + close(opipe[0]); + close(opipe[1]); + +fail_opipe: + free(c); + return rpc_errno_status(); } static int @@ -901,16 +989,17 @@ static int rpc_file_api_init(const struct rpc_daemon_ops *o, struct ubus_context *ctx) { static const struct ubus_method file_methods[] = { - UBUS_METHOD("read", rpc_file_read, rpc_file_rb_policy), - UBUS_METHOD("write", rpc_file_write, rpc_file_rw_policy), - UBUS_METHOD("list", rpc_file_list, rpc_file_r_policy), - UBUS_METHOD("stat", rpc_file_stat, rpc_file_r_policy), - UBUS_METHOD("md5", rpc_file_md5, rpc_file_r_policy), - UBUS_METHOD("exec", rpc_file_exec, rpc_exec_policy), + UBUS_METHOD("read", rpc_file_read, rpc_file_RB_policy), + UBUS_METHOD("write", rpc_file_write, rpc_file_RW_policy), + UBUS_METHOD("list", rpc_file_list, rpc_file_R_policy), + UBUS_METHOD("stat", rpc_file_stat, rpc_file_R_policy), + UBUS_METHOD("md5", rpc_file_md5, rpc_file_R_policy), + UBUS_METHOD("remove", rpc_file_remove, rpc_file_R_policy), + UBUS_METHOD("exec", rpc_file_exec, rpc_exec_policy), }; static struct ubus_object_type file_type = - UBUS_OBJECT_TYPE("luci-rpc-file", file_methods); + UBUS_OBJECT_TYPE("rpcd-plugin-file", file_methods); static struct ubus_object obj = { .name = "file",