package org.apache.kerberos.kerb.client;
import org.apache.haox.event.Event;
import org.apache.haox.event.EventHub;
import org.apache.haox.event.EventWaiter;
import org.apache.kerberos.kerb.KrbErrorCode;
import org.apache.kerberos.kerb.client.event.KrbClientEvent;
import org.apache.kerberos.kerb.client.event.KrbClientEventType;
import org.apache.kerberos.kerb.client.request.*;
import org.apache.kerberos.kerb.common.KrbErrorUtil;
import org.apache.kerberos.kerb.common.KrbStreamingDecoder;
import org.apache.kerberos.kerb.KrbErrorException;
import org.apache.kerberos.kerb.KrbException;
import org.apache.kerberos.kerb.spec.common.KrbError;
import org.apache.kerberos.kerb.spec.common.PrincipalName;
import org.apache.kerberos.kerb.spec.ticket.ServiceTicket;
import org.apache.kerberos.kerb.spec.ticket.TgtTicket;
import org.haox.token.KerbToken;
import org.apache.haox.transport.Connector;
import org.apache.haox.transport.Transport;
import org.apache.haox.transport.event.TransportEvent;
import org.apache.haox.transport.event.TransportEventType;
import org.apache.haox.transport.tcp.TcpConnector;
import java.io.IOException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* A krb client API for applications to interact with KDC
*/
public class KrbClient {
private EventHub eventHub;
private EventWaiter eventWaiter;
private Transport transport;
private KrbHandler krbHandler;
private KrbContext context;
private KrbConfig config;
/**
*
* @param kdcHost
* @param kdcPort
*/
public KrbClient(String kdcHost, short kdcPort) {
this(new KrbConfig());
setKdcHost(kdcHost);
setKdcPort(kdcPort);
}
public KrbClient(KrbConfig config) {
this.config = config;
this.context = new KrbContext();
context.init(config);
}
/**
* Set KDC realm for ticket request
* @param realm
*/
public void setKdcRealm(String realm) {
context.setKdcRealm(realm);
}
/**
*
* @param kdcHost
*/
public void setKdcHost(String kdcHost) {
context.setKdcHost(kdcHost);
}
/**
*
* @param kdcPort
*/
public void setKdcPort(short kdcPort) {
context.setKdcPort(kdcPort);
}
/**
* Set time out for connection
* @param timeout in seconds
*/
public void setTimeout(long timeout) {
context.setTimeout(timeout);
}
public void init() {
this.krbHandler = new KrbHandler();
krbHandler.init(context);
this.eventHub = new EventHub();
eventHub.register(krbHandler);
Connector connector = new TcpConnector(new KrbStreamingDecoder());
eventHub.register(connector);
eventWaiter = eventHub.waitEvent(
TransportEventType.NEW_TRANSPORT,
KrbClientEventType.TGT_RESULT,
KrbClientEventType.TKT_RESULT
);
eventHub.start();
connector.connect(context.getKdcHost(), context.getKdcPort());
Event event = eventWaiter.waitEvent(TransportEventType.NEW_TRANSPORT);
transport = ((TransportEvent) event).getTransport();
}
/**
* Attempt to request a TGT and you'll be prompted to input a credential.
* Whatever credential requested to provide depends on KDC admin configuration.
* @param options
* @return
* @throws KrbException
*/
public TgtTicket requestTgtTicket(String principal, KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
AsRequest asRequest = new AsRequest(context);
asRequest.setKrbOptions(options);
return requestTgtTicket(principal, asRequest);
}
/**
* Request a TGT with user plain credential
* @param principal
* @param password
* @param options
* @return
* @throws KrbException
*/
public TgtTicket requestTgtTicket(String principal, String password,
KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
AsRequest asRequest = new AsRequestWithPasswd(context);
options.add(KrbOption.USER_PASSWD, password);
asRequest.setKrbOptions(options);
return requestTgtTicket(principal, asRequest);
}
/**
* Request a TGT with user x509 certificate credential
* @param principal
* @param certificate
* @param privateKey
* @param options
* @return
* @throws KrbException
*/
public TgtTicket requestTgtTicket(String principal, Certificate certificate,
PrivateKey privateKey, KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
AsRequestWithCert asRequest = new AsRequestWithCert(context);
options.add(KrbOption.PKINIT_X509_CERTIFICATE, certificate);
options.add(KrbOption.PKINIT_X509_PRIVATE_KEY, privateKey);
asRequest.setKrbOptions(options);
return requestTgtTicket(principal, asRequest);
}
/**
* Request a TGT with using Anonymous PKINIT
* @param options
* @return
* @throws KrbException
*/
public TgtTicket requestTgtTicket(KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
AsRequestWithCert asRequest = new AsRequestWithCert(context);
options.add(KrbOption.PKINIT_X509_ANONYMOUS);
asRequest.setKrbOptions(options);
String principal = AsRequestWithCert.ANONYMOUS_PRINCIPAL;
return requestTgtTicket(principal, asRequest);
}
/**
* Request a TGT with user token credential
* @param principal
* @param token
* @param options
* @return
* @throws KrbException
*/
public TgtTicket requestTgtTicket(String principal, KerbToken token,
KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
AsRequestWithToken asRequest = new AsRequestWithToken(context);
options.add(KrbOption.TOKEN_USER_ID_TOKEN, token);
asRequest.setKrbOptions(options);
return requestTgtTicket(principal, asRequest);
}
/**
* Request a service ticket targeting for a server with user plain credentials
* @param clientPrincipal
* @param password
* @param serverPrincipal
* @param options
* @return
* @throws KrbException
*/
public ServiceTicket requestServiceTicket(String clientPrincipal, String password,
String serverPrincipal, KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
TgtTicket tgt = requestTgtTicket(clientPrincipal, password, options);
return requestServiceTicket(tgt, serverPrincipal, options);
}
/**
* Request a service ticket targeting for a server with an user Access Token
* @param clientPrincipal
* @param token
* @param serverPrincipal
* @param options
* @return
* @throws KrbException
*/
public ServiceTicket requestServiceTicket(String clientPrincipal, KerbToken token,
String serverPrincipal, KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
TgtTicket tgt = requestTgtTicket(clientPrincipal, token, options);
return requestServiceTicket(tgt, serverPrincipal, options);
}
private TgtTicket requestTgtTicket(String clientPrincipal, AsRequest tgtTktReq) throws KrbException {
tgtTktReq.setClientPrincipal(new PrincipalName(clientPrincipal));
tgtTktReq.setTransport(transport);
try {
return doRequestTgtTicket(tgtTktReq);
} catch(KrbErrorException e) {
KrbError krbError = e.getKrbError();
if (krbError.getErrorCode() == KrbErrorCode.KDC_ERR_PREAUTH_REQUIRED) {
try {
tgtTktReq.setEncryptionTypes(KrbErrorUtil.getEtypes(krbError));
} catch (IOException ioe) {
throw new KrbException("Failed to decode and get etypes from krbError", ioe);
}
tgtTktReq.getPreauthContext().setPreauthRequired(true);
return requestTgtTicket(clientPrincipal, tgtTktReq);
}
throw e;
}
}
private TgtTicket doRequestTgtTicket(AsRequest tgtTktReq) throws KrbException {
eventHub.dispatch(KrbClientEvent.createTgtIntentEvent(tgtTktReq));
Event resultEvent = null;
try {
resultEvent = eventWaiter.waitEvent(KrbClientEventType.TGT_RESULT,
context.getTimeout(), TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new KrbException("Network timeout", e);
}
AsRequest asResponse = (AsRequest) resultEvent.getEventData();
return asResponse.getTicket();
}
/**
* Request a service ticket with a TGT targeting for a server
* @param tgt
* @param serverPrincipal
* @return
* @throws KrbException
*/
public ServiceTicket requestServiceTicket(TgtTicket tgt, String serverPrincipal,
KrbOptions options) throws KrbException {
if (options == null) options = new KrbOptions();
TgsRequest ticketReq = new TgsRequest(context, tgt);
ticketReq.setServerPrincipal(new PrincipalName(serverPrincipal));
ticketReq.setTransport(transport);
eventHub.dispatch(KrbClientEvent.createTktIntentEvent(ticketReq));
Event resultEvent = null;
try {
resultEvent = eventWaiter.waitEvent(KrbClientEventType.TKT_RESULT,
context.getTimeout(), TimeUnit.SECONDS);
} catch (TimeoutException e) {
throw new KrbException("Network timeout", e);
}
TgsRequest tgsResponse = (TgsRequest) resultEvent.getEventData();
return tgsResponse.getServiceTicket();
}
}