blob: 6d5353dbd9953966f63312db456a7fed4921a63d [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.has.server.kdc;
import org.apache.kerby.has.common.util.HasUtil;
import org.apache.kerby.has.server.HasServer;
import org.apache.kerby.kerberos.kerb.KrbCodec;
import org.apache.kerby.kerberos.kerb.KrbErrorCode;
import org.apache.kerby.kerberos.kerb.KrbException;
import org.apache.kerby.kerberos.kerb.client.KrbContext;
import org.apache.kerby.kerberos.kerb.common.EncryptionUtil;
import org.apache.kerby.kerberos.kerb.common.KrbUtil;
import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
import org.apache.kerby.kerberos.kerb.server.KdcContext;
import org.apache.kerby.kerberos.kerb.server.KdcRecoverableException;
import org.apache.kerby.kerberos.kerb.server.KdcServer;
import org.apache.kerby.kerberos.kerb.server.preauth.PreauthHandler;
import org.apache.kerby.kerberos.kerb.server.request.AsRequest;
import org.apache.kerby.kerberos.kerb.server.request.KdcRequest;
import org.apache.kerby.kerberos.kerb.type.KerberosTime;
import org.apache.kerby.kerberos.kerb.type.base.AuthToken;
import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
import org.apache.kerby.kerberos.kerb.type.base.EncryptionType;
import org.apache.kerby.kerberos.kerb.type.base.HostAddress;
import org.apache.kerby.kerberos.kerb.type.base.HostAddresses;
import org.apache.kerby.kerberos.kerb.type.base.KrbError;
import org.apache.kerby.kerberos.kerb.type.base.KrbMessage;
import org.apache.kerby.kerberos.kerb.type.base.KrbToken;
import org.apache.kerby.kerberos.kerb.type.base.PrincipalName;
import org.apache.kerby.kerberos.kerb.type.base.TokenFormat;
import org.apache.kerby.kerberos.kerb.type.kdc.AsReq;
import org.apache.kerby.kerberos.kerb.type.kdc.KdcOption;
import org.apache.kerby.kerberos.kerb.type.kdc.KdcOptions;
import org.apache.kerby.kerberos.kerb.type.kdc.KdcReqBody;
import org.apache.kerby.kerberos.kerb.type.pa.PaData;
import org.apache.kerby.kerberos.kerb.type.pa.PaDataEntry;
import org.apache.kerby.kerberos.kerb.type.pa.PaDataType;
import org.apache.kerby.kerberos.kerb.type.pa.token.PaTokenRequest;
import org.apache.kerby.kerberos.kerb.type.pa.token.TokenInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class HasKdcHandler {
private static final Logger LOG = LoggerFactory.getLogger(HasKdcHandler.class);
private KdcContext kdcContext;
private KrbContext krbContext;
private KdcServer kdcServer;
/**
* Constructor with has server.
*
* @param hasServer has server
*/
public HasKdcHandler(HasServer hasServer) {
this.krbContext = new KrbContext();
this.krbContext.init(hasServer.getKrbSetting());
this.kdcServer = hasServer.getKdcServer();
prepareHandler(kdcServer);
}
public KrbContext getKrbContext() {
return krbContext;
}
public KdcContext getKdcContext() {
return kdcContext;
}
private KdcServer getKdcServer() {
return kdcServer;
}
private void prepareHandler(KdcServer kdcServer) {
this.kdcContext = new KdcContext(kdcServer.getKdcSetting());
this.kdcContext.setIdentityService(kdcServer.getIdentityService());
PreauthHandler preauthHandler = new PreauthHandler();
preauthHandler.init();
this.kdcContext.setPreauthHandler(preauthHandler);
}
private String getAudience(String name) {
return name + "/" + getKdcContext().getKdcRealm() + "@" + getKdcContext().getKdcRealm();
}
public KrbMessage getResponse(AuthToken authToken, String passPhrase) {
KrbMessage krbMessage = null;
try {
krbMessage = handleMessage(authToken, passPhrase);
} catch (KrbException e) {
LOG.error("Failed to handle message. " + e.getMessage());
}
return krbMessage;
}
/**
* Process the client request message.
*/
public KrbMessage handleMessage(AuthToken authToken, String passPhrase) throws KrbException {
// set the audiences
List<String> auds = new ArrayList<>();
String audience = getAudience("krbtgt");
auds.add(audience);
authToken.setAudiences(auds);
AsReq asReq = createAsReq(authToken);
KdcRequest kdcRequest = new AsRequest(asReq, kdcContext);
kdcRequest.setHttps(true);
List<EncryptionType> requestedTypes = getEncryptionTypes();
EncryptionType bestType = EncryptionUtil.getBestEncryptionType(requestedTypes,
kdcContext.getConfig().getEncryptionTypes());
if (bestType == null) {
LOG.error("Can't get the best encryption type.");
throw new KrbException(KrbErrorCode.KDC_ERR_ETYPE_NOSUPP);
}
PrincipalName clientPrincipal = new PrincipalName(authToken.getSubject());
String clientRealm = asReq.getReqBody().getRealm();
if (clientRealm == null || clientRealm.isEmpty()) {
clientRealm = getKdcContext().getKdcRealm();
}
clientPrincipal.setRealm(clientRealm);
// Set the client key
EncryptionKey clientKey = HasUtil.getClientKey(clientPrincipal.getName(),
passPhrase, bestType);
kdcRequest.setClientKey(clientKey);
// Set the token issuers
getKdcServer().getKdcConfig().setString(KdcConfigKey.TOKEN_ISSUERS, "has");
KrbMessage krbResponse;
try {
kdcRequest.process();
krbResponse = kdcRequest.getReply();
} catch (KrbException e) {
LOG.error("Error occurred when request tgt. " + e.getMessage());
if (e instanceof KdcRecoverableException) {
krbResponse = handleRecoverableException(
(KdcRecoverableException) e, kdcRequest);
} else {
KrbError krbError = new KrbError();
krbError.setStime(KerberosTime.now());
krbError.setSusec(100);
if (e.getKrbErrorCode() != null) {
krbError.setErrorCode(e.getKrbErrorCode());
} else {
krbError.setErrorCode(KrbErrorCode.UNKNOWN_ERR);
}
krbError.setCrealm(kdcContext.getKdcRealm());
if (kdcRequest.getClientPrincipal() != null) {
krbError.setCname(kdcRequest.getClientPrincipal());
}
krbError.setRealm(kdcContext.getKdcRealm());
if (kdcRequest.getServerPrincipal() != null) {
krbError.setSname(kdcRequest.getServerPrincipal());
} else {
PrincipalName serverPrincipal = kdcRequest.getKdcReq().getReqBody().getSname();
serverPrincipal.setRealm(kdcRequest.getKdcReq().getReqBody().getRealm());
krbError.setSname(serverPrincipal);
}
if (KrbErrorCode.KRB_AP_ERR_BAD_INTEGRITY.equals(e.getKrbErrorCode())) {
krbError.setEtext("PREAUTH_FAILED");
} else {
krbError.setEtext(e.getMessage());
}
krbResponse = krbError;
}
}
return krbResponse;
}
/**
* Process the recoverable exception.
*
* @param e The exception return by kdc
* @param kdcRequest kdc request
* @return The KrbError
*/
private KrbMessage handleRecoverableException(KdcRecoverableException e,
KdcRequest kdcRequest)
throws KrbException {
LOG.info("KRB error occurred while processing request:"
+ e.getMessage());
KrbError error = e.getKrbError();
error.setStime(KerberosTime.now());
error.setSusec(100);
error.setErrorCode(e.getKrbError().getErrorCode());
error.setRealm(kdcContext.getKdcRealm());
if (kdcRequest != null) {
error.setSname(kdcRequest.getKdcReq().getReqBody().getCname());
} else {
error.setSname(new PrincipalName("NONE"));
}
error.setEtext(e.getMessage());
return error;
}
public AsReq createAsReq(AuthToken authToken) throws KrbException {
AsReq asReq = new AsReq();
KdcReqBody body = makeReqBody();
asReq.setReqBody(body);
PaTokenRequest tokenPa = new PaTokenRequest();
KrbToken krbToken = new KrbToken(authToken, TokenFormat.JWT);
tokenPa.setToken(krbToken);
TokenInfo info = new TokenInfo();
info.setTokenVendor(authToken.getIssuer());
tokenPa.setTokenInfo(info);
PaDataEntry paDataEntry = new PaDataEntry();
paDataEntry.setPaDataType(PaDataType.TOKEN_REQUEST);
paDataEntry.setPaDataValue(KrbCodec.encode(tokenPa));
PaData paData = new PaData();
paData.addElement(paDataEntry);
asReq.setPaData(paData);
return asReq;
}
/**
* Create the KdcReqBody
*
* @return KdcReqBody
*
* @throws KrbException e
*/
protected KdcReqBody makeReqBody() throws KrbException {
KdcReqBody body = new KdcReqBody();
long startTime = System.currentTimeMillis();
body.setFrom(new KerberosTime(startTime));
// set the client principal as null
PrincipalName cName = null;
body.setCname(cName);
body.setRealm(getKrbContext().getKrbSetting().getKdcRealm());
PrincipalName sName = getServerPrincipal();
body.setSname(sName);
body.setTill(new KerberosTime(startTime + krbContext.getTicketValidTime()));
int nonce = krbContext.generateNonce();
body.setNonce(nonce);
// setChosenNonce(nonce);
body.setKdcOptions(getKdcOptions());
HostAddresses addresses = getHostAddresses();
if (addresses != null) {
body.setAddresses(addresses);
}
body.setEtypes(getEncryptionTypes());
return body;
}
private PrincipalName getServerPrincipal() {
return KrbUtil.makeTgsPrincipal(getKrbContext().getKrbSetting().getKdcRealm());
}
private KdcOptions getKdcOptions() {
KdcOptions kdcOptions = new KdcOptions();
// By default enforce these flags
kdcOptions.setFlag(KdcOption.FORWARDABLE);
kdcOptions.setFlag(KdcOption.PROXIABLE);
kdcOptions.setFlag(KdcOption.RENEWABLE_OK);
return kdcOptions;
}
public HostAddresses getHostAddresses() {
List<HostAddress> hostAddresses = new ArrayList<>();
HostAddresses addresses = null;
//empty
if (!hostAddresses.isEmpty()) {
addresses = new HostAddresses();
for (HostAddress ha : hostAddresses) {
addresses.addElement(ha);
}
}
return addresses;
}
public List<EncryptionType> getEncryptionTypes() {
List<EncryptionType> encryptionTypes = krbContext.getConfig().getEncryptionTypes();
return EncryptionUtil.orderEtypesByStrength(encryptionTypes);
}
}