summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYehuda Sadeh <yehuda@inktank.com>2012-11-08 17:19:18 -0800
committerYehuda Sadeh <yehuda@inktank.com>2012-11-08 17:19:18 -0800
commit2475d0143b17ad4470009b62682d0e5845673e8c (patch)
tree08f7bba903bd2c247b03b250296079eae607d78f
parente5124ced11e4150bae1b4b9b3fcc30b1deebbcac (diff)
parentfc0d6d345320057182adfac85681241c976c944b (diff)
downloadceph-2475d0143b17ad4470009b62682d0e5845673e8c.tar.gz
Merge branch 'wip-keystone' into wip-rgw-integration
Conflicts: debian/control Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
-rw-r--r--debian/control2
-rw-r--r--doc/radosgw/config.rst43
-rw-r--r--src/.gitignore1
-rw-r--r--src/Makefile.am23
-rw-r--r--src/common/ceph_crypto.cc12
-rw-r--r--src/common/ceph_crypto.h4
-rw-r--r--src/common/ceph_crypto_cms.cc360
-rw-r--r--src/common/ceph_crypto_cms.h10
-rw-r--r--src/common/common_init.cc2
-rw-r--r--src/common/config_opts.h15
-rw-r--r--src/json_spirit/json_spirit_reader_template.h2
-rw-r--r--src/rgw/rgw_common.cc9
-rw-r--r--src/rgw/rgw_common.h7
-rw-r--r--src/rgw/rgw_http_client.cc76
-rw-r--r--src/rgw/rgw_http_client.h23
-rw-r--r--src/rgw/rgw_json.cc54
-rw-r--r--src/rgw/rgw_json.h6
-rw-r--r--src/rgw/rgw_main.cc10
-rw-r--r--src/rgw/rgw_rest_swift.cc2
-rw-r--r--src/rgw/rgw_swift.cc685
-rw-r--r--src/rgw/rgw_swift.h73
-rw-r--r--src/test/ceph_crypto.cc6
-rw-r--r--src/test/crypto.cc2
23 files changed, 1339 insertions, 88 deletions
diff --git a/debian/control b/debian/control
index dbf95fa944f..45030953a98 100644
--- a/debian/control
+++ b/debian/control
@@ -6,7 +6,7 @@ Vcs-Git: git://github.com/ceph/ceph.git
Vcs-Browser: https://github.com/ceph/ceph
Maintainer: Laszlo Boszormenyi (GCS) <gcs@debian.hu>
Uploaders: Sage Weil <sage@newdream.net>
-Build-Depends: debhelper (>= 6.0.7~), autotools-dev, autoconf, automake, libfuse-dev, libboost-dev (>= 1.34), libedit-dev, libnss3-dev, libtool, libexpat1-dev, libfcgi-dev, libatomic-ops-dev, libgoogle-perftools-dev [i386 amd64], pkg-config, libcurl4-gnutls-dev, libkeyutils-dev, uuid-dev, libaio-dev, python (>= 2.6.6-3~), libxml2-dev, javahelper, default-jdk
+Build-Depends: debhelper (>= 6.0.7~), autotools-dev, autoconf, automake, libfuse-dev, libboost-dev (>= 1.34), libboost-thread-dev, libedit-dev, libnss3-dev, libtool, libexpat1-dev, libfcgi-dev, libatomic-ops-dev, libgoogle-perftools-dev [i386 amd64], pkg-config, libcurl4-gnutls-dev, libkeyutils-dev, uuid-dev, libaio-dev, python (>= 2.6.6-3~), libxml2-dev, javahelper, default-jdk
Standards-Version: 3.9.3
Package: ceph
diff --git a/doc/radosgw/config.rst b/doc/radosgw/config.rst
index 3e14a20e1fe..c9605d06cef 100644
--- a/doc/radosgw/config.rst
+++ b/doc/radosgw/config.rst
@@ -291,3 +291,46 @@ RGW's ``user:subuser`` tuple maps to the ``tenant:user`` tuple expected by Swift
built-in Swift authentication (``-V 1.0``) at this point. There is
currently no way to make RGW authenticate users via OpenStack
Identity Service (Keystone).
+
+Integrating with OpenStack Keystone
+===================================
+
+It is possible to integrate RGW with Keystone, the OpenStack identity service. This sets up RGW to accept Keystone
+as the users authority. A user that Keystone authorizes to access RGW will also be automatically created on RGW
+(if didn't exist beforehand). A token that Keystone validates will be considered as valid by RGW.
+
+The following config options are available for Keystone integration::
+
+ [client.radosgw.gateway]
+ rgw keystone url = {keystone server url}
+ rgw keystone admin token = {keystone admin token}
+ rgw keystone accepted roles = {accepted user roles}
+ rgw keystone token cache size = {number of tokens to cache}
+ rgw keystone revocation interval = {number of seconds before checking revoked tickets}
+ nss db path = {path to nss db}
+
+An RGW user is mapped into a Keystone ``tenant``. A Keystone user has different roles assigned to it on possibly more
+than a single tenant. When RGW gets the ticket, it looks at the tenant, and the user roles that are assigned to
+that ticket, and accepts/rejects the request according to the ``rgw keystone accepted roles`` configurable.
+
+Keystone itself needs to be configured to point to RGW as an object-storage endpoint::
+
+ keystone service-create --name swift --type-object-store
+ keystone endpoint-create --service-id <id> --public-url http://radosgw.example.com/swift/v1
+
+
+The keystone url is the Keystone admin RESTful api url. The admin token is the token that is configured internally
+in Keystone for admin requests.
+
+RGW will query Keystone periodically for a list of revoked tokens. These requests are encoded and signed. Also, Keystone
+may be configured to provide self signed tokens, which are also encoded and signed. RGW needs to be able to decode
+and verify these signed messages, and it requires it to be set up appropriately. Currently, RGW will be able to do
+it only if it was compiled with ``--with-nss``. It also requires converting the OpenSSL certificates that Keystone uses
+for creating the requests to the nss db format, for example::
+
+ mkdir /var/ceph/nss
+
+ openssl x509 -in /etc/keystone/ssl/certs/ca.pem -pubkey | \
+ certutil -d /var/ceph/nss -A -n ca -t "TCu,Cu,Tuw"
+ openssl x509 -in /etc/keystone/ssl/certs/signing_cert.pem -pubkey | \
+ certutil -d /var/ceph/nss -A -n signing_cert -t "TCu,Cu,Tuw"
diff --git a/src/.gitignore b/src/.gitignore
index 5a4216503ff..7548b5e47ae 100644
--- a/src/.gitignore
+++ b/src/.gitignore
@@ -23,6 +23,7 @@
/radosgw
/radosgw-admin
/rbdtool
+/rgw_jsonparser
/rgw_multiparser
/streamtest
/bench_log
diff --git a/src/Makefile.am b/src/Makefile.am
index 51abc35fa46..a42f02103f4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -43,7 +43,7 @@ check-local:
$(srcdir)/test/encoding/check-generated.sh
$(srcdir)/test/encoding/readable.sh ../ceph-object-corpus
-EXTRALIBS = -luuid
+EXTRALIBS = -luuid -lboost_thread
if FREEBSD
EXTRALIBS += -lexecinfo
endif
@@ -207,12 +207,15 @@ test_ioctls_SOURCES = client/test_ioctls.c
bin_DEBUGPROGRAMS += test_ioctls
dupstore_SOURCES = dupstore.cc
+dupstore_CXXFLAGS= ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS}
dupstore_LDADD = $(LIBOS_LDA) $(LIBGLOBAL_LDA)
streamtest_SOURCES = streamtest.cc
+streamtest_CXXFLAGS= ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS}
streamtest_LDADD = $(LIBOS_LDA) $(LIBGLOBAL_LDA)
bin_DEBUGPROGRAMS += dupstore streamtest
test_trans_SOURCES = test_trans.cc
+test_trans_CXXFLAGS= ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS}
test_trans_LDADD = $(LIBOS_LDA) $(LIBGLOBAL_LDA)
bin_DEBUGPROGRAMS += test_trans
@@ -362,6 +365,7 @@ radosgw_SOURCES = \
rgw/rgw_rest_swift.cc \
rgw/rgw_rest_s3.cc \
rgw/rgw_rest_usage.cc \
+ rgw/rgw_http_client.cc \
rgw/rgw_swift.cc \
rgw/rgw_swift_auth.cc \
rgw/rgw_main.cc
@@ -920,7 +924,7 @@ bin_DEBUGPROGRAMS += test_libcephfs
test_filestore_SOURCES = test/filestore/store_test.cc
test_filestore_LDFLAGS = ${AM_LDFLAGS}
test_filestore_LDADD = ${UNITTEST_STATIC_LDADD} $(LIBOS_LDA) $(LIBGLOBAL_LDA)
-test_filestore_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE)
+test_filestore_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE) ${CRYPTO_CXXFLAGS}
bin_DEBUGPROGRAMS += test_filestore
test_filestore_workloadgen_SOURCES = \
@@ -928,11 +932,12 @@ test_filestore_workloadgen_SOURCES = \
test/filestore/TestFileStoreState.cc
test_filestore_workloadgen_LDFLAGS = ${AM_LDFLAGS}
test_filestore_workloadgen_LDADD = $(LIBOS_LDA) $(LIBGLOBAL_LDA)
+test_filestore_workloadgen_CXXFLAGS = ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS}
bin_DEBUGPROGRAMS += test_filestore_workloadgen
test_filestore_idempotent_SOURCES = test/filestore/test_idempotent.cc test/filestore/FileStoreTracker.cc test/common/ObjectContents.cc
test_filestore_idempotent_LDADD = $(LIBOS_LDA) $(LIBGLOBAL_LDA)
-test_filestore_idempotent_CXXFLAGS = $(LEVELDB_INCLUDE)
+test_filestore_idempotent_CXXFLAGS = ${CRYPTO_CXXFLAGS} $(LEVELDB_INCLUDE)
bin_DEBUGPROGRAMS += test_filestore_idempotent
test_filestore_idempotent_sequence_SOURCES = \
@@ -940,13 +945,14 @@ test_filestore_idempotent_sequence_SOURCES = \
test/filestore/DeterministicOpSequence.cc \
test/filestore/TestFileStoreState.cc \
test/filestore/FileStoreDiff.cc
+test_filestore_idempotent_sequence_CXXFLAGS = ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS}
test_filestore_idempotent_sequence_LDADD = $(LIBOS_LDA) $(LIBGLOBAL_LDA)
bin_DEBUGPROGRAMS += test_filestore_idempotent_sequence
xattr_bench_SOURCES = test/xattr_bench.cc
xattr_bench_LDFLAGS = ${AM_LDFLAGS}
xattr_bench_LDADD = ${UNITTEST_STATIC_LDADD} $(LIBOS_LDA) $(LIBGLOBAL_LDA)
-xattr_bench_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE)
+xattr_bench_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE) ${CRYPTO_CXXFLAGS}
bin_DEBUGPROGRAMS += xattr_bench
test_filejournal_SOURCES = test/test_filejournal.cc
@@ -964,13 +970,13 @@ bin_DEBUGPROGRAMS += test_stress_watch
test_object_map_SOURCES = test/ObjectMap/test_object_map.cc test/ObjectMap/KeyValueDBMemory.cc os/DBObjectMap.cc os/LevelDBStore.cc
test_object_map_LDFLAGS = ${AM_LDFLAGS}
test_object_map_LDADD = ${UNITTEST_STATIC_LDADD} $(LIBOS_LDA) $(LIBGLOBAL_LDA)
-test_object_map_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE)
+test_object_map_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE) ${CRYPTO_CXXFLAGS}
bin_DEBUGPROGRAMS += test_object_map
test_keyvaluedb_atomicity_SOURCES = test/ObjectMap/test_keyvaluedb_atomicity.cc os/LevelDBStore.cc
test_keyvaluedb_atomicity_LDFLAGS = ${AM_LDFLAGS}
test_keyvaluedb_atomicity_LDADD = ${UNITTEST_STATIC_LDADD} $(LIBOS_LDA) $(LIBGLOBAL_LDA)
-test_keyvaluedb_atomicity_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE)
+test_keyvaluedb_atomicity_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE) ${CRYPTO_CXXFLAGS}
bin_DEBUGPROGRAMS += test_keyvaluedb_atomicity
test_keyvaluedb_iterators_SOURCES = test/ObjectMap/test_keyvaluedb_iterators.cc \
@@ -978,7 +984,7 @@ test_keyvaluedb_iterators_SOURCES = test/ObjectMap/test_keyvaluedb_iterators.cc
os/LevelDBStore.cc
test_keyvaluedb_iterators_LDFLAGS = ${AM_LDFLAGS}
test_keyvaluedb_iterators_LDADD = ${UNITTEST_STATIC_LDADD} $(LIBOS_LDA) $(LIBGLOBAL_LDA)
-test_keyvaluedb_iterators_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE)
+test_keyvaluedb_iterators_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS} $(LEVELDB_INCLUDE) ${CRYPTO_CXXFLAGS}
bin_DEBUGPROGRAMS += test_keyvaluedb_iterators
test_cfuse_cache_invalidate_SOURCES = test/test_cfuse_cache_invalidate.cc
@@ -1255,6 +1261,7 @@ libcommon_files = \
common/hex.cc \
common/entity_name.cc \
common/ceph_crypto.cc \
+ common/ceph_crypto_cms.cc \
common/ipaddr.cc \
common/pick_address.cc \
include/addr_parsing.c \
@@ -1490,6 +1497,7 @@ noinst_HEADERS = \
common/config_obs.h\
common/config_opts.h\
common/ceph_crypto.h\
+ common/ceph_crypto_cms.h\
common/utf8.h\
common/mime.h\
common/pick_address.h\
@@ -1829,6 +1837,7 @@ noinst_HEADERS = \
rgw/rgw_gc.h\
rgw/rgw_multi_del.h\
rgw/rgw_op.h\
+ rgw/rgw_http_client.h\
rgw/rgw_swift.h\
rgw/rgw_swift_auth.h\
rgw/rgw_rados.h\
diff --git a/src/common/ceph_crypto.cc b/src/common/ceph_crypto.cc
index 95909d07e74..3f04349c20b 100644
--- a/src/common/ceph_crypto.cc
+++ b/src/common/ceph_crypto.cc
@@ -12,6 +12,8 @@
*
*/
+#include "common/config.h"
+#include "common/ceph_context.h"
#include "ceph_crypto.h"
#include "auth/Crypto.h"
@@ -21,7 +23,7 @@
void ceph::crypto::shutdown();
#ifdef USE_CRYPTOPP
-void ceph::crypto::init()
+void ceph::crypto::init(CephContext *cct)
{
}
@@ -36,10 +38,14 @@ ceph::crypto::HMACSHA1::~HMACSHA1()
#elif USE_NSS
-void ceph::crypto::init()
+void ceph::crypto::init(CephContext *cct)
{
SECStatus s;
- s = NSS_NoDB_Init(NULL);
+ if (cct->_conf->nss_db_path.empty()) {
+ s = NSS_NoDB_Init(NULL);
+ } else {
+ s = NSS_Init(cct->_conf->nss_db_path.c_str());
+ }
assert(s == SECSuccess);
}
diff --git a/src/common/ceph_crypto.h b/src/common/ceph_crypto.h
index 52b98b83a63..c55359431d4 100644
--- a/src/common/ceph_crypto.h
+++ b/src/common/ceph_crypto.h
@@ -21,7 +21,7 @@
namespace ceph {
namespace crypto {
void assert_init();
- void init();
+ void init(CephContext *cct);
void shutdown();
using CryptoPP::Weak::MD5;
@@ -56,7 +56,7 @@ typedef unsigned char byte;
namespace ceph {
namespace crypto {
void assert_init();
- void init();
+ void init(CephContext *cct);
void shutdown();
class Digest {
private:
diff --git a/src/common/ceph_crypto_cms.cc b/src/common/ceph_crypto_cms.cc
new file mode 100644
index 00000000000..4d7a4ef598b
--- /dev/null
+++ b/src/common/ceph_crypto_cms.cc
@@ -0,0 +1,360 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Netscape security libraries.
+ *
+ * The Initial Developer of the Original Code is
+ * Netscape Communications Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 1994-2000
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#include "common/config.h"
+
+#ifdef USE_NSS
+
+#include <nspr.h>
+#include <cert.h>
+#include <nss.h>
+#include <smime.h>
+
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+
+#include "include/buffer.h"
+
+#include "common/debug.h"
+
+#include "ceph_crypto_cms.h"
+
+#define dout_subsys ceph_subsys_crypto
+
+
+#ifndef USE_NSS
+
+int ceph_decode_cms(CephContext *cct, bufferlist& cms_bl, bufferlist& decoded_bl)
+{
+ return -ENOTSUP;
+}
+
+#else
+
+
+static int cms_verbose = 0;
+
+static SECStatus
+DigestFile(PLArenaPool *poolp, SECItem ***digests, SECItem *input,
+ SECAlgorithmID **algids)
+{
+ NSSCMSDigestContext *digcx;
+ SECStatus rv;
+
+ digcx = NSS_CMSDigestContext_StartMultiple(algids);
+ if (digcx == NULL)
+ return SECFailure;
+
+ NSS_CMSDigestContext_Update(digcx, input->data, input->len);
+
+ rv = NSS_CMSDigestContext_FinishMultiple(digcx, poolp, digests);
+ return rv;
+}
+
+
+struct optionsStr {
+ SECCertUsage certUsage;
+ CERTCertDBHandle *certHandle;
+};
+
+struct decodeOptionsStr {
+ struct optionsStr *options;
+ SECItem content;
+ int headerLevel;
+ PRBool suppressContent;
+ NSSCMSGetDecryptKeyCallback dkcb;
+ PK11SymKey *bulkkey;
+ PRBool keepCerts;
+};
+
+static NSSCMSMessage *
+decode(CephContext *cct, SECItem *input, const struct decodeOptionsStr *decodeOptions, bufferlist& out)
+{
+ NSSCMSDecoderContext *dcx;
+ SECStatus rv;
+ NSSCMSMessage *cmsg;
+ int nlevels, i;
+ SECItem sitem;
+ bufferptr bp;
+ SECItem *item;
+
+ memset(&sitem, 0, sizeof(sitem));
+
+ PORT_SetError(0);
+ dcx = NSS_CMSDecoder_Start(NULL,
+ NULL, NULL, /* content callback */
+ NULL, NULL, /* password callback */
+ decodeOptions->dkcb, /* decrypt key callback */
+ decodeOptions->bulkkey);
+ if (dcx == NULL) {
+ ldout(cct, 0) << "ERROR: failed to set up message decoder" << dendl;
+ return NULL;
+ }
+ rv = NSS_CMSDecoder_Update(dcx, (char *)input->data, input->len);
+ if (rv != SECSuccess) {
+ ldout(cct, 0) << "ERROR: failed to decode message" << dendl;
+ NSS_CMSDecoder_Cancel(dcx);
+ return NULL;
+ }
+ cmsg = NSS_CMSDecoder_Finish(dcx);
+ if (cmsg == NULL) {
+ ldout(cct, 0) << "ERROR: failed to decode message" << dendl;
+ return NULL;
+ }
+
+ if (decodeOptions->headerLevel >= 0) {
+ ldout(cct, 20) << "SMIME: " << dendl;
+ }
+
+ nlevels = NSS_CMSMessage_ContentLevelCount(cmsg);
+ for (i = 0; i < nlevels; i++) {
+ NSSCMSContentInfo *cinfo;
+ SECOidTag typetag;
+
+ cinfo = NSS_CMSMessage_ContentLevel(cmsg, i);
+ typetag = NSS_CMSContentInfo_GetContentTypeTag(cinfo);
+
+ ldout(cct, 20) << "level=" << decodeOptions->headerLevel << "." << nlevels - i << dendl;
+
+ switch (typetag) {
+ case SEC_OID_PKCS7_SIGNED_DATA:
+ {
+ NSSCMSSignedData *sigd = NULL;
+ SECItem **digests;
+ int nsigners;
+ int j;
+
+ if (decodeOptions->headerLevel >= 0)
+ ldout(cct, 20) << "type=signedData; " << dendl;
+ sigd = (NSSCMSSignedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ if (sigd == NULL) {
+ ldout(cct, 0) << "ERROR: signedData component missing" << dendl;
+ goto loser;
+ }
+
+ /* if we have a content file, but no digests for this signedData */
+ if (decodeOptions->content.data != NULL &&
+ !NSS_CMSSignedData_HasDigests(sigd)) {
+ PLArenaPool *poolp;
+ SECAlgorithmID **digestalgs;
+
+ /* detached content: grab content file */
+ sitem = decodeOptions->content;
+
+ if ((poolp = PORT_NewArena(1024)) == NULL) {
+ ldout(cct, 0) << "ERROR: Out of memory" << dendl;
+ goto loser;
+ }
+ digestalgs = NSS_CMSSignedData_GetDigestAlgs(sigd);
+ if (DigestFile (poolp, &digests, &sitem, digestalgs)
+ != SECSuccess) {
+ ldout(cct, 0) << "ERROR: problem computing message digest" << dendl;
+ PORT_FreeArena(poolp, PR_FALSE);
+ goto loser;
+ }
+ if (NSS_CMSSignedData_SetDigests(sigd, digestalgs, digests)
+ != SECSuccess) {
+ ldout(cct, 0) << "ERROR: problem setting message digests" << dendl;
+ PORT_FreeArena(poolp, PR_FALSE);
+ goto loser;
+ }
+ PORT_FreeArena(poolp, PR_FALSE);
+ }
+
+ /* import the certificates */
+ if (NSS_CMSSignedData_ImportCerts(sigd,
+ decodeOptions->options->certHandle,
+ decodeOptions->options->certUsage,
+ decodeOptions->keepCerts)
+ != SECSuccess) {
+ ldout(cct, 0) << "ERROR: cert import failed" << dendl;
+ goto loser;
+ }
+
+ /* find out about signers */
+ nsigners = NSS_CMSSignedData_SignerInfoCount(sigd);
+ if (decodeOptions->headerLevel >= 0)
+ ldout(cct, 20) << "nsigners=" << nsigners << dendl;
+ if (nsigners == 0) {
+ /* Might be a cert transport message
+ ** or might be an invalid message, such as a QA test message
+ ** or a message from an attacker.
+ */
+ SECStatus rv;
+ rv = NSS_CMSSignedData_VerifyCertsOnly(sigd,
+ decodeOptions->options->certHandle,
+ decodeOptions->options->certUsage);
+ if (rv != SECSuccess) {
+ ldout(cct, 0) << "ERROR: Verify certs-only failed!" << dendl;
+ goto loser;
+ }
+ return cmsg;
+ }
+
+ /* still no digests? */
+ if (!NSS_CMSSignedData_HasDigests(sigd)) {
+ ldout(cct, 0) << "ERROR: no message digests" << dendl;
+ goto loser;
+ }
+
+ for (j = 0; j < nsigners; j++) {
+ const char * svs;
+ NSSCMSSignerInfo *si;
+ NSSCMSVerificationStatus vs;
+ SECStatus bad;
+
+ si = NSS_CMSSignedData_GetSignerInfo(sigd, j);
+ if (decodeOptions->headerLevel >= 0) {
+ char *signercn;
+ static char empty[] = { "" };
+
+ signercn = NSS_CMSSignerInfo_GetSignerCommonName(si);
+ if (signercn == NULL)
+ signercn = empty;
+ ldout(cct, 20) << "\t\tsigner" << j << ".id=" << signercn << dendl;
+ if (signercn != empty)
+ PORT_Free(signercn);
+ }
+ bad = NSS_CMSSignedData_VerifySignerInfo(sigd, j,
+ decodeOptions->options->certHandle,
+ decodeOptions->options->certUsage);
+ vs = NSS_CMSSignerInfo_GetVerificationStatus(si);
+ svs = NSS_CMSUtil_VerificationStatusToString(vs);
+ if (decodeOptions->headerLevel >= 0) {
+ ldout(cct, 20) << "signer" << j << "status=" << svs << dendl;
+ /* goto loser ? */
+ } else if (bad) {
+ ldout(cct, 0) << "ERROR: signer " << j << " status = " << svs << dendl;
+ goto loser;
+ }
+ }
+ }
+ break;
+ case SEC_OID_PKCS7_ENVELOPED_DATA:
+ {
+ NSSCMSEnvelopedData *envd;
+ if (decodeOptions->headerLevel >= 0)
+ ldout(cct, 20) << "type=envelopedData; " << dendl;
+ envd = (NSSCMSEnvelopedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ if (envd == NULL) {
+ ldout(cct, 0) << "ERROR: envelopedData component missing" << dendl;
+ goto loser;
+ }
+ }
+ break;
+ case SEC_OID_PKCS7_ENCRYPTED_DATA:
+ {
+ NSSCMSEncryptedData *encd;
+ if (decodeOptions->headerLevel >= 0)
+ ldout(cct, 20) << "type=encryptedData; " << dendl;
+ encd = (NSSCMSEncryptedData *)NSS_CMSContentInfo_GetContent(cinfo);
+ if (encd == NULL) {
+ ldout(cct, 0) << "ERROR: encryptedData component missing" << dendl;
+ goto loser;
+ }
+ }
+ break;
+ case SEC_OID_PKCS7_DATA:
+ if (decodeOptions->headerLevel >= 0)
+ ldout(cct, 20) << "type=data; " << dendl;
+ break;
+ default:
+ break;
+ }
+ }
+
+ item = (sitem.data ? &sitem : NSS_CMSMessage_GetContent(cmsg));
+ out.append((char *)item->data, item->len);
+ return cmsg;
+
+loser:
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+ return NULL;
+}
+
+int ceph_decode_cms(CephContext *cct, bufferlist& cms_bl, bufferlist& decoded_bl)
+{
+ NSSCMSMessage *cmsg = NULL;
+ struct decodeOptionsStr decodeOptions = { 0 };
+ struct optionsStr options;
+ SECItem input;
+
+ memset(&options, 0, sizeof(options));
+ memset(&input, 0, sizeof(input));
+
+ input.data = (unsigned char *)cms_bl.c_str();
+ input.len = cms_bl.length();
+
+ decodeOptions.content.data = NULL;
+ decodeOptions.content.len = 0;
+ decodeOptions.suppressContent = PR_FALSE;
+ decodeOptions.headerLevel = -1;
+ decodeOptions.keepCerts = PR_FALSE;
+ options.certUsage = certUsageEmailSigner;
+
+ options.certHandle = CERT_GetDefaultCertDB();
+ if (!options.certHandle) {
+ ldout(cct, 0) << "ERROR: No default cert DB" << dendl;
+ return -EIO;
+ }
+ if (cms_verbose) {
+ fprintf(stderr, "Got default certdb\n");
+ }
+
+ decodeOptions.options = &options;
+
+ int ret = 0;
+
+ cmsg = decode(cct, &input, &decodeOptions, decoded_bl);
+ if (!cmsg) {
+ ldout(cct, 0) << "ERROR: problem decoding" << dendl;
+ ret = -EINVAL;
+ }
+
+ if (cmsg)
+ NSS_CMSMessage_Destroy(cmsg);
+
+ SECITEM_FreeItem(&decodeOptions.content, PR_FALSE);
+
+ return ret;
+}
+
+#endif
diff --git a/src/common/ceph_crypto_cms.h b/src/common/ceph_crypto_cms.h
new file mode 100644
index 00000000000..5b0a7f5950f
--- /dev/null
+++ b/src/common/ceph_crypto_cms.h
@@ -0,0 +1,10 @@
+#ifndef CEPH_CRYPTO_CMS_H
+#define CEPH_CRYPTO_CMS_H
+
+#include "include/buffer.h"
+
+class CephContext;
+
+int ceph_decode_cms(CephContext *cct, bufferlist& cms_bl, bufferlist& decoded_bl);
+
+#endif
diff --git a/src/common/common_init.cc b/src/common/common_init.cc
index 76b50e714a5..3f7d501eb26 100644
--- a/src/common/common_init.cc
+++ b/src/common/common_init.cc
@@ -109,7 +109,7 @@ void complain_about_parse_errors(CephContext *cct,
* same application. */
void common_init_finish(CephContext *cct)
{
- ceph::crypto::init();
+ ceph::crypto::init(cct);
cct->start_service_thread();
if (cct->_conf->lockdep) {
diff --git a/src/common/config_opts.h b/src/common/config_opts.h
index d2068fd11ce..5db0889dd20 100644
--- a/src/common/config_opts.h
+++ b/src/common/config_opts.h
@@ -77,6 +77,7 @@ SUBSYS(monc, 0, 5)
SUBSYS(paxos, 0, 5)
SUBSYS(tp, 0, 5)
SUBSYS(auth, 1, 5)
+SUBSYS(crypto, 1, 5)
SUBSYS(finisher, 1, 5)
SUBSYS(heartbeatmap, 1, 5)
SUBSYS(perfcounter, 1, 5)
@@ -417,15 +418,25 @@ OPTION(rbd_cache_size, OPT_LONGLONG, 32<<20) // cache size in bytes
OPTION(rbd_cache_max_dirty, OPT_LONGLONG, 24<<20) // dirty limit in bytes - set to 0 for write-through caching
OPTION(rbd_cache_target_dirty, OPT_LONGLONG, 16<<20) // target dirty limit in bytes
OPTION(rbd_cache_max_dirty_age, OPT_FLOAT, 1.0) // seconds in cache before writeback starts
+
+OPTION(nss_db_path, OPT_STR, "") // path to nss db
+
OPTION(rgw_data, OPT_STR, "/var/lib/ceph/radosgw/$cluster-$id")
OPTION(rgw_enable_apis, OPT_STR, "s3, swift, swift_auth, admin")
OPTION(rgw_cache_enabled, OPT_BOOL, true) // rgw cache enabled
OPTION(rgw_cache_lru_size, OPT_INT, 10000) // num of entries in rgw cache
OPTION(rgw_socket_path, OPT_STR, "") // path to unix domain socket, if not specified, rgw will not run as external fcgi
OPTION(rgw_dns_name, OPT_STR, "")
-OPTION(rgw_swift_url, OPT_STR, "") //
-OPTION(rgw_swift_url_prefix, OPT_STR, "swift") //
+OPTION(rgw_swift_url, OPT_STR, "") // the swift url, being published by the internal swift auth
+OPTION(rgw_swift_url_prefix, OPT_STR, "swift") // entry point for which a url is considered a swift url
+OPTION(rgw_swift_auth_url, OPT_STR, "") // default URL to go and verify tokens for v1 auth (if not using internal swift auth)
OPTION(rgw_swift_auth_entry, OPT_STR, "auth") // entry point for which a url is considered a swift auth url
+OPTION(rgw_swift_use_keystone, OPT_BOOL, false) // should swift use keystone?
+OPTION(rgw_keystone_url, OPT_STR, "") // url for keystone server
+OPTION(rgw_keystone_admin_token, OPT_STR, "") // keystone admin token (shared secret)
+OPTION(rgw_keystone_accepted_roles, OPT_STR, "Member, admin") // roles required to serve requests
+OPTION(rgw_keystone_token_cache_size, OPT_INT, 10000) // max number of entries in keystone token cache
+OPTION(rgw_keystone_revocation_interval, OPT_INT, 15 * 60) // seconds between tokens revocation check
OPTION(rgw_admin_entry, OPT_STR, "admin") // entry point for which a url is considered an admin request
OPTION(rgw_enforce_swift_acls, OPT_BOOL, true)
OPTION(rgw_print_continue, OPT_BOOL, true) // enable if 100-Continue works
diff --git a/src/json_spirit/json_spirit_reader_template.h b/src/json_spirit/json_spirit_reader_template.h
index b814e916255..f87b59331b7 100644
--- a/src/json_spirit/json_spirit_reader_template.h
+++ b/src/json_spirit/json_spirit_reader_template.h
@@ -13,7 +13,7 @@
#include "json_spirit_value.h"
#include "json_spirit_error_position.h"
-//#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread
+#define BOOST_SPIRIT_THREADSAFE // uncomment for multithreaded use, requires linking to boost.thread
#include <boost/bind.hpp>
#include <boost/function.hpp>
diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc
index c030e3466cd..62fda4b4d3c 100644
--- a/src/rgw/rgw_common.cc
+++ b/src/rgw/rgw_common.cc
@@ -39,6 +39,9 @@ int rgw_perf_start(CephContext *cct)
plb.add_u64_counter(l_rgw_cache_hit, "cache_hit");
plb.add_u64_counter(l_rgw_cache_miss, "cache_miss");
+ plb.add_u64_counter(l_rgw_keystone_token_cache_hit, "keystone_token_cache_hit");
+ plb.add_u64_counter(l_rgw_keystone_token_cache_miss, "keystone_token_cache_miss");
+
perfcounter = plb.create_perf_counters();
cct->get_perfcounters_collection()->add(perfcounter);
return 0;
@@ -88,7 +91,7 @@ is_err() const
req_state::req_state(CephContext *_cct, struct RGWEnv *e) : cct(_cct), cio(NULL), op(OP_UNKNOWN),
os_auth_token(NULL),
- os_user(NULL), os_groups(NULL), env(e)
+ env(e)
{
enable_ops_log = env->conf->enable_ops_log;
enable_usage_log = env->conf->enable_usage_log;
@@ -109,8 +112,6 @@ req_state::req_state(CephContext *_cct, struct RGWEnv *e) : cct(_cct), cio(NULL)
prot_flags = 0;
os_auth_token = NULL;
- os_user = NULL;
- os_groups = NULL;
time = ceph_clock_now(cct);
perm_mask = 0;
content_length = 0;
@@ -130,8 +131,6 @@ req_state::~req_state() {
delete formatter;
delete bucket_acl;
delete object_acl;
- free(os_user);
- free(os_groups);
free((void *)object);
free((void *)bucket_name);
}
diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h
index 0ba9e95c80b..c55907a38d3 100644
--- a/src/rgw/rgw_common.h
+++ b/src/rgw/rgw_common.h
@@ -138,6 +138,9 @@ enum {
l_rgw_cache_hit,
l_rgw_cache_miss,
+ l_rgw_keystone_token_cache_hit,
+ l_rgw_keystone_token_cache_miss,
+
l_rgw_last,
};
@@ -611,8 +614,8 @@ struct req_state {
int prot_flags;
const char *os_auth_token;
- char *os_user;
- char *os_groups;
+ string swift_user;
+ string swift_groups;
utime_t time;
diff --git a/src/rgw/rgw_http_client.cc b/src/rgw/rgw_http_client.cc
new file mode 100644
index 00000000000..4c7b99c17c3
--- /dev/null
+++ b/src/rgw/rgw_http_client.cc
@@ -0,0 +1,76 @@
+#include <curl/curl.h>
+#include <curl/easy.h>
+
+#include "rgw_common.h"
+#include "rgw_http_client.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+static size_t read_http_header(void *ptr, size_t size, size_t nmemb, void *_info)
+{
+ RGWHTTPClient *client = (RGWHTTPClient *)_info;
+ size_t len = size * nmemb;
+ int ret = client->read_header(ptr, size * nmemb);
+ if (ret < 0) {
+ dout(0) << "WARNING: client->read_header() returned ret=" << ret << dendl;
+ }
+
+ return len;
+}
+
+static size_t read_http_data(void *ptr, size_t size, size_t nmemb, void *_info)
+{
+ RGWHTTPClient *client = (RGWHTTPClient *)_info;
+ size_t len = size * nmemb;
+ int ret = client->read_data(ptr, size * nmemb);
+ if (ret < 0) {
+ dout(0) << "WARNING: client->read_data() returned ret=" << ret << dendl;
+ }
+
+ return len;
+}
+
+int RGWHTTPClient::process(const string& url)
+{
+ int ret = 0;
+ CURL *curl_handle;
+
+ char error_buf[CURL_ERROR_SIZE];
+
+ curl_handle = curl_easy_init();
+
+ dout(20) << "sending request to " << url << dendl;
+
+ curl_slist *h = NULL;
+
+ list<pair<string, string> >::iterator iter;
+ for (iter = headers.begin(); iter != headers.end(); ++iter) {
+ pair<string, string>& p = *iter;
+ string val = p.first;
+ val.append(": ");
+ val.append(p.second);
+ h = curl_slist_append(h, val.c_str());
+ }
+
+ curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
+ curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
+ curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, read_http_header);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, (void *)this);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, read_http_data);
+ curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, (void *)this);
+ curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, (void *)error_buf);
+ if (h) {
+ curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, (void *)h);
+ }
+ CURLcode status = curl_easy_perform(curl_handle);
+ if (status) {
+ dout(0) << "curl_easy_performed returned error: " << error_buf << dendl;
+ ret = -EINVAL;
+ }
+ curl_easy_cleanup(curl_handle);
+ curl_slist_free_all(h);
+
+ return ret;
+}
+
+
diff --git a/src/rgw/rgw_http_client.h b/src/rgw/rgw_http_client.h
new file mode 100644
index 00000000000..944ea89e3f3
--- /dev/null
+++ b/src/rgw/rgw_http_client.h
@@ -0,0 +1,23 @@
+#ifndef CEPH_RGW_HTTP_CLIENT_H
+#define CEPH_RGW_HTTP_CLIENT_H
+
+#include "rgw_common.h"
+
+class RGWHTTPClient
+{
+ list<pair<string, string> > headers;
+public:
+ virtual ~RGWHTTPClient() {}
+ RGWHTTPClient() {}
+
+ void append_header(const string& name, const string& val) {
+ headers.push_back(pair<string, string>(name, val));
+ }
+
+ virtual int read_header(void *ptr, size_t len) { return 0; }
+ virtual int read_data(void *ptr, size_t len) { return 0; }
+
+ int process(const string& url);
+};
+
+#endif
diff --git a/src/rgw/rgw_json.cc b/src/rgw/rgw_json.cc
index 1a6fc32dea9..0f91c2279e1 100644
--- a/src/rgw/rgw_json.cc
+++ b/src/rgw/rgw_json.cc
@@ -2,6 +2,7 @@
#include <include/types.h>
#include "rgw_json.h"
+#include "rgw_common.h"
// for testing DELETE ME
#include <fstream>
@@ -9,6 +10,8 @@
using namespace std;
using namespace json_spirit;
+#define dout_subsys ceph_subsys_rgw
+
JSONObjIter::JSONObjIter()
{
}
@@ -52,7 +55,7 @@ JSONObj::~JSONObj()
void JSONObj::add_child(string el, JSONObj *obj)
{
- cout << "add_child: " << name << " <- " << el << endl;
+ cout << "add_child: " << name << " <- " << el << std::endl;
children.insert(pair<string, JSONObj *>(el, obj));
}
@@ -65,14 +68,16 @@ bool JSONObj::get_attr(string name, string& attr)
return true;
}
-JSONObjIter JSONObj::find(string name)
+JSONObjIter JSONObj::find(const string& name)
{
JSONObjIter iter;
map<string, JSONObj *>::iterator first;
map<string, JSONObj *>::iterator last;
first = children.find(name);
- last = children.upper_bound(name);
- iter.set(first, last);
+ if (first != children.end()) {
+ last = children.upper_bound(name);
+ iter.set(first, last);
+ }
return iter;
}
@@ -80,14 +85,10 @@ JSONObjIter JSONObj::find_first()
{
JSONObjIter iter;
iter.set(children.begin(), children.end());
- cout << "count=" << children.size() << endl;
- for (map<string, JSONObj *>:: iterator i = children.begin(); i != children.end(); ++i) {
- cout << "child: " << i->first << endl;
- }
return iter;
}
-JSONObjIter JSONObj::find_first(string name)
+JSONObjIter JSONObj::find_first(const string& name)
{
JSONObjIter iter;
map<string, JSONObj *>::iterator first;
@@ -96,6 +97,26 @@ JSONObjIter JSONObj::find_first(string name)
return iter;
}
+JSONObj *JSONObj::find_obj(const string& name)
+{
+ JSONObjIter iter = find(name);
+ if (iter.end())
+ return NULL;
+
+ return *iter;
+}
+
+bool JSONObj::get_data(const string& key, string *dest)
+{
+ JSONObj *obj = find_obj(key);
+ if (!obj)
+ return false;
+
+ *dest = obj->get_data();
+
+ return true;
+}
+
/* accepts a JSON Array or JSON Object contained in
* a JSON Spirit Value, v, and creates a JSONObj for each
* child contained in v
@@ -118,16 +139,11 @@ void JSONObj::handle_value(Value v)
for (unsigned j = 0; j < temp_array.size(); j++) {
Value cur = temp_array[j];
-
- if (cur.type() == obj_type) {
- handle_value(cur);
- } else {
- string temp_name;
-
- JSONObj *child = new JSONObj;
- child->init(this, cur, temp_name);
- add_child(child->get_name(), child);
- }
+ string temp_name;
+
+ JSONObj *child = new JSONObj;
+ child->init(this, cur, temp_name);
+ add_child(child->get_name(), child);
}
}
}
diff --git a/src/rgw/rgw_json.h b/src/rgw/rgw_json.h
index e11ab25b405..d0dec397c28 100644
--- a/src/rgw/rgw_json.h
+++ b/src/rgw/rgw_json.h
@@ -55,12 +55,14 @@ public:
string& get_name() { return name; }
string& get_data() { return data_string; }
+ bool get_data(const string& key, string *dest);
JSONObj *get_parent();
void add_child(string el, JSONObj *child);
bool get_attr(string name, string& attr);
- JSONObjIter find(string name);
+ JSONObjIter find(const string& name);
JSONObjIter find_first();
- JSONObjIter find_first(string name);
+ JSONObjIter find_first(const string& name);
+ JSONObj *find_obj(const string& name);
friend ostream& operator<<(ostream& out, JSONObj& obj); // does not work, FIXME
diff --git a/src/rgw/rgw_main.cc b/src/rgw/rgw_main.cc
index 5d9efee3cd4..944b59a5c8d 100644
--- a/src/rgw/rgw_main.cc
+++ b/src/rgw/rgw_main.cc
@@ -458,6 +458,7 @@ int main(int argc, const char **argv)
RGWREST rest;
list<string> apis;
+ bool do_swift = false;
get_str_list(g_conf->rgw_enable_apis, apis);
@@ -469,8 +470,11 @@ int main(int argc, const char **argv)
if (apis_map.count("s3") > 0)
rest.register_default_mgr(new RGWRESTMgr_S3);
- if (apis_map.count("swift") > 0)
+ if (apis_map.count("swift") > 0) {
+ do_swift = true;
+ swift_init(g_ceph_context);
rest.register_resource(g_conf->rgw_swift_url_prefix, new RGWRESTMgr_SWIFT);
+ }
if (apis_map.count("swift_auth") > 0)
rest.register_resource(g_conf->rgw_swift_auth_entry, new RGWRESTMgr_SWIFT_Auth);
@@ -484,6 +488,10 @@ int main(int argc, const char **argv)
RGWProcess process(g_ceph_context, store, g_conf->rgw_thread_pool_size, &rest);
process.run();
+ if (do_swift) {
+ swift_finalize();
+ }
+
rgw_log_usage_finalize();
rgw_perf_stop(g_ceph_context);
diff --git a/src/rgw/rgw_rest_swift.cc b/src/rgw/rgw_rest_swift.cc
index 6fefb548e75..e64028c3caf 100644
--- a/src/rgw/rgw_rest_swift.cc
+++ b/src/rgw/rgw_rest_swift.cc
@@ -612,7 +612,7 @@ RGWOp *RGWHandler_ObjStore_Obj_SWIFT::op_copy()
int RGWHandler_ObjStore_SWIFT::authorize()
{
- bool authorized = rgw_verify_swift_token(store, s);
+ bool authorized = rgw_swift->verify_swift_token(store, s);
if (!authorized)
return -EPERM;
diff --git a/src/rgw/rgw_swift.cc b/src/rgw/rgw_swift.cc
index e7f4035d56c..2ce04074a03 100644
--- a/src/rgw/rgw_swift.cc
+++ b/src/rgw/rgw_swift.cc
@@ -2,25 +2,45 @@
#include <stdlib.h>
#include <unistd.h>
-#include <curl/curl.h>
-#include <curl/easy.h>
-
+#include "rgw_json.h"
#include "rgw_common.h"
#include "rgw_swift.h"
#include "rgw_swift_auth.h"
#include "rgw_user.h"
+#include "rgw_http_client.h"
+
+#include "include/str_list.h"
+
+#include "common/ceph_crypto_cms.h"
+#include "common/armor.h"
#define dout_subsys ceph_subsys_rgw
-static size_t read_http_header(void *ptr, size_t size, size_t nmemb, void *_info)
+static list<string> roles_list;
+
+class RGWKeystoneTokenCache;
+
+class RGWValidateSwiftToken : public RGWHTTPClient {
+ CephContext *cct;
+ struct rgw_swift_auth_info *info;
+
+protected:
+ RGWValidateSwiftToken() : cct(NULL), info(NULL) {}
+public:
+ RGWValidateSwiftToken(CephContext *_cct, struct rgw_swift_auth_info *_info) : cct(_cct), info(_info) {}
+
+ int read_header(void *ptr, size_t len);
+
+ friend class RGWKeystoneTokenCache;
+};
+
+int RGWValidateSwiftToken::read_header(void *ptr, size_t len)
{
- size_t len = size * nmemb;
char line[len + 1];
- struct rgw_swift_auth_info *info = (struct rgw_swift_auth_info *)_info;
char *s = (char *)ptr, *end = (char *)ptr + len;
char *p = line;
- dout(10) << "read_http_header" << dendl;
+ ldout(cct, 10) << "read_http_header" << dendl;
while (s != end) {
if (*s == '\r') {
@@ -29,7 +49,7 @@ static size_t read_http_header(void *ptr, size_t size, size_t nmemb, void *_info
}
if (*s == '\n') {
*p = '\0';
- dout(10) << "os_auth:" << line << dendl;
+ ldout(cct, 10) << "os_auth:" << line << dendl;
// TODO: fill whatever data required here
char *l = line;
char *tok = strsep(&l, " \t:");
@@ -40,11 +60,11 @@ static size_t read_http_header(void *ptr, size_t size, size_t nmemb, void *_info
if (strcmp(tok, "HTTP") == 0) {
info->status = atoi(l);
} else if (strcasecmp(tok, "X-Auth-Groups") == 0) {
- info->auth_groups = strdup(l);
+ info->auth_groups = l;
char *s = strchr(l, ',');
if (s) {
*s = '\0';
- info->user = strdup(l);
+ info->user = l;
}
} else if (strcasecmp(tok, "X-Auth-Ttl") == 0) {
info->ttl = atoll(l);
@@ -54,34 +74,558 @@ static size_t read_http_header(void *ptr, size_t size, size_t nmemb, void *_info
if (s != end)
*p++ = *s++;
}
- return len;
+ return 0;
}
-static int rgw_swift_validate_token(const char *token, struct rgw_swift_auth_info *info)
+int RGWSwift::validate_token(const char *token, struct rgw_swift_auth_info *info)
{
- CURL *curl_handle;
- string auth_url = "http://127.0.0.1:11000/token";
+ if (g_conf->rgw_swift_auth_url.empty())
+ return -EINVAL;
+
+ string auth_url = g_conf->rgw_swift_auth_url;
+ if (auth_url[auth_url.size() - 1] != '/')
+ auth_url.append("/");
+ auth_url.append("token");
char url_buf[auth_url.size() + 1 + strlen(token) + 1];
sprintf(url_buf, "%s/%s", auth_url.c_str(), token);
- dout(10) << "rgw_swift_validate_token url=" << url_buf << dendl;
+ RGWValidateSwiftToken validate(cct, info);
+
+ ldout(cct, 10) << "rgw_swift_validate_token url=" << url_buf << dendl;
+
+ int ret = validate.process(url_buf);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int KeystoneToken::parse(CephContext *cct, bufferlist& bl)
+{
+ RGWJSONParser parser;
+
+ if (!parser.parse(bl.c_str(), bl.length())) {
+ ldout(cct, 0) << "malformed json" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObjIter iter = parser.find_first("access");
+ if (iter.end()) {
+ ldout(cct, 0) << "token response is missing access section" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObj *access_obj = *iter;
+ JSONObj *user = access_obj->find_obj("user");
+ if (!user) {
+ ldout(cct, 0) << "token response is missing user section" << dendl;
+ return -EINVAL;
+ }
+
+ if (!user->get_data("username", &user_name)) {
+ ldout(cct, 0) << "token response is missing user username field" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObj *roles_obj = user->find_obj("roles");
+ if (!roles_obj) {
+ ldout(cct, 0) << "token response is missing roles section, or section empty" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObjIter riter = roles_obj->find_first();
+ if (riter.end()) {
+ ldout(cct, 0) << "token response has an empty roles list" << dendl;
+ return -EINVAL;
+ }
+
+ for (; !riter.end(); ++riter) {
+ JSONObj *role_obj = *riter;
+ if (!role_obj) {
+ ldout(cct, 0) << "ERROR: role object is NULL" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObj *role_name = role_obj->find_obj("name");
+ if (!role_name) {
+ ldout(cct, 0) << "token response is missing role name section" << dendl;
+ return -EINVAL;
+ }
+ string role = role_name->get_data();
+ roles[role] = true;
+ }
+
+ JSONObj *token = access_obj->find_obj("token");
+ if (!token) {
+ ldout(cct, 0) << "missing token section in response" << dendl;
+ return -EINVAL;
+ }
+
+ string expires;
+
+ if (!token->get_data("expires", &expires)) {
+ ldout(cct, 0) << "token response is missing expiration field" << dendl;
+ return -EINVAL;
+ }
+
+ struct tm t;
+ if (!parse_iso8601(expires.c_str(), &t)) {
+ ldout(cct, 0) << "failed to parse token expiration (" << expires << ")" << dendl;
+ return -EINVAL;
+ }
+
+ expiration = timegm(&t);
+
+ JSONObj *tenant = token->find_obj("tenant");
+ if (!tenant) {
+ ldout(cct, 0) << "token response is missing tenant section" << dendl;
+ return -EINVAL;
+ }
+
+ if (!tenant->get_data("id", &tenant_id)) {
+ ldout(cct, 0) << "tenant is missing id field" << dendl;
+ return -EINVAL;
+ }
+
+
+ if (!tenant->get_data("name", &tenant_name)) {
+ ldout(cct, 0) << "tenant is missing name field" << dendl;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct token_entry {
+ KeystoneToken token;
+ list<string>::iterator lru_iter;
+};
+
+class RGWKeystoneTokenCache {
+ CephContext *cct;
+
+ map<string, token_entry> tokens;
+ list<string> tokens_lru;
+
+ Mutex lock;
+
+ size_t max;
+
+public:
+ RGWKeystoneTokenCache(CephContext *_cct, int _max) : cct(_cct), lock("RGWKeystoneTokenCache"), max(_max) {}
+
+ bool find(const string& token_id, KeystoneToken& token);
+ void add(const string& token_id, KeystoneToken& token);
+ void invalidate(const string& token_id);
+};
+
+bool RGWKeystoneTokenCache::find(const string& token_id, KeystoneToken& token)
+{
+ lock.Lock();
+ map<string, token_entry>::iterator iter = tokens.find(token_id);
+ if (iter == tokens.end()) {
+ lock.Unlock();
+ if (perfcounter) perfcounter->inc(l_rgw_keystone_token_cache_miss);
+ return false;
+ }
+
+ token_entry& entry = iter->second;
+ tokens_lru.erase(entry.lru_iter);
+
+ if (entry.token.expired()) {
+ tokens.erase(iter);
+ lock.Unlock();
+ if (perfcounter) perfcounter->inc(l_rgw_keystone_token_cache_hit);
+ return false;
+ }
+ token = entry.token;
+
+ tokens_lru.push_front(token_id);
+ entry.lru_iter = tokens_lru.begin();
+
+ lock.Unlock();
+ if (perfcounter) perfcounter->inc(l_rgw_keystone_token_cache_hit);
+
+ return true;
+}
+
+void RGWKeystoneTokenCache::add(const string& token_id, KeystoneToken& token)
+{
+ lock.Lock();
+ map<string, token_entry>::iterator iter = tokens.find(token_id);
+ if (iter != tokens.end()) {
+ token_entry& e = iter->second;
+ tokens_lru.erase(e.lru_iter);
+ }
+
+ tokens_lru.push_front(token_id);
+ token_entry& entry = tokens[token_id];
+ entry.token = token;
+ entry.lru_iter = tokens_lru.begin();
+
+ while (tokens_lru.size() > max) {
+ list<string>::reverse_iterator riter = tokens_lru.rbegin();
+ iter = tokens.find(*riter);
+ assert(iter != tokens.end());
+ tokens.erase(iter);
+ tokens_lru.pop_back();
+ }
+
+ lock.Unlock();
+}
+
+void RGWKeystoneTokenCache::invalidate(const string& token_id)
+{
+ Mutex::Locker l(lock);
+ map<string, token_entry>::iterator iter = tokens.find(token_id);
+ if (iter == tokens.end())
+ return;
+
+ ldout(cct, 20) << "invalidating revoked token id=" << token_id << dendl;
+ token_entry& e = iter->second;
+ tokens_lru.erase(e.lru_iter);
+ tokens.erase(iter);
+}
+
+class RGWValidateKeystoneToken : public RGWHTTPClient {
+ bufferlist *bl;
+public:
+ RGWValidateKeystoneToken(bufferlist *_bl) : bl(_bl) {}
+
+ int read_data(void *ptr, size_t len) {
+ bl->append((char *)ptr, len);
+ return 0;
+ }
+};
+
+static RGWKeystoneTokenCache *keystone_token_cache = NULL;
+
+class RGWGetRevokedTokens : public RGWHTTPClient {
+ bufferlist *bl;
+public:
+ RGWGetRevokedTokens(bufferlist *_bl) : bl(_bl) {}
+
+ int read_data(void *ptr, size_t len) {
+ bl->append((char *)ptr, len);
+ return 0;
+ }
+};
+
+static int open_cms_envelope(CephContext *cct, string& src, string& dst)
+{
+#define BEGIN_CMS "-----BEGIN CMS-----"
+#define END_CMS "-----END CMS-----"
+
+ int start = src.find(BEGIN_CMS);
+ if (start < 0) {
+ ldout(cct, 0) << "failed to find " << BEGIN_CMS << " in response" << dendl;
+ return -EINVAL;
+ }
+ start += sizeof(BEGIN_CMS) - 1;
+
+ int end = src.find(END_CMS);
+ if (end < 0) {
+ ldout(cct, 0) << "failed to find " << END_CMS << " in response" << dendl;
+ return -EINVAL;
+ }
+
+ string s = src.substr(start, end - start);
+
+ int pos = 0;
+
+ do {
+ int next = s.find('\n', pos);
+ if (next < 0) {
+ dst.append(s.substr(pos));
+ break;
+ } else {
+ dst.append(s.substr(pos, next - pos));
+ }
+ pos = next + 1;
+ } while (pos < (int)s.size());
+
+ return 0;
+}
+
+static int decode_b64_cms(CephContext *cct, const string& signed_b64, bufferlist& bl)
+{
+ bufferptr signed_ber(signed_b64.size() * 2);
+ char *dest = signed_ber.c_str();
+ const char *src = signed_b64.c_str();
+ size_t len = signed_b64.size();
+ char buf[len + 1];
+ buf[len] = '\0';
+ for (size_t i = 0; i < len; i++, src++) {
+ if (*src != '-')
+ buf[i] = *src;
+ else
+ buf[i] = '/';
+ }
+ int ret = ceph_unarmor(dest, dest + signed_ber.length(), buf, buf + signed_b64.size());
+ if (ret < 0) {
+ ldout(cct, 0) << "ceph_unarmor() failed, ret=" << ret << dendl;
+ return ret;
+ }
+
+ bufferlist signed_ber_bl;
+ signed_ber_bl.append(signed_ber);
+
+ ret = ceph_decode_cms(cct, signed_ber_bl, bl);
+ if (ret < 0) {
+ ldout(cct, 0) << "ceph_decode_cms returned " << ret << dendl;
+ return ret;
+ }
+
+ return 0;
+}
+
+
+int RGWSwift::check_revoked()
+{
+ bufferlist bl;
+ RGWGetRevokedTokens req(&bl);
+
+ string url = g_conf->rgw_keystone_url;
+ if (url.empty()) {
+ ldout(cct, 0) << "ERROR: keystone url is not configured" << dendl;
+ return -EINVAL;
+ }
+ if (url[url.size() - 1] != '/')
+ url.append("/");
+ url.append("v2.0/tokens/revoked");
+
+ req.append_header("X-Auth-Token", g_conf->rgw_keystone_admin_token);
+
+ int ret = req.process(url);
+ if (ret < 0)
+ return ret;
+
+ bl.append((char)0); // NULL terminate for debug output
+
+ ldout(cct, 10) << "request returned " << bl.c_str() << dendl;
+
+ RGWJSONParser parser;
+
+ if (!parser.parse(bl.c_str(), bl.length())) {
+ ldout(cct, 0) << "malformed json" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObjIter iter = parser.find_first("signed");
+ if (iter.end()) {
+ ldout(cct, 0) << "revoked tokens response is missing signed section" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObj *signed_obj = *iter;
+
+ string signed_str = signed_obj->get_data();
+
+ ldout(cct, 10) << "signed=" << signed_str << dendl;
+
+ string signed_b64;
+ ret = open_cms_envelope(cct, signed_str, signed_b64);
+ if (ret < 0)
+ return ret;
+
+ ldout(cct, 10) << "content=" << signed_b64 << dendl;
+
+ bufferlist json;
+ ret = decode_b64_cms(cct, signed_b64, json);
+ if (ret < 0) {
+ return ret;
+ }
+
+ ldout(cct, 10) << "ceph_decode_cms: decoded: " << json.c_str() << dendl;
+
+ RGWJSONParser list_parser;
+ if (!list_parser.parse(json.c_str(), json.length())) {
+ ldout(cct, 0) << "malformed json" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObjIter revoked_iter = list_parser.find_first("revoked");
+ if (revoked_iter.end()) {
+ ldout(cct, 0) << "no revoked section in json" << dendl;
+ return -EINVAL;
+ }
+
+ JSONObj *revoked_obj = *revoked_iter;
+
+ JSONObjIter tokens_iter = revoked_obj->find_first();
+ for (; !tokens_iter.end(); ++tokens_iter) {
+ JSONObj *o = *tokens_iter;
+
+ JSONObj *token = o->find_obj("id");
+ if (!token) {
+ ldout(cct, 0) << "bad token in array, missing id" << dendl;
+ continue;
+ }
+
+ string token_id = token->get_data();
+ keystone_token_cache->invalidate(token_id);
+ }
+
+ return 0;
+}
+
+static void rgw_set_keystone_token_auth_info(KeystoneToken& token, struct rgw_swift_auth_info *info)
+{
+ info->user = token.tenant_id;
+ info->display_name = token.tenant_name;
+ info->status = 200;
+}
+
+int RGWSwift::parse_keystone_token_response(const string& token, bufferlist& bl, struct rgw_swift_auth_info *info, KeystoneToken& t)
+{
+ int ret = t.parse(cct, bl);
+ if (ret < 0)
+ return ret;
+
+ bool found = false;
+ list<string>::iterator iter;
+ for (iter = roles_list.begin(); iter != roles_list.end(); ++iter) {
+ const string& role = *iter;
+ if (t.roles.find(role) != t.roles.end()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ ldout(cct, 0) << "user does not hold a matching role; required roles: " << g_conf->rgw_keystone_accepted_roles << dendl;
+ return -EPERM;
+ }
+
+ ldout(cct, 0) << "validated token: " << t.tenant_name << ":" << t.user_name << " expires: " << t.expiration << dendl;
+
+ rgw_set_keystone_token_auth_info(t, info);
+
+ return 0;
+}
+
+int RGWSwift::update_user_info(RGWRados *store, struct rgw_swift_auth_info *info, RGWUserInfo& user_info)
+{
+ if (rgw_get_user_info_by_uid(store, info->user, user_info) < 0) {
+ ldout(cct, 0) << "NOTICE: couldn't map swift user" << dendl;
+ user_info.user_id = info->user;
+ user_info.display_name = info->display_name;
+
+ int ret = rgw_store_user_info(store, user_info, true);
+ if (ret < 0) {
+ ldout(cct, 0) << "ERROR: failed to store new user's info: ret=" << ret << dendl;
+ return ret;
+ }
+ }
+ return 0;
+}
+
+#define PKI_ANS1_PREFIX "MII"
+
+static bool is_pki_token(const string& token)
+{
+ return token.compare(0, sizeof(PKI_ANS1_PREFIX) - 1, PKI_ANS1_PREFIX) == 0;
+}
+
+static void get_token_id(const string& token, string& token_id)
+{
+ if (!is_pki_token(token)) {
+ token_id = token;
+ return;
+ }
+
+ unsigned char m[CEPH_CRYPTO_MD5_DIGESTSIZE];
+
+ MD5 hash;
+ hash.Update((const byte *)token.c_str(), token.size());
+ hash.Final(m);
+
+
+ char calc_md5[CEPH_CRYPTO_MD5_DIGESTSIZE * 2 + 1];
+ buf_to_hex(m, CEPH_CRYPTO_MD5_DIGESTSIZE, calc_md5);
+ token_id = calc_md5;
+}
+
+static bool decode_pki_token(CephContext *cct, const string& token, bufferlist& bl)
+{
+ if (!is_pki_token(token))
+ return false;
+
+ int ret = decode_b64_cms(cct, token, bl);
+ if (ret < 0)
+ return false;
- curl_handle = curl_easy_init();
+ ldout(cct, 20) << "successfully decoded pki token" << dendl;
- curl_easy_setopt(curl_handle, CURLOPT_URL, url_buf);
- curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
+ return true;
+}
- curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, read_http_header);
+int RGWSwift::validate_keystone_token(RGWRados *store, const string& token, struct rgw_swift_auth_info *info,
+ RGWUserInfo& rgw_user)
+{
+ KeystoneToken t;
- curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, info);
+ string token_id;
+ get_token_id(token, token_id);
- curl_easy_perform(curl_handle);
- curl_easy_cleanup(curl_handle);
+ ldout(cct, 20) << "token_id=" << token_id << dendl;
+
+ /* check cache first */
+ if (keystone_token_cache->find(token_id, t)) {
+ rgw_set_keystone_token_auth_info(t, info);
+
+ ldout(cct, 20) << "cached token.tenant_id=" << t.tenant_id << dendl;
+
+ int ret = update_user_info(store, info, rgw_user);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ }
+
+ bufferlist bl;
+
+ /* check if that's a self signed token that we can decode */
+ if (!decode_pki_token(cct, token, bl)) {
+
+ /* can't decode, just go to the keystone server for validation */
+
+ RGWValidateKeystoneToken validate(&bl);
+
+ string url = g_conf->rgw_keystone_url;
+ if (url.empty()) {
+ ldout(cct, 0) << "ERROR: keystone url is not configured" << dendl;
+ return -EINVAL;
+ }
+ if (url[url.size() - 1] != '/')
+ url.append("/");
+ url.append("v2.0/tokens/");
+ url.append(token);
+
+ validate.append_header("X-Auth-Token", g_conf->rgw_keystone_admin_token);
+
+ int ret = validate.process(url);
+ if (ret < 0)
+ return ret;
+ }
+
+ bl.append((char)0); // NULL terminate for debug output
+
+ ldout(cct, 20) << "received response: " << bl.c_str() << dendl;
+
+ int ret = parse_keystone_token_response(token, bl, info, t);
+ if (ret < 0)
+ return ret;
+
+ ret = update_user_info(store, info, rgw_user);
+ if (ret < 0)
+ return ret;
return 0;
}
-bool rgw_verify_swift_token(RGWRados *store, req_state *s)
+
+bool RGWSwift::verify_swift_token(RGWRados *store, req_state *s)
{
if (!s->os_auth_token)
return false;
@@ -96,32 +640,105 @@ bool rgw_verify_swift_token(RGWRados *store, req_state *s)
struct rgw_swift_auth_info info;
- memset(&info, 0, sizeof(info));
-
info.status = 401; // start with access denied, validate_token might change that
- int ret = rgw_swift_validate_token(s->os_auth_token, &info);
+ int ret;
+
+ if (g_conf->rgw_swift_use_keystone) {
+ ret = validate_keystone_token(store, s->os_auth_token, &info, s->user);
+ return (ret >= 0);
+ }
+
+ ret = validate_token(s->os_auth_token, &info);
if (ret < 0)
return ret;
- if (!info.user) {
- dout(5) << "swift auth didn't authorize a user" << dendl;
+ if (info.user.empty()) {
+ ldout(cct, 5) << "swift auth didn't authorize a user" << dendl;
return false;
}
- s->os_user = info.user;
- s->os_groups = info.auth_groups;
+ s->swift_user = info.user;
+ s->swift_groups = info.auth_groups;
- string swift_user = s->os_user;
+ string swift_user = s->swift_user;
- dout(10) << "swift user=" << s->os_user << dendl;
+ ldout(cct, 10) << "swift user=" << s->swift_user << dendl;
if (rgw_get_user_info_by_swift(store, swift_user, s->user) < 0) {
- dout(0) << "NOTICE: couldn't map swift user" << dendl;
+ ldout(cct, 0) << "NOTICE: couldn't map swift user" << dendl;
return false;
}
- dout(10) << "user_id=" << s->user.user_id << dendl;
+ ldout(cct, 10) << "user_id=" << s->user.user_id << dendl;
return true;
}
+
+void RGWSwift::init()
+{
+ get_str_list(cct->_conf->rgw_keystone_accepted_roles, roles_list);
+
+ keystone_token_cache = new RGWKeystoneTokenCache(cct, cct->_conf->rgw_keystone_token_cache_size);
+
+ keystone_revoke_thread = new KeystoneRevokeThread(cct, this);
+ keystone_revoke_thread->create();
+}
+
+
+void RGWSwift::finalize()
+{
+ delete keystone_token_cache;
+ keystone_token_cache = NULL;
+
+ down_flag.set(1);
+ if (keystone_revoke_thread) {
+ keystone_revoke_thread->stop();
+ keystone_revoke_thread->join();
+ }
+ delete keystone_revoke_thread;
+ keystone_revoke_thread = NULL;
+}
+
+RGWSwift *rgw_swift = NULL;
+
+void swift_init(CephContext *cct)
+{
+ rgw_swift = new RGWSwift(cct);
+}
+
+void swift_finalize()
+{
+ delete rgw_swift;
+}
+
+bool RGWSwift::going_down()
+{
+ return (down_flag.read() != 0);
+}
+
+void *RGWSwift::KeystoneRevokeThread::entry() {
+ do {
+ dout(2) << "keystone revoke thread: start" << dendl;
+ int r = swift->check_revoked();
+ if (r < 0) {
+ dout(0) << "ERROR: keystone revocation processing returned error r=" << r << dendl;
+ }
+
+ if (swift->going_down())
+ break;
+
+ lock.Lock();
+ cond.WaitInterval(cct, lock, utime_t(cct->_conf->rgw_keystone_revocation_interval, 0));
+ lock.Unlock();
+ } while (!swift->going_down());
+
+ return NULL;
+}
+
+void RGWSwift::KeystoneRevokeThread::stop()
+{
+ Mutex::Locker l(lock);
+ cond.Signal();
+}
+
diff --git a/src/rgw/rgw_swift.h b/src/rgw/rgw_swift.h
index a678b22065a..bdca5b46283 100644
--- a/src/rgw/rgw_swift.h
+++ b/src/rgw/rgw_swift.h
@@ -3,18 +3,85 @@
#define CEPH_RGW_SWIFT_H
#include "rgw_common.h"
+#include "common/Cond.h"
class RGWRados;
struct rgw_swift_auth_info {
int status;
- char *auth_groups;
- char *user;
+ string auth_groups;
+ string user;
+ string display_name;
long long ttl;
+
+ rgw_swift_auth_info() : status(0), ttl(0) {}
+};
+
+class KeystoneToken {
+public:
+ string tenant_name;
+ string tenant_id;
+ string user_name;
+ time_t expiration;
+
+ map<string, bool> roles;
+
+ KeystoneToken() {}
+
+ int parse(CephContext *cct, bufferlist& bl);
+
+ bool expired() {
+ uint64_t now = ceph_clock_now(NULL).sec();
+ return (now < (uint64_t)expiration);
+ }
};
-bool rgw_verify_swift_token(RGWRados *store, req_state *s);
+class RGWSwift {
+ CephContext *cct;
+ atomic_t down_flag;
+
+ int validate_token(const char *token, struct rgw_swift_auth_info *info);
+ int validate_keystone_token(RGWRados *store, const string& token, struct rgw_swift_auth_info *info,
+ RGWUserInfo& rgw_user);
+
+ int parse_keystone_token_response(const string& token, bufferlist& bl, struct rgw_swift_auth_info *info,
+ KeystoneToken& t);
+ int update_user_info(RGWRados *store, struct rgw_swift_auth_info *info, RGWUserInfo& user_info);
+
+ class KeystoneRevokeThread : public Thread {
+ CephContext *cct;
+ RGWSwift *swift;
+ Mutex lock;
+ Cond cond;
+
+ public:
+ KeystoneRevokeThread(CephContext *_cct, RGWSwift *_swift) : cct(_cct), swift(_swift), lock("KeystoneRevokeThread") {}
+ void *entry();
+ void stop();
+ };
+
+ KeystoneRevokeThread *keystone_revoke_thread;
+
+ void init();
+ void finalize();
+protected:
+ int check_revoked();
+public:
+
+ RGWSwift(CephContext *_cct) : cct(_cct) {
+ init();
+ }
+ ~RGWSwift() {
+ finalize();
+ }
+
+ bool verify_swift_token(RGWRados *store, req_state *s);
+ bool going_down();
+};
+extern RGWSwift *rgw_swift;
+void swift_init(CephContext *cct);
+void swift_finalize();
#endif
diff --git a/src/test/ceph_crypto.cc b/src/test/ceph_crypto.cc
index 403f6b5400b..2c934fa848c 100644
--- a/src/test/ceph_crypto.cc
+++ b/src/test/ceph_crypto.cc
@@ -5,7 +5,7 @@
class CryptoEnvironment: public ::testing::Environment {
public:
void SetUp() {
- ceph::crypto::init();
+ ceph::crypto::init(g_ceph_context);
}
};
@@ -117,7 +117,7 @@ class ForkDeathTest : public ::testing::Test {
virtual void TearDown() {
// undo the NSS shutdown we did in the parent process, after the
// test is done
- ceph::crypto::init();
+ ceph::crypto::init(g_ceph_context);
}
};
@@ -127,7 +127,7 @@ void do_simple_crypto() {
// fork, and if you comment out the ceph::crypto::init, or if the
// trick were to fail, you would see this ending in an assert and
// not exit status 0
- ceph::crypto::init();
+ ceph::crypto::init(g_ceph_context);
ceph::crypto::MD5 h;
h.Update((const byte*)"foo", 3);
unsigned char digest[CEPH_CRYPTO_MD5_DIGESTSIZE];
diff --git a/src/test/crypto.cc b/src/test/crypto.cc
index 85150ef80a9..80a5495001d 100644
--- a/src/test/crypto.cc
+++ b/src/test/crypto.cc
@@ -10,7 +10,7 @@
class CryptoEnvironment: public ::testing::Environment {
public:
void SetUp() {
- ceph::crypto::init();
+ ceph::crypto::init(g_ceph_context);
}
};