/*
*
* 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 com.jcraft.jsch;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.airavata.gsi.ssh.GSSContextX509;
import org.apache.airavata.gsi.ssh.api.authentication.GSIAuthenticationInfo;
import org.globus.gsi.gssapi.GSSConstants;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.Oid;
/* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
/*
* Copyright(c)2004,2005,2006 ymnk, JCraft,Inc. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. The names of the authors may not
* be used to endorse or promote products derived from this software without
* specific prior written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND
* ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS
* SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION)HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE)ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/**
* This class now supports two mappings to the gssapi-with-mic method: x509
* (preferred) and krb5.
*
* @author Al Rossi
* @author Jeff Overbey
*/
public class UserAuthGSSAPIWithMICGSSCredentials extends UserAuth {
private static final int SSH_MSG_USERAUTH_GSSAPI_RESPONSE = 60;
private static final int SSH_MSG_USERAUTH_GSSAPI_TOKEN = 61;
// private static final int SSH_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE = 63;
private static final int SSH_MSG_USERAUTH_GSSAPI_ERROR = 64;
private static final int SSH_MSG_USERAUTH_GSSAPI_ERRTOK = 65;
private static final int SSH_MSG_USERAUTH_GSSAPI_MIC = 66;
// this is the preferred order
private static String[] supportedMethods = { "gssapi-with-mic.x509",
"gssapi-with-mic.krb5" };
private static byte[][] supportedOids;
static {
try {
supportedOids = new byte[][] {
GSSConstants.MECH_OID.getDER(),
new Oid("1.2.840.113554.1.2.2").getDER() };
} catch (GSSException gsse) {
gsse.printStackTrace();
}
}
@Override
public boolean start(Session session) throws Exception {
// this.userinfo = userinfo;
Packet packet = session.packet;
Buffer buf = session.buf;
final String username = session.username;
byte[] _username = Util.str2byte(username);
// checkForSupportedOIDs
List methods = new ArrayList();
boolean found = false;
for (int i = 0; i < supportedOids.length; i++) {
found = found
|| checkForSupportedOIDs(methods, packet, buf, i,
_username, session);
}
if (!found)
return false;
// logger.debug( "supported methods " + methods );
boolean success = false;
for (Iterator it = methods.iterator(); it.hasNext();) {
String method = (String) it.next();
success = tryMethod(username, _username, method, session, packet,
buf);
if (success)
break;
}
return success;
}
private boolean checkForSupportedOIDs(List methods, Packet packet,
Buffer buf, int index, byte[] _username, Session session)
throws Exception {
packet.reset();
// byte SSH_MSG_USERAUTH_REQUEST(50)
// string user name(in ISO-10646 UTF-8 encoding)
// string service name(in US-ASCII)
// string "gssapi"(US-ASCII)
// uint32 n, the number of OIDs client supports
// string[n] mechanism OIDS
buf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
buf.putString(_username);
buf.putString("ssh-connection".getBytes());
buf.putString("gssapi-with-mic".getBytes());
buf.putInt(1);
buf.putString(supportedOids[index]);
session.write(packet);
while (true) {
buf = session.read(buf);
if (buf.buffer[5] == SSH_MSG_USERAUTH_FAILURE) {
return false;
}
if (buf.buffer[5] == SSH_MSG_USERAUTH_GSSAPI_RESPONSE) {
buf.getInt();
buf.getByte();
buf.getByte();
byte[] message = buf.getString();
// logger.debug( "OID " + supportedOids[index] );
if (Util.array_equals(message, supportedOids[index])) {
methods.add(supportedMethods[index]);
// logger.debug( "OID MATCH, method is " + methods );
return true;
}
}
if (buf.buffer[5] == SSH_MSG_USERAUTH_BANNER) {
buf.getInt();
buf.getByte();
buf.getByte();
byte[] _message = buf.getString();
buf.getString();
String message = Util.byte2str(_message);
if (userinfo != null) {
userinfo.showMessage(message);
}
continue;
}
return false;
}
}
private boolean tryMethod(String username, byte[] _username, String method,
Session session, Packet packet, Buffer buf) throws Exception {
GSSContext context = null;
try {
Class c = Class.forName(session.getConfig(method));
context = (GSSContext) (c.newInstance());
} catch (Exception e) {
// logger.error( "could not instantiate GSSContext", e );
return false;
}
// Get the credentials and set them
// Not a good way, but we dont have any choice
if (session instanceof ExtendedSession) {
GSIAuthenticationInfo authenticationInfo = ((ExtendedSession) session).getAuthenticationInfo();
if (context instanceof GSSContextX509) {
((GSSContextX509) context).setCredential(authenticationInfo.getCredentials());
}
}
// logger.debug( "GOT CONTEXT: " + context );
// FIXME
// if ( userinfo instanceof IX509UserInfo ) {
// if ( context instanceof GSSContextX509 ) {
// GSSCredential credential = ( ( IX509UserInfo )userinfo
// ).getCredential();
// logger.debug( "user info credential = " + credential );
// ( ( GSSContextX509 )context ).setCredential( credential );
// }
// }
try {
context.create(username, session.host);
} catch (JSchException e) {
// logger.error( "context creation failed", e );
return false;
}
byte[] token = new byte[0];
while (!context.isEstablished()) {
try {
token = context.init(token, 0, token.length);
} catch (JSchException e) {
// logger.error( "context initialization failed", e );
// TODO
// ERRTOK should be sent?
// byte SSH_MSG_USERAUTH_GSSAPI_ERRTOK
// string error token
return false;
}
if (token != null) {
packet.reset();
buf.putByte((byte) SSH_MSG_USERAUTH_GSSAPI_TOKEN);
buf.putString(token);
session.write(packet);
}
if (!context.isEstablished()) {
buf = session.read(buf);
if (buf.buffer[5] == SSH_MSG_USERAUTH_GSSAPI_ERROR) {
// uint32 major_status
// uint32 minor_status
// string message
// string language tag
buf = session.read(buf);
} else if (buf.buffer[5] == SSH_MSG_USERAUTH_GSSAPI_ERRTOK) {
buf = session.read(buf);
}
if (buf.buffer[5] == SSH_MSG_USERAUTH_FAILURE) {
return false;
}
buf.getInt();
buf.getByte();
buf.getByte();
token = buf.getString();
}
}
Buffer mbuf = new Buffer();
// string session identifier
// byte SSH_MSG_USERAUTH_REQUEST
// string user name
// string service
// string "gssapi-with-mic"
mbuf.putString(session.getSessionId());
mbuf.putByte((byte) SSH_MSG_USERAUTH_REQUEST);
mbuf.putString(_username);
mbuf.putString("ssh-connection".getBytes());
mbuf.putString("gssapi-with-mic".getBytes());
byte[] mic = context.getMIC(mbuf.buffer, 0, mbuf.getLength());
if (mic == null) { // there was an error in the getMIC call
return false;
}
packet.reset();
buf.putByte((byte) SSH_MSG_USERAUTH_GSSAPI_MIC);
buf.putString(mic);
session.write(packet);
context.dispose();
buf = session.read(buf);
if (buf.buffer[5] == SSH_MSG_USERAUTH_SUCCESS) {
return true;
}
if (buf.buffer[5] == SSH_MSG_USERAUTH_FAILURE) {
buf.getInt();
buf.getByte();
buf.getByte();
byte[] foo = buf.getString();
int partial_success = buf.getByte();
if (partial_success != 0) {
throw new JSchPartialAuthException(new String(foo));
}
}
return false;
}
}