/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.crypto.key.kms.server; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.io.IOException; import java.net.URI; import java.security.PrivilegedExceptionAction; import java.security.SecureRandom; import java.util.HashMap; import java.util.Map; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.crypto.key.KeyProvider; import org.apache.hadoop.crypto.key.KeyProvider.KeyVersion; import org.apache.hadoop.crypto.key.KeyProvider.Options; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension; import org.apache.hadoop.crypto.key.KeyProviderCryptoExtension.EncryptedKeyVersion; import org.apache.hadoop.crypto.key.UserProvider; import org.apache.hadoop.crypto.key.kms.server.KeyAuthorizationKeyProvider.KeyACLs; import org.apache.hadoop.crypto.key.kms.server.KeyAuthorizationKeyProvider.KeyOpType; import org.apache.hadoop.security.UserGroupInformation; import org.junit.Assert; import org.junit.Test; public class TestKeyAuthorizationKeyProvider { private static final String CIPHER = "AES"; @Test public void testCreateKey() throws Exception { final Configuration conf = new Configuration(); KeyProvider kp = new UserProvider.Factory().createProvider(new URI("user:///"), conf); KeyACLs mock = mock(KeyACLs.class); when(mock.isACLPresent("foo", KeyOpType.MANAGEMENT)).thenReturn(true); UserGroupInformation u1 = UserGroupInformation.createRemoteUser("u1"); when(mock.hasAccessToKey("foo", u1, KeyOpType.MANAGEMENT)).thenReturn(true); final KeyProviderCryptoExtension kpExt = new KeyAuthorizationKeyProvider( KeyProviderCryptoExtension.createKeyProviderCryptoExtension(kp), mock); u1.doAs( new PrivilegedExceptionAction<Void>() { @Override public Void run() throws Exception { try { kpExt.createKey("foo", SecureRandom.getSeed(16), newOptions(conf)); } catch (IOException ioe) { Assert.fail("User should be Authorized !!"); } // "bar" key not configured try { kpExt.createKey("bar", SecureRandom.getSeed(16), newOptions(conf)); Assert.fail("User should NOT be Authorized !!"); } catch (IOException ioe) { // Ignore } return null; } } ); // Unauthorized User UserGroupInformation.createRemoteUser("badGuy").doAs( new PrivilegedExceptionAction<Void>() { @Override public Void run() throws Exception { try { kpExt.createKey("foo", SecureRandom.getSeed(16), newOptions(conf)); Assert.fail("User should NOT be Authorized !!"); } catch (IOException ioe) { // Ignore } return null; } } ); } @Test public void testOpsWhenACLAttributeExists() throws Exception { final Configuration conf = new Configuration(); KeyProvider kp = new UserProvider.Factory().createProvider(new URI("user:///"), conf); KeyACLs mock = mock(KeyACLs.class); when(mock.isACLPresent("testKey", KeyOpType.MANAGEMENT)).thenReturn(true); when(mock.isACLPresent("testKey", KeyOpType.GENERATE_EEK)).thenReturn(true); when(mock.isACLPresent("testKey", KeyOpType.DECRYPT_EEK)).thenReturn(true); when(mock.isACLPresent("testKey", KeyOpType.ALL)).thenReturn(true); UserGroupInformation u1 = UserGroupInformation.createRemoteUser("u1"); UserGroupInformation u2 = UserGroupInformation.createRemoteUser("u2"); UserGroupInformation u3 = UserGroupInformation.createRemoteUser("u3"); UserGroupInformation sudo = UserGroupInformation.createRemoteUser("sudo"); when(mock.hasAccessToKey("testKey", u1, KeyOpType.MANAGEMENT)).thenReturn(true); when(mock.hasAccessToKey("testKey", u2, KeyOpType.GENERATE_EEK)).thenReturn(true); when(mock.hasAccessToKey("testKey", u3, KeyOpType.DECRYPT_EEK)).thenReturn(true); when(mock.hasAccessToKey("testKey", sudo, KeyOpType.ALL)).thenReturn(true); final KeyProviderCryptoExtension kpExt = new KeyAuthorizationKeyProvider( KeyProviderCryptoExtension.createKeyProviderCryptoExtension(kp), mock); final KeyVersion barKv = u1.doAs( new PrivilegedExceptionAction<KeyVersion>() { @Override public KeyVersion run() throws Exception { Options opt = newOptions(conf); Map<String, String> m = new HashMap<String, String>(); m.put("key.acl.name", "testKey"); opt.setAttributes(m); try { KeyVersion kv = kpExt.createKey("foo", SecureRandom.getSeed(16), opt); kpExt.rollNewVersion(kv.getName()); kpExt.rollNewVersion(kv.getName(), SecureRandom.getSeed(16)); kpExt.deleteKey(kv.getName()); } catch (IOException ioe) { Assert.fail("User should be Authorized !!"); } KeyVersion retkv = null; try { retkv = kpExt.createKey("bar", SecureRandom.getSeed(16), opt); kpExt.generateEncryptedKey(retkv.getName()); Assert.fail("User should NOT be Authorized to generate EEK !!"); } catch (IOException ioe) { } Assert.assertNotNull(retkv); return retkv; } } ); final EncryptedKeyVersion barEKv = u2.doAs( new PrivilegedExceptionAction<EncryptedKeyVersion>() { @Override public EncryptedKeyVersion run() throws Exception { try { kpExt.deleteKey(barKv.getName()); Assert.fail("User should NOT be Authorized to " + "perform any other operation !!"); } catch (IOException ioe) { } return kpExt.generateEncryptedKey(barKv.getName()); } }); u3.doAs( new PrivilegedExceptionAction<KeyVersion>() { @Override public KeyVersion run() throws Exception { try { kpExt.deleteKey(barKv.getName()); Assert.fail("User should NOT be Authorized to " + "perform any other operation !!"); } catch (IOException ioe) { } return kpExt.decryptEncryptedKey(barEKv); } }); sudo.doAs( new PrivilegedExceptionAction<Void>() { @Override public Void run() throws Exception { Options opt = newOptions(conf); Map<String, String> m = new HashMap<String, String>(); m.put("key.acl.name", "testKey"); opt.setAttributes(m); try { KeyVersion kv = kpExt.createKey("foo", SecureRandom.getSeed(16), opt); kpExt.rollNewVersion(kv.getName()); kpExt.rollNewVersion(kv.getName(), SecureRandom.getSeed(16)); EncryptedKeyVersion ekv = kpExt.generateEncryptedKey(kv.getName()); kpExt.decryptEncryptedKey(ekv); kpExt.deleteKey(kv.getName()); } catch (IOException ioe) { Assert.fail("User should be Allowed to do everything !!"); } return null; } } ); } private static KeyProvider.Options newOptions(Configuration conf) { KeyProvider.Options options = new KeyProvider.Options(conf); options.setCipher(CIPHER); options.setBitLength(128); return options; } @Test(expected = IllegalArgumentException.class) public void testDecryptWithKeyVersionNameKeyMismatch() throws Exception { final Configuration conf = new Configuration(); KeyProvider kp = new UserProvider.Factory().createProvider(new URI("user:///"), conf); KeyACLs mock = mock(KeyACLs.class); when(mock.isACLPresent("testKey", KeyOpType.MANAGEMENT)).thenReturn(true); when(mock.isACLPresent("testKey", KeyOpType.GENERATE_EEK)).thenReturn(true); when(mock.isACLPresent("testKey", KeyOpType.DECRYPT_EEK)).thenReturn(true); when(mock.isACLPresent("testKey", KeyOpType.ALL)).thenReturn(true); UserGroupInformation u1 = UserGroupInformation.createRemoteUser("u1"); UserGroupInformation u2 = UserGroupInformation.createRemoteUser("u2"); UserGroupInformation u3 = UserGroupInformation.createRemoteUser("u3"); UserGroupInformation sudo = UserGroupInformation.createRemoteUser("sudo"); when(mock.hasAccessToKey("testKey", u1, KeyOpType.MANAGEMENT)).thenReturn(true); when(mock.hasAccessToKey("testKey", u2, KeyOpType.GENERATE_EEK)).thenReturn(true); when(mock.hasAccessToKey("testKey", u3, KeyOpType.DECRYPT_EEK)).thenReturn(true); when(mock.hasAccessToKey("testKey", sudo, KeyOpType.ALL)).thenReturn(true); final KeyProviderCryptoExtension kpExt = new KeyAuthorizationKeyProvider( KeyProviderCryptoExtension.createKeyProviderCryptoExtension(kp), mock); sudo.doAs( new PrivilegedExceptionAction<Void>() { @Override public Void run() throws Exception { Options opt = newOptions(conf); Map<String, String> m = new HashMap<String, String>(); m.put("key.acl.name", "testKey"); opt.setAttributes(m); KeyVersion kv = kpExt.createKey("foo", SecureRandom.getSeed(16), opt); kpExt.rollNewVersion(kv.getName()); kpExt.rollNewVersion(kv.getName(), SecureRandom.getSeed(16)); EncryptedKeyVersion ekv = kpExt.generateEncryptedKey(kv.getName()); ekv = EncryptedKeyVersion.createForDecryption( ekv.getEncryptionKeyName() + "x", ekv.getEncryptionKeyVersionName(), ekv.getEncryptedKeyIv(), ekv.getEncryptedKeyVersion().getMaterial()); kpExt.decryptEncryptedKey(ekv); return null; } } ); } }