Commit b32999f5 authored by Tigran Mkrtchyan's avatar Tigran Mkrtchyan
Browse files

rpc: add rfc2203 compliant RPCGSS_SEC support.

Introduced gss aware grizzly filter to create gss context.
For established contexts replaces rpc call with a wrapper
which can handle different QOS. Supports NONE, INTEGRITY
and PRIVACY qos.

RFC 2203 complient.
parent 9a0bd607
......@@ -21,5 +21,6 @@ public interface RpcAuthType {
static public final int NONE = 0;
static public final int UNIX = 1;
static public final int RPCGSS_SEC = 6;
}
......@@ -75,11 +75,16 @@ public class RpcCall {
private final Xdr _xdr;
public RpcCall(int prog, int ver, RpcAuth cred, XdrTransport transport) {
this(prog, ver, cred, new Xdr(Xdr.MAX_XDR_SIZE), transport);
}
public RpcCall(int prog, int ver, RpcAuth cred, Xdr xdr, XdrTransport transport) {
_prog = prog;
_version = ver;
_cred = cred;
_transport = transport;
_xdr = new Xdr(Xdr.MAX_XDR_SIZE);
_xdr = xdr;
_proc = 0;
}
public RpcCall(int xid, Xdr xdr, XdrTransport transport) {
......@@ -88,6 +93,17 @@ public class RpcCall {
_transport = transport;
}
public RpcCall(int xid, int prog, int ver, int proc, RpcAuth cred, Xdr xdr, XdrTransport transport) {
_xid = xid;
_prog = prog;
_version = ver;
_proc = proc;
_cred = cred;
_xdr = xdr;
_transport = transport;
_rpcvers = RPCVERS;
}
public void accept() throws IOException, OncRpcException {
_rpcvers = _xdr.xdrDecodeInt();
if (_rpcvers != RPCVERS) {
......@@ -135,6 +151,14 @@ public class RpcCall {
return _transport;
}
public int getXid() {
return _xid;
}
public Xdr getXdr() {
return _xdr;
}
@Override
public String toString() {
return String.format("RPCv%d call: program=%d, version=%d, procedure=%d",
......@@ -180,7 +204,7 @@ public class RpcCall {
acceptedReply(RpcAccepsStatus.SUCCESS, reply);
}
private void acceptedReply(int state, XdrAble reply) {
public void acceptedReply(int state, XdrAble reply) {
XdrEncodingStream xdr = _xdr;
try {
......
......@@ -18,6 +18,7 @@
package org.dcache.xdr;
import java.io.IOException;
import org.dcache.xdr.gss.RpcAuthGss;
/**
* The RPC call message has two authentication fields - the credential and verifier.
......@@ -39,6 +40,9 @@ public class RpcCredential {
case RpcAuthType.NONE:
credential = new RpcAuthTypeNone();
break;
case RpcAuthType.RPCGSS_SEC:
credential = new RpcAuthGss();
break;
default:
throw new RpcAuthException("Unsuported type: " + authType,
new RpcAuthError(RpcAuthStat.AUTH_FAILED));
......
......@@ -70,6 +70,9 @@ public class RpcDispatcher implements ProtocolFilter {
}else{
try {
program.dispatchOncRpcCall(call);
} catch (RpcException e) {
call.reject(e.getStatus(), e.getRpcReply());
_log.log(Level.SEVERE, "Failed to process RPC request:", e);
} catch (OncRpcException e) {
_log.log(Level.SEVERE, "Failed to process RPC request:", e);
}
......
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr.gss;
import java.io.IOException;
import org.dcache.xdr.OncRpcException;
import org.dcache.xdr.XdrAble;
import org.dcache.xdr.XdrDecodingStream;
import org.dcache.xdr.XdrEncodingStream;
/**
* RPCGSS_SEC data body for integrity QOS as defined in RFC 2203
*/
public class DataBodyInteg implements XdrAble {
private byte[] data;
private byte[] checksum;
public DataBodyInteg() {
}
public DataBodyInteg(byte[] data, byte[] checksum) {
this.data = data;
this.checksum = checksum;
}
@Override
public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException {
data = xdr.xdrDecodeDynamicOpaque();
checksum = xdr.xdrDecodeDynamicOpaque();
}
@Override
public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException {
xdr.xdrEncodeDynamicOpaque(data);
xdr.xdrEncodeDynamicOpaque(checksum);
}
public byte[] getChecksum() {
return checksum;
}
public byte[] getData() {
return data;
}
}
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr.gss;
import java.io.IOException;
import org.dcache.xdr.OncRpcException;
import org.dcache.xdr.XdrAble;
import org.dcache.xdr.XdrDecodingStream;
import org.dcache.xdr.XdrEncodingStream;
/**
* RPCGSS_SEC data body for privacy QOS as defined in RFC 2203
*/
public class DataBodyPrivacy implements XdrAble {
byte[] data;
public DataBodyPrivacy() {
}
public DataBodyPrivacy(byte[] data) {
this.data = data;
}
@Override
public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException {
data = xdr.xdrDecodeDynamicOpaque();
}
@Override
public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException {
xdr.xdrEncodeDynamicOpaque(data);
}
public byte[] getData() {
return data;
}
}
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr.gss;
import java.io.IOException;
import org.dcache.xdr.OncRpcException;
import org.dcache.xdr.XdrAble;
import org.dcache.xdr.XdrDecodingStream;
import org.dcache.xdr.XdrEncodingStream;
/**
* The data for a GSS context creation request.
*/
public class GSSINITargs implements XdrAble {
private byte[] token;
public byte[] getToken() {
return token;
}
public void setToken(byte[] token) {
this.token = token;
}
public GSSINITargs() {
}
public GSSINITargs(byte[] token) {
this.token = token;
}
public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException {
token = xdr.xdrDecodeDynamicOpaque();
}
public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException {
xdr.xdrEncodeDynamicOpaque(token);
}
}
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr.gss;
import java.io.IOException;
import org.dcache.xdr.OncRpcException;
import org.dcache.xdr.XdrAble;
import org.dcache.xdr.XdrDecodingStream;
import org.dcache.xdr.XdrEncodingStream;
/**
*
* @author tigran
*/
public class GSSINITres implements XdrAble {
private byte[] handle;
private int gssMajor;
private int gssMinor;
private int sequence;
private byte[] token;
public int getGssMajor() {
return gssMajor;
}
public void setGssMajor(int gssMajor) {
this.gssMajor = gssMajor;
}
public int getGssMinor() {
return gssMinor;
}
public void setGssMinor(int gssMinor) {
this.gssMinor = gssMinor;
}
public byte[] getHandle() {
return handle;
}
public void setHandle(byte[] handle) {
this.handle = handle;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public byte[] getToken() {
return token;
}
public void setToken(byte[] token) {
this.token = token;
}
public void xdrDecode(XdrDecodingStream xdr) throws OncRpcException, IOException {
handle = xdr.xdrDecodeDynamicOpaque();
gssMajor = xdr.xdrDecodeInt();
gssMinor = xdr.xdrDecodeInt();
sequence = xdr.xdrDecodeInt();
token = xdr.xdrDecodeDynamicOpaque();
}
public void xdrEncode(XdrEncodingStream xdr) throws OncRpcException, IOException {
xdr.xdrEncodeDynamicOpaque(handle);
xdr.xdrEncodeInt(gssMajor);
xdr.xdrEncodeInt(gssMinor);
xdr.xdrEncodeInt(sequence);
xdr.xdrEncodeDynamicOpaque(token);
}
}
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr.gss;
public interface GssProc {
public static final int RPCSEC_GSS_DATA = 0;
public static final int RPCSEC_GSS_INIT = 1;
public static final int RPCSEC_GSS_CONTINUE_INIT = 2;
public static final int RPCSEC_GSS_DESTROY = 3;
}
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr.gss;
import com.sun.grizzly.Context;
import com.sun.grizzly.ProtocolFilter;
import com.google.common.primitives.Ints;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.security.auth.kerberos.KerberosPrincipal;
import org.dcache.xdr.OncRpcException;
import org.dcache.xdr.RpcAuthError;
import org.dcache.xdr.RpcAuthException;
import org.dcache.xdr.RpcAuthStat;
import org.dcache.xdr.RpcAuthType;
import org.dcache.xdr.RpcAuthVerifier;
import org.dcache.xdr.RpcCall;
import org.dcache.xdr.RpcException;
import org.dcache.xdr.RpcProtocolFilter;
import org.dcache.xdr.RpcRejectStatus;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.MessageProp;
/**
* A {@link ProtocolFilter} that handles RPCSEC_GSS requests.
* Filter is responsible to establish and destroy GSS context.
* For requests with established contexts RPC requests repacked into
* GSS aware {@link RpsGssCall}.
*
* @since 0.0.4
*/
public class GssProtocolFilter implements ProtocolFilter {
private final static Logger _log = Logger.getLogger(GssProtocolFilter.class.getName());
/**
* Return value from either accept or init stating that
* the context creation phase is complete for this peer.
* @see #init
* @see #accept
*/
public static final int COMPLETE = 0;
/**
* Return value from either accept or init stating that
* another token is required from the peer to continue context
* creation. This may be returned several times indicating
* multiple token exchanges.
* @see #init
* @see #accept
*/
public static final int CONTINUE_NEEDED = 1;
private final GssSessionManager _gssSessionManager;
public GssProtocolFilter(GssSessionManager gssSessionManager) {
_gssSessionManager = gssSessionManager;
}
@Override
public boolean execute(Context context) throws IOException {
RpcCall call = (RpcCall) context.getAttribute(RpcProtocolFilter.RPC_CALL);
if (call.getCredential().type() != RpcAuthType.RPCGSS_SEC) {
return true;
}
boolean hasContext = false;
try {
RpcAuthGss authGss = (RpcAuthGss) call.getCredential();
RpcGssContext cred = _gssSessionManager.getCredential(authGss);
if (cred == null) {
throw new RpcAuthException("No context found",
new RpcAuthError(RpcAuthStat.RPCSEC_GSS_CTXPROBLEM));
}
GSSContext gssContext = cred.getContext();
int _sequence = authGss.getSequence();
switch (authGss.getProc()) {
case GssProc.RPCSEC_GSS_INIT:
case GssProc.RPCSEC_GSS_CONTINUE_INIT:
GSSINITargs gssArgs = new GSSINITargs();
GSSINITres res = new GSSINITres();
call.retrieveCall(gssArgs);
byte[] inToken = gssArgs.getToken();
byte[] outToken = gssContext.acceptSecContext(inToken, 0, inToken.length);
res.setHandle(cred.getHandle());
res.setGssMajor(cred.getContext().isEstablished() ? COMPLETE : CONTINUE_NEEDED);
res.setGssMinor(0);
res.setToken(outToken);
if (gssContext.isEstablished()) {
// FIXME: hard coded number
_sequence = 2;
call.getCredential().getSubject().getPrincipals().
add(new KerberosPrincipal(gssContext.getSrcName().toString()));
_log.log(Level.FINE, "RPCGSS_SEC: {0}", cred.getContext().getSrcName());
res.setSequence(_sequence);
byte[] crc = Ints.toByteArray(_sequence);
crc = gssContext.getMIC(crc, 0, 4, new MessageProp(false));
authGss.setVerifier(new RpcAuthVerifier(authGss.type(), crc));
}
call.reply(res);
break;
case GssProc.RPCSEC_GSS_DESTROY:
cred.getContext().dispose();
break;
case GssProc.RPCSEC_GSS_DATA:
call.getCredential().getSubject().getPrincipals().
add(new KerberosPrincipal(gssContext.getSrcName().toString()));
_log.log(Level.FINE, "RPCGSS_SEC: {0}", cred.getContext().getSrcName());
byte[] crc = Ints.toByteArray(authGss.getSequence());
crc = gssContext.getMIC(crc, 0, 4, new MessageProp(false));
authGss.setVerifier(new RpcAuthVerifier(authGss.type(), crc));
context.setAttribute(RpcProtocolFilter.RPC_CALL,
new RpcGssCall(call, cred.getContext(), new MessageProp(false)));
hasContext = true;
}
} catch (RpcException e) {
call.reject(e.getStatus(), e.getRpcReply());
_log.log(Level.INFO, "GSS mechanism failed {0}", e.getMessage());
} catch (IOException e) {
call.reject(RpcRejectStatus.AUTH_ERROR, new RpcAuthError(RpcAuthStat.RPCSEC_GSS_CTXPROBLEM));
_log.log(Level.INFO, "GSS mechanism failed {0}", e.getMessage());
} catch (OncRpcException e) {
call.reject(RpcRejectStatus.AUTH_ERROR, new RpcAuthError(RpcAuthStat.RPCSEC_GSS_CTXPROBLEM));
_log.log(Level.INFO, "RPC request rejected: {0}", e.getMessage());
} catch (GSSException e) {
call.reject(RpcRejectStatus.AUTH_ERROR, new RpcAuthError(RpcAuthStat.RPCSEC_GSS_CTXPROBLEM));
_log.log(Level.INFO, "GSS mechanism failed {0}", e.getMessage());
}
return hasContext;
}
@Override
public boolean postExecute(Context cntxt) throws IOException {
return true;
}
}
/*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this program (see the file COPYING.LIB for more
* details); if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.dcache.xdr.gss;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import org.dcache.utils.Opaque;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.Oid;
public class GssSessionManager {
private static final Logger _log = Logger.getLogger(GssSessionManager.class.getName());
private final GSSManager gManager = GSSManager.getInstance();
private final GSSCredential _serviceCredential;
public GssSessionManager() throws GSSException {
System.setProperty("javax.security.auth.useSubjectCredsOnly", "false");
Oid krb5Mechanism = new Oid("1.2.840.113554.1.2.2");
_serviceCredential = gManager.createCredential(null,
GSSCredential.INDEFINITE_LIFETIME,
krb5Mechanism, GSSCredential.ACCEPT_ONLY);
}
private final Map<Opaque, RpcGssContext> sessions = new ConcurrentHashMap<Opaque, RpcGssContext>();
public RpcGssContext getCredential(RpcAuthGss gssAuth) throws GSSException {
RpcGssContext cred;
switch (gssAuth.getProc()) {
case GssProc.RPCSEC_GSS_INIT:
_log.fine("RPCSEC_GSS_INIT");
UUID id = UUID.randomUUID();
GSSContext context = gManager.createContext(_serviceCredential);
cred = new RpcGssContext(id.toString().getBytes(), context);
sessions.put(new Opaque(cred.getHandle()), cred);
break;
case GssProc.RPCSEC_GSS_DESTROY:
_log.fine("RPCSEC_GSS_DESTROY");
cred = sessions.remove(new Opaque(gssAuth.getHandle()));