From aab390529df56cabc75cc8fff2707b32c35aaaec Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Thu, 2 May 2013 21:05:21 -0700 Subject: rgw: user operation mask Fixes: #4716 add user operation mask for controlling user permissions. Also add admin controls for it. Signed-off-by: Yehuda Sadeh --- src/rgw/rgw_admin.cc | 14 ++++++++++++++ src/rgw/rgw_common.cc | 45 +++++++++++++++++++++++++++++++++------------ src/rgw/rgw_common.h | 19 +++++++++++++++++-- src/rgw/rgw_json_enc.cc | 31 ++++++++++++++++++++++++++++--- src/rgw/rgw_main.cc | 7 +++++++ src/rgw/rgw_op.cc | 24 ++++++++++++++++++++++++ src/rgw/rgw_op.h | 29 ++++++++++++++++++++++++++++- src/rgw/rgw_user.cc | 6 ++++++ src/rgw/rgw_user.h | 9 +++++++++ 9 files changed, 166 insertions(+), 18 deletions(-) diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index 9804761e8ab..edbe887c208 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -485,6 +485,7 @@ int main(int argc, char **argv) std::string infile; RGWUserAdminOpState user_op; RGWBucketAdminOpState bucket_op; + string op_mask_str; std::string val; std::ostringstream errs; @@ -513,6 +514,8 @@ int main(int argc, char **argv) pool_name = val; } else if (ceph_argparse_witharg(args, i, &val, "-o", "--object", (char*)NULL)) { object = val; + } else if (ceph_argparse_witharg(args, i, &val, "--op-mask", (char*)NULL)) { + op_mask_str = val; } else if (ceph_argparse_witharg(args, i, &val, "--key-type", (char*)NULL)) { key_type_str = val; if (key_type_str.compare("swift") == 0) { @@ -677,6 +680,17 @@ int main(int argc, char **argv) if (set_perm) user_op.set_perm(perm_mask); + if (!op_mask_str.empty()) { + uint32_t op_mask; + int ret = rgw_parse_op_type_list(op_mask_str, &op_mask); + if (ret < 0) { + cerr << "failed to parse op_mask: " << cpp_strerror(-ret) << std::endl; + return -ret; + } + + user_op.set_op_mask(op_mask); + } + if (key_type != KEY_TYPE_UNDEFINED) user_op.set_key_type(key_type); diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 48a377cd7dc..5a9bf3d2747 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -638,15 +638,13 @@ bool url_decode(string& src_str, string& dest_str) return true; } -static struct { +struct rgw_name_to_flag { const char *type_name; - uint32_t perm; -} cap_names[] = { {"*", RGW_CAP_ALL}, - {"read", RGW_CAP_READ}, - {"write", RGW_CAP_WRITE}, - {NULL, 0} }; + uint32_t flag; +}; -int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm) +static int parse_list_of_flags(struct rgw_name_to_flag *mapping, + const string& str, uint32_t *perm) { list strs; get_str_list(str, strs); @@ -654,9 +652,9 @@ int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm) uint32_t v = 0; for (iter = strs.begin(); iter != strs.end(); ++iter) { string& s = *iter; - for (int i = 0; cap_names[i].type_name; i++) { - if (s.compare(cap_names[i].type_name) == 0) - v |= cap_names[i].perm; + for (int i = 0; mapping[i].type_name; i++) { + if (s.compare(mapping[i].type_name) == 0) + v |= mapping[i].flag; } } @@ -664,6 +662,16 @@ int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm) return 0; } +static struct rgw_name_to_flag cap_names[] = { {"*", RGW_CAP_ALL}, + {"read", RGW_CAP_READ}, + {"write", RGW_CAP_WRITE}, + {NULL, 0} }; + +int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm) +{ + return parse_list_of_flags(cap_names, str, perm); +} + int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm) { int pos = cap.find('='); @@ -777,12 +785,12 @@ void RGWUserCaps::dump(Formatter *f, const char *name) const uint32_t perm = iter->second; string perm_str; for (int i=0; cap_names[i].type_name; i++) { - if ((perm & cap_names[i].perm) == cap_names[i].perm) { + if ((perm & cap_names[i].flag) == cap_names[i].flag) { if (perm_str.size()) perm_str.append(", "); perm_str.append(cap_names[i].type_name); - perm &= ~cap_names[i].perm; + perm &= ~cap_names[i].flag; } } if (perm_str.empty()) @@ -833,3 +841,16 @@ int RGWUserCaps::check_cap(const string& cap, uint32_t perm) return 0; } + +static struct rgw_name_to_flag op_type_mapping[] = { {"*", RGW_OP_TYPE_ALL}, + {"read", RGW_OP_TYPE_READ}, + {"write", RGW_OP_TYPE_WRITE}, + {"delete", RGW_OP_TYPE_DELETE}, + {NULL, 0} }; + + +int rgw_parse_op_type_list(const string& str, uint32_t *perm) +{ + return parse_list_of_flags(op_type_mapping, str, perm); +} + diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index f4878f4b291..9b761810286 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -79,6 +79,12 @@ using ceph::crypto::MD5; #define RGW_SUSPENDED_USER_AUID (uint64_t)-2 +#define RGW_OP_TYPE_READ 0x01 +#define RGW_OP_TYPE_WRITE 0x02 +#define RGW_OP_TYPE_DELETE 0x04 + +#define RGW_OP_TYPE_ALL (RGW_OP_TYPE_READ | RGW_OP_TYPE_WRITE | RGW_OP_TYPE_DELETE) + #define RGW_DEFAULT_MAX_BUCKETS 1000 #define STATUS_CREATED 1900 @@ -381,12 +387,13 @@ struct RGWUserInfo map subusers; __u8 suspended; uint32_t max_buckets; + uint32_t op_mask; RGWUserCaps caps; - RGWUserInfo() : auid(0), suspended(0), max_buckets(RGW_DEFAULT_MAX_BUCKETS) {} + RGWUserInfo() : auid(0), suspended(0), max_buckets(RGW_DEFAULT_MAX_BUCKETS), op_mask(RGW_OP_TYPE_ALL) {} void encode(bufferlist& bl) const { - ENCODE_START(11, 9, bl); + ENCODE_START(12, 9, bl); ::encode(auid, bl); string access_key; string secret_key; @@ -417,6 +424,7 @@ struct RGWUserInfo ::encode(swift_keys, bl); ::encode(max_buckets, bl); ::encode(caps, bl); + ::encode(op_mask, bl); ENCODE_FINISH(bl); } void decode(bufferlist::iterator& bl) { @@ -462,6 +470,11 @@ struct RGWUserInfo if (struct_v >= 11) { ::decode(caps, bl); } + if (struct_v >= 12) { + ::decode(op_mask, bl); + } else { + op_mask = RGW_OP_TYPE_ALL; + } DECODE_FINISH(bl); } void dump(Formatter *f) const; @@ -1046,4 +1059,6 @@ extern void calc_hmac_sha1(const char *key, int key_len, const char *msg, int msg_len, char *dest); /* destination should be CEPH_CRYPTO_HMACSHA1_DIGESTSIZE bytes long */ +extern int rgw_parse_op_type_list(const string& str, uint32_t *perm); + #endif diff --git a/src/rgw/rgw_json_enc.cc b/src/rgw/rgw_json_enc.cc index 04dfb67c71d..e26299f24ad 100644 --- a/src/rgw/rgw_json_enc.cc +++ b/src/rgw/rgw_json_enc.cc @@ -235,7 +235,7 @@ static struct rgw_flags_desc rgw_perms[] = { { 0, NULL } }; -static void perm_to_str(uint32_t mask, char *buf, int len) +static void mask_to_str(rgw_flags_desc *mask_list, uint32_t mask, char *buf, int len) { const char *sep = ""; int pos = 0; @@ -245,8 +245,8 @@ static void perm_to_str(uint32_t mask, char *buf, int len) } while (mask) { uint32_t orig_mask = mask; - for (int i = 0; rgw_perms[i].mask; i++) { - struct rgw_flags_desc *desc = &rgw_perms[i]; + for (int i = 0; mask_list[i].mask; i++) { + struct rgw_flags_desc *desc = &mask_list[i]; if ((mask & desc->mask) == desc->mask) { pos += snprintf(buf + pos, len - pos, "%s%s", sep, desc->str); if (pos == len) @@ -262,6 +262,23 @@ static void perm_to_str(uint32_t mask, char *buf, int len) } } +static void perm_to_str(uint32_t mask, char *buf, int len) +{ + return mask_to_str(rgw_perms, mask, buf, len); +} + +static struct rgw_flags_desc op_type_flags[] = { + { RGW_OP_TYPE_READ, "read" }, + { RGW_OP_TYPE_WRITE, "write" }, + { RGW_OP_TYPE_DELETE, "delete" }, + { 0, NULL } +}; + +static void op_type_to_str(uint32_t mask, char *buf, int len) +{ + return mask_to_str(op_type_flags, mask, buf, len); +} + void RGWSubUser::dump(Formatter *f) const { encode_json("id", name, f); @@ -340,6 +357,10 @@ void RGWUserInfo::dump(Formatter *f) const encode_json_map("swift_keys", NULL, "key", NULL, user_info_dump_swift_key,(void *)this, swift_keys, f); encode_json("caps", caps, f); + + char buf[256]; + op_type_to_str(op_mask, buf, sizeof(buf)); + encode_json("op_mask", (const char *)buf, f); } @@ -380,6 +401,10 @@ void RGWUserInfo::decode_json(JSONObj *obj) JSONDecoder::decode_json("subusers", subusers, decode_subusers, obj); JSONDecoder::decode_json("caps", caps, obj); + + string mask_str; + JSONDecoder::decode_json("op_mask", mask_str, obj); + rgw_parse_op_type_list(mask_str, &op_mask); } void rgw_bucket::dump(Formatter *f) const diff --git a/src/rgw/rgw_main.cc b/src/rgw/rgw_main.cc index c80037520b3..3d6bfb908f0 100644 --- a/src/rgw/rgw_main.cc +++ b/src/rgw/rgw_main.cc @@ -336,6 +336,13 @@ void RGWProcess::handle_request(RGWRequest *req) req->log(s, "reading the cors attr"); handler->read_cors_config(); + req->log(s, "verifying op mask"); + ret = op->verify_op_mask(); + if (ret < 0) { + abort_early(s, ret); + goto done; + } + req->log(s, "verifying op permissions"); ret = op->verify_permission(); if (ret < 0) { diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 0c157c561fb..acf31f51ecb 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -339,6 +339,20 @@ int RGWGetObj::verify_permission() } +int RGWOp::verify_op_mask() +{ + uint32_t required_mask = op_mask(); + + ldout(s->cct, 20) << "required_mask= " << required_mask << " user.op_mask=" << s->user.op_mask << dendl; + + if ((s->user.op_mask & required_mask) != required_mask) { + return -EPERM; + } + + return 0; +} + + int RGWGetObj::read_user_manifest_part(rgw_bucket& bucket, RGWObjEnt& ent, RGWAccessControlPolicy *bucket_policy, off_t start_ofs, off_t end_ofs) { ldout(s->cct, 0) << "user manifest obj=" << ent.name << dendl; @@ -1780,6 +1794,11 @@ int RGWGetACLs::verify_permission() return 0; } +uint32_t RGWGetACLs::op_mask() +{ + return RGW_OP_TYPE_READ; +} + void RGWGetACLs::execute() { stringstream ss; @@ -1805,6 +1824,11 @@ int RGWPutACLs::verify_permission() return 0; } +uint32_t RGWPutACLs::op_mask() +{ + return RGW_OP_TYPE_WRITE; +} + void RGWPutACLs::execute() { bufferlist bl; diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index b3e8c86e3f0..7fc9446c57c 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -29,7 +29,6 @@ class RGWHandler; void rgw_get_request_metadata(struct req_state *s, map& attrs); int rgw_build_policies(RGWRados *store, struct req_state *s, bool only_bucket, bool prefetch_data); - /** * Provide the base class for all ops. */ @@ -50,10 +49,13 @@ public: virtual int verify_params() { return 0; } virtual bool prefetch_data() { return false; } virtual int verify_permission() = 0; + virtual int verify_op_mask(); virtual void execute() = 0; virtual void send_response() {} virtual void complete() { send_response(); } virtual const char *name() = 0; + + virtual uint32_t op_mask() { return 0; } }; class RGWGetObj : public RGWOp { @@ -119,6 +121,7 @@ public: virtual int send_response_data(bufferlist& bl, off_t ofs, off_t len) = 0; virtual const char *name() { return "get_obj"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; #define RGW_LIST_BUCKETS_LIMIT_MAX 10000 @@ -148,6 +151,7 @@ public: virtual bool should_get_stats() { return false; } virtual const char *name() { return "list_buckets"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWStatAccount : public RGWOp { @@ -172,6 +176,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "stat_account"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWListBucket : public RGWOp { @@ -203,6 +208,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "list_bucket"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWGetBucketLogging : public RGWOp { @@ -213,6 +219,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "get_bucket_logging"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWStatBucket : public RGWOp { @@ -229,6 +236,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "stat_bucket"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWCreateBucket : public RGWOp { @@ -248,6 +256,7 @@ public: virtual int get_params() { return 0; } virtual void send_response() = 0; virtual const char *name() { return "create_bucket"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWDeleteBucket : public RGWOp { @@ -262,6 +271,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "delete_bucket"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; } }; class RGWPutObjProcessor @@ -330,6 +340,7 @@ public: virtual int get_data(bufferlist& bl) = 0; virtual void send_response() = 0; virtual const char *name() { return "put_obj"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWPostObj : public RGWOp { @@ -371,6 +382,7 @@ public: virtual int get_data(bufferlist& bl) = 0; virtual void send_response() = 0; virtual const char *name() { return "post_obj"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWPutMetadata : public RGWOp { @@ -398,6 +410,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "put_obj_metadata"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWDeleteObj : public RGWOp { @@ -412,6 +425,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "delete_obj"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; } }; class RGWCopyObj : public RGWOp { @@ -473,6 +487,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "copy_obj"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWGetACLs : public RGWOp { @@ -488,6 +503,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "get_acls"; } + virtual uint32_t op_mask(); }; class RGWPutACLs : public RGWOp { @@ -513,6 +529,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "put_acls"; } + virtual uint32_t op_mask(); }; class RGWGetCORS : public RGWOp { @@ -528,6 +545,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "get_cors"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWPutCORS : public RGWOp { @@ -552,6 +570,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "put_cors"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWDeleteCORS : public RGWOp { @@ -566,6 +585,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "delete_cors"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWOptionsCORS : public RGWOp { @@ -585,6 +605,7 @@ public: void get_response_params(string& allowed_hdrs, string& exp_hdrs, unsigned *max_age); virtual void send_response() = 0; virtual const char *name() { return "options_cors"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWInitMultipart : public RGWOp { @@ -608,6 +629,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "init_multipart"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWCompleteMultipart : public RGWOp { @@ -636,6 +658,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "complete_multipart"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_WRITE; } }; class RGWAbortMultipart : public RGWOp { @@ -650,6 +673,7 @@ public: virtual void send_response() = 0; virtual const char *name() { return "abort_multipart"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; } }; class RGWListMultipart : public RGWOp { @@ -678,6 +702,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "list_multipart"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; #define MP_META_SUFFIX ".meta" @@ -786,6 +811,7 @@ public: virtual int get_params() = 0; virtual void send_response() = 0; virtual const char *name() { return "list_bucket_multiparts"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_READ; } }; class RGWDeleteMultiObj : public RGWOp { @@ -818,6 +844,7 @@ public: virtual void send_partial_response(pair& result) = 0; virtual void end_response() = 0; virtual const char *name() { return "multi_object_delete"; } + virtual uint32_t op_mask() { return RGW_OP_TYPE_DELETE; } }; diff --git a/src/rgw/rgw_user.cc b/src/rgw/rgw_user.cc index 9a75ba69b9e..8a636d1cae3 100644 --- a/src/rgw/rgw_user.cc +++ b/src/rgw/rgw_user.cc @@ -1639,6 +1639,9 @@ int RGWUser::execute_add(RGWUserAdminOpState& op_state, std::string *err_msg) user_info.max_buckets = op_state.get_max_buckets(); user_info.suspended = op_state.get_suspension_status(); + if (op_state.op_mask_specified) + user_info.op_mask = op_state.get_op_mask(); + // update the request op_state.set_user_info(user_info); op_state.set_populated(); @@ -1835,6 +1838,9 @@ int RGWUser::execute_modify(RGWUserAdminOpState& op_state, std::string *err_msg) if (op_state.max_buckets_specified) user_info.max_buckets = max_buckets; + if (op_state.op_mask_specified) + user_info.op_mask = op_state.get_op_mask(); + if (op_state.has_suspension_op()) { __u8 suspended = op_state.get_suspension_status(); user_info.suspended = suspended; diff --git a/src/rgw/rgw_user.h b/src/rgw/rgw_user.h index 3b277000b57..804f67c4565 100644 --- a/src/rgw/rgw_user.h +++ b/src/rgw/rgw_user.h @@ -139,6 +139,7 @@ struct RGWUserAdminOpState { uint32_t max_buckets; __u8 suspended; std::string caps; + uint32_t op_mask; // subuser attributes std::string subuser; @@ -167,6 +168,7 @@ struct RGWUserAdminOpState { bool user_email_specified; bool max_buckets_specified; bool perm_specified; + bool op_mask_specified; bool caps_specified; bool suspension_op; bool key_op; @@ -243,6 +245,10 @@ struct RGWUserAdminOpState { perm_mask = perm; perm_specified = true; } + void set_op_mask(uint32_t mask) { + op_mask = mask; + op_mask_specified = true; + } void set_key_type(int32_t type) { key_type = type; type_specified = true; @@ -294,6 +300,7 @@ struct RGWUserAdminOpState { bool has_caps_op() { return caps_specified; }; bool has_suspension_op() { return suspension_op; }; bool has_subuser_perm() { return perm_specified; }; + bool has_op_mask() { return op_mask_specified; }; bool will_gen_access() { return gen_access; }; bool will_gen_secret() { return gen_secret; }; bool will_gen_subuser() { return gen_subuser; }; @@ -313,6 +320,7 @@ struct RGWUserAdminOpState { int32_t get_key_type() {return key_type; }; uint32_t get_subuser_perm() { return perm_mask; }; uint32_t get_max_buckets() { return max_buckets; }; + uint32_t get_op_mask() { return op_mask; }; std::string get_user_id() { return user_id; }; std::string get_subuser() { return subuser; }; @@ -389,6 +397,7 @@ struct RGWUserAdminOpState { user_email_specified = false; max_buckets_specified = false; perm_specified = false; + op_mask_specified = false; suspension_op = false; key_op = false; populated = false; -- cgit v1.2.1