]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
RGW: validate bucket names and object names
authorColin Patrick McCabe <cmccabe@alumni.cmu.edu>
Wed, 23 Mar 2011 17:02:17 +0000 (10:02 -0700)
committerColin Patrick McCabe <cmccabe@alumni.cmu.edu>
Thu, 24 Mar 2011 00:33:51 +0000 (17:33 -0700)
Signed-off-by: Colin McCabe <colin.mccabe@dreamhost.com>
do_autogen.sh
src/rgw/rgw_main.cc
src/rgw/rgw_rest.cc
src/rgw/rgw_rest.h

index d23ce7411e2e4fd79969803dd806ce0b84dcdf1f..b0a7f0a27626b041d225029f482b704b0ef47add 100755 (executable)
@@ -101,5 +101,5 @@ export CXXFLAGS
 
 ./configure \
 --prefix=/usr --sbindir=/sbin --localstatedir=/var --sysconfdir=/etc \
---with-gtk2=yes --with-debug $with_profiler \
+--with-gtk2=yes --with-debug $with_profiler --with-cryptopp \
 || die "configure failed"
index 86b52295e399a046ab3586cc0c1e50dc160f65a2..628a3664c185d8119c11911082a709d84e87c0bc 100644 (file)
@@ -78,10 +78,16 @@ int main(int argc, const char **argv)
 
   while (FCGX_Accept(&fcgx.in, &fcgx.out, &fcgx.err, &fcgx.envp) >= 0) 
   {
-    RGWHandler *handler = RGWHandler_REST::init_handler(&s, &fcgx);
     RGWOp *op;
+    int init_error = 0;
+    RGWHandler *handler = RGWHandler_REST::init_handler(&s, &fcgx, &init_error);
     int ret;
     
+    if (init_error != 0) {
+      abort_early(&s, init_error);
+      goto done;
+    }
+
     if (!handler->authorize(&s)) {
       RGW_LOG(10) << "failed to authorize request" << std::endl;
       abort_early(&s, -EPERM);
index f18c02d603411608707aed05322b2dc93b81af87..e2dd0e1c4b683eb260a29a4c2c98779a7903622a 100644 (file)
@@ -1,5 +1,6 @@
 #include <errno.h>
 
+#include "common/utf8.h"
 #include "rgw_common.h"
 #include "rgw_access.h"
 #include "rgw_op.h"
@@ -33,12 +34,17 @@ struct errno_http {
   const char *default_code;
 };
 
+#define INVALID_BUCKET_NAME 2000
+#define INVALID_OBJECT_NAME 2001
+
 const static struct errno_http hterrs[] = {
     { 0, "200", "" },
     { 201, "201", "Created" },
     { 204, "204", "NoContent" },
     { 206, "206", "" },
     { EINVAL, "400", "InvalidArgument" },
+    { INVALID_BUCKET_NAME, "400", "InvalidBucketName" },
+    { INVALID_OBJECT_NAME, "400", "InvalidObjectName" },
     { EACCES, "403", "AccessDenied" },
     { EPERM, "403", "AccessDenied" },
     { ENOENT, "404", "NoSuchKey" },
@@ -511,8 +517,66 @@ static void init_auth_info(struct req_state *s)
   }
 }
 
-void RGWHandler_REST::init_rest(struct req_state *s, struct fcgx_state *fcgx)
+// This function enforces some fairly strict limits on bucket names.  These
+// correspond to Amazon's "recommendations", and are stricter than its actual
+// hard-and-fast rules about bucket names.  This way, all our buckets will be
+// accessible via the virtual host calling format, rather than only some of
+// them.
+static int validate_bucket_name(const char *bucket)
+{
+  int len = strlen(bucket);
+  if (len < 3) {
+    if (len == 0)
+      return 0;
+    // Name too short
+    return INVALID_BUCKET_NAME;
+  }
+  else if (len > 63) {
+    // Name too long
+    return INVALID_BUCKET_NAME;
+  }
+  for (const char *s = bucket; *s; ++s) {
+    char c = *s;
+    if (islower(c))
+      continue;
+    if (isdigit(c))
+      continue;
+    if (c == '-')
+      continue;
+    // Invalid character
+    // Yes, we are even excluding capital letters.
+    return INVALID_BUCKET_NAME;
+  }
+  // can't have dashes at the beginning or the end.
+  if (bucket[0] == '-')
+    return INVALID_BUCKET_NAME;
+  if (bucket[len-1] == '-')
+    return INVALID_BUCKET_NAME;
+  return 0;
+}
+
+// "The name for a key is a sequence of Unicode characters whose UTF-8 encoding
+// is at most 1024 bytes long."
+// However, we can still have control characters and other nasties in there.
+// Just as long as they're utf-8 nasties.
+static int validate_object_name(const char *object)
 {
+  int len = strlen(object);
+  if (len > 1024) {
+    // Name too long
+    return INVALID_OBJECT_NAME;
+  }
+
+  if (check_utf8(object, len)) {
+    // Object names must be valid UTF-8.
+    return INVALID_OBJECT_NAME;
+  }
+  return 0;
+}
+
+int RGWHandler_REST::init_rest(struct req_state *s, struct fcgx_state *fcgx)
+{
+  int ret = 0;
   RGWHandler::init_state(s, fcgx);
 
   s->path_name = FCGX_GetParam("SCRIPT_NAME", s->fcgx->envp);
@@ -541,6 +605,12 @@ void RGWHandler_REST::init_rest(struct req_state *s, struct fcgx_state *fcgx)
     s->op = OP_UNKNOWN;
 
   init_entities_from_header(s);
+  ret = validate_bucket_name(s->bucket_str.c_str());
+  if (ret)
+    return ret;
+  ret = validate_object_name(s->object_str.c_str());
+  if (ret)
+    return ret;
   RGW_LOG(10) << "s->object=" << (s->object ? s->object : "<NULL>") << " s->bucket=" << (s->bucket ? s->bucket : "<NULL>") << endl;
 
   init_auth_info(s);
@@ -557,6 +627,7 @@ void RGWHandler_REST::init_rest(struct req_state *s, struct fcgx_state *fcgx)
     const char *expect = FCGX_GetParam("HTTP_EXPECT", s->fcgx->envp);
     s->expect_cont = (expect && !strcasecmp(expect, "100-continue"));
   }
+  return ret;
 }
 
 int RGWHandler_REST::read_permissions()
@@ -613,11 +684,12 @@ RGWOp *RGWHandler_REST::get_op()
 }
 
 
-RGWHandler *RGWHandler_REST::init_handler(struct req_state *s, struct fcgx_state *fcgx)
+RGWHandler *RGWHandler_REST::init_handler(struct req_state *s, struct fcgx_state *fcgx,
+                                         int *init_error)
 {
   RGWHandler *handler;
 
-  init_rest(s, fcgx);
+  *init_error = init_rest(s, fcgx);
 
   if (s->prot_flags & RGW_REST_OPENSTACK)
     handler = &rgwhandler_os;
index 583b6be7efd728b6c2e14c4f329ea423c3da3dfc..09403f8064c15752584503a8514aeef625d427b0 100644 (file)
@@ -99,14 +99,15 @@ protected:
   virtual RGWOp *get_create_op(struct req_state *s) = 0;
   virtual RGWOp *get_delete_op(struct req_state *s) = 0;
 
-  static void init_rest(struct req_state *s, struct fcgx_state *fcgx);
+  static int init_rest(struct req_state *s, struct fcgx_state *fcgx);
 public:
   int read_permissions();
   RGWOp *get_op();
 
   virtual bool authorize(struct req_state *s) = 0;
 
-  static RGWHandler *init_handler(struct req_state *s, struct fcgx_state *fcgx);
+  static RGWHandler *init_handler(struct req_state *s, struct fcgx_state *fcgx,
+                                 int *init_error);
 };
 
 extern void dump_errno(struct req_state *s, int err, struct rgw_err *rgwerr = NULL);