blob: a539e1ee134273bfea4db3decb6390a8e4285cb0 [file] [log] [blame]
/**
* 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.kerby.kerberos.kerb.admin.server;
import org.apache.commons.io.FileUtils;
import org.apache.kerby.kerberos.kerb.admin.AuthUtil;
import org.apache.kerby.kerberos.kerb.admin.kadmin.local.LocalKadminImpl;
import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminClient;
import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminConfig;
import org.apache.kerby.kerberos.kerb.admin.kadmin.remote.AdminUtil;
import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServer;
import org.apache.kerby.kerberos.kerb.admin.server.kadmin.AdminServerConfig;
import org.apache.kerby.kerberos.kerb.identity.backend.BackendConfig;
import org.apache.kerby.kerberos.kerb.keytab.Keytab;
import org.apache.kerby.kerberos.kerb.server.KdcConfig;
import org.apache.kerby.kerberos.kerb.server.KdcServer;
import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
import org.apache.kerby.kerberos.kerb.transport.KrbNetwork;
import org.apache.kerby.kerberos.kerb.transport.KrbTransport;
import org.apache.kerby.kerberos.kerb.transport.TransportPair;
import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
import org.apache.kerby.util.NetworkUtil;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.Subject;
import javax.security.sasl.Sasl;
import javax.security.sasl.SaslClient;
import java.io.File;
import java.nio.ByteBuffer;
import java.security.PrivilegedAction;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;
public class RemoteKadminTest {
private static final Logger LOG = LoggerFactory.getLogger(RemoteKadminTest.class);
protected static File testDir = new File(System.getProperty("test.dir", "target"));
private static File testClassDir = new File(testDir, "test-classes");
private static File confDir = new File(testClassDir, "conf");
private static File workDir = new File(testDir, "work-dir");
private static final String SERVER_HOST = "localhost";
private static final String ADMIN_PRINCIPAL = "kadmin/EXAMPLE.COM@EXAMPLE.COM";
private static final String PROTOCOL_PRINCIPAL = "adminprotocol/localhost@EXAMPLE.COM";
private static KdcServer kdcServer;
private static LocalKadminImpl localKadmin;
private static AdminServer adminServer;
private static SimpleKdcServer buildMiniKdc() throws Exception {
KdcConfig config = new KdcConfig();
BackendConfig backendConfig = new BackendConfig();
backendConfig.addIniConfig(new File(confDir, "backend.conf"));
SimpleKdcServer kdc = new SimpleKdcServer(config, backendConfig);
kdc.setWorkDir(workDir);
kdc.setKdcHost(SERVER_HOST);
int kdcPort = NetworkUtil.getServerPort();
kdc.setAllowTcp(true);
kdc.setAllowUdp(false);
kdc.setKdcTcpPort(kdcPort);
LOG.info("Starting KDC server at " + SERVER_HOST + ":" + kdcPort);
kdc.init();
return kdc;
}
private static void buildLocalKadmin() throws Exception {
localKadmin = new LocalKadminImpl(kdcServer.getKdcSetting(), kdcServer.getIdentityService());
localKadmin.addPrincipal(PROTOCOL_PRINCIPAL);
localKadmin.exportKeytab(new File(workDir, "protocol.keytab"), PROTOCOL_PRINCIPAL);
localKadmin.exportKeytab(new File(workDir, "admin.keytab"), ADMIN_PRINCIPAL);
}
private static AdminServer buildAdminServer() throws Exception {
AdminServer server = new AdminServer(confDir);
AdminServerConfig config = server.getAdminServerConfig();
server.setAdminHost(config.getAdminHost());
server.setAllowTcp(true);
server.setAllowUdp(false);
server.setAdminServerPort(config.getAdminPort());
server.init();
return server;
}
private AdminClient buildKadminRemoteClient() throws Exception {
final AdminConfig config = new AdminConfig();
config.addKrb5Config(new File(confDir, "adminClient.conf"));
AdminClient adminClient = new AdminClient(config);
adminClient.setAdminRealm(config.getAdminRealm());
adminClient.setAllowTcp(true);
adminClient.setAllowUdp(false);
adminClient.setAdminTcpPort(config.getAdminPort());
adminClient.init();
doSaslHandShake(adminClient, config);
return adminClient;
}
@BeforeClass
public static void setUpBeforeClass() throws Exception {
workDir.mkdirs();
kdcServer = buildMiniKdc();
kdcServer.start();
buildLocalKadmin();
adminServer = buildAdminServer();
adminServer.start();
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
adminServer.stop();
kdcServer.stop();
FileUtils.deleteDirectory(workDir);
}
@Test
public void remoteListPrincipalTest() throws Exception {
AdminClient adminClient = buildKadminRemoteClient();
List<String> principals = adminClient.requestGetprincs();
assertTrue(principals.contains("kadmin/EXAMPLE.COM@EXAMPLE.COM"));
assertTrue(principals.contains("krbtgt/EXAMPLE.COM@EXAMPLE.COM"));
assertTrue(principals.contains("adminprotocol/localhost@EXAMPLE.COM"));
principals = adminClient.requestGetprincsWithExp("k*");
assertTrue(principals.contains("kadmin/EXAMPLE.COM@EXAMPLE.COM"));
assertTrue(principals.contains("krbtgt/EXAMPLE.COM@EXAMPLE.COM"));
assertFalse(principals.contains("adminprotocol/localhost@EXAMPLE.COM"));
}
@Test
public void remoteModifyPrincipalTest() throws Exception {
AdminClient adminClient = buildKadminRemoteClient();
String testPrincipal = "test/EXAMPLE.COM";
String renamePrincipal = "test_rename/EXAMPLE.COM";
adminClient.requestAddPrincipal(testPrincipal);
assertTrue("Remote kadmin add principal test failed.",
localKadmin.getPrincipals().contains(testPrincipal + "@EXAMPLE.COM"));
adminClient.requestRenamePrincipal(testPrincipal, renamePrincipal);
assertTrue("Remote kadmin rename principal test failed, the renamed principal does not exist.",
localKadmin.getPrincipals().contains(renamePrincipal + "@EXAMPLE.COM"));
assertFalse("Remote kadmin rename principal test failed, the old principal still exists.",
localKadmin.getPrincipals().contains(testPrincipal + "@EXAMPLE.COM"));
adminClient.requestDeletePrincipal(renamePrincipal);
assertFalse("Remote kadmin delete principal test failed.",
localKadmin.getPrincipals().contains(renamePrincipal + "@EXAMPLE.COM"));
}
@Test
public void remoteKtaddTest() throws Exception {
AdminClient adminClient = buildKadminRemoteClient();
String testPrincipal = "test/EXAMPLE.COM";
File keytabOutput = new File(testDir, "test.keytab");
localKadmin.addPrincipal(testPrincipal);
adminClient.requestExportKeytab(keytabOutput, testPrincipal);
Keytab localKeytab = Keytab.loadKeytab(keytabOutput);
List<String> principalNames = localKeytab.getPrincipals()
.stream().map(PrincipalName::getName).collect(Collectors.toList());
assertTrue(principalNames.contains(testPrincipal + "@EXAMPLE.COM"));
}
private void doSaslHandShake(AdminClient adminClient, AdminConfig config) throws Exception {
TransportPair tpair = AdminUtil.getTransportPair(adminClient.getSetting());
KrbNetwork network = new KrbNetwork();
network.setSocketTimeout(adminClient.getSetting().getTimeout());
KrbTransport transport = network.connect(tpair);
Subject subject = AuthUtil.loginUsingKeytab(ADMIN_PRINCIPAL, new File(config.getKeyTabFile()));
Subject.doAs(subject, (PrivilegedAction<Object>) () -> {
try {
Map<String, String> props = new HashMap<>();
props.put(Sasl.QOP, "auth-conf");
props.put(Sasl.SERVER_AUTH, "true");
SaslClient saslClient = null;
String protocol = config.getProtocol();
String serverName = config.getServerName();
saslClient = Sasl.createSaslClient(new String[]{"GSSAPI"}, null,
protocol, serverName, props, null);
if (saslClient == null) {
System.out.println("Unable to find client implementation for: GSSAPI");
return null;
}
byte[] response;
response = saslClient.hasInitialResponse()
? saslClient.evaluateChallenge(new byte[0]) : new byte[0];
sendMessage(response, saslClient, transport);
ByteBuffer message = transport.receiveMessage();
while (!saslClient.isComplete()) {
int ssComplete = message.getInt();
if (ssComplete == 0) {
System.out.println("Sasl Server completed");
}
byte[] arr = new byte[message.remaining()];
message.get(arr);
byte[] challenge = saslClient.evaluateChallenge(arr);
sendMessage(challenge, saslClient, transport);
if (!saslClient.isComplete()) {
message = transport.receiveMessage();
}
}
} catch (Exception e) {
LOG.warn(e.getMessage());
}
return null;
});
}
private void sendMessage(byte[] challenge, SaslClient saslClient, KrbTransport transport) throws Exception {
ByteBuffer buffer = ByteBuffer.allocate(challenge.length + 8);
buffer.putInt(challenge.length + 4);
int scComplete = saslClient.isComplete() ? 0 : 1;
buffer.putInt(scComplete);
buffer.put(challenge);
buffer.flip();
transport.sendMessage(buffer);
}
}