Kerberos for Thrift? - java

I have a simple Thrift based java application I have written. It is really very simple, not much more than a "Hello World" message transport using Thrift in java. I have been told that I need to add Kerberos support to my message. I have done some googling and am surprised that Thrift does not already have Kerberos support in some form (or if it does, I cannot find it). I thought about writing my own wrapper using GSSAPI, but I cannot wrap/unwrap my Thrift message as that screws up the Thrift message format.
Has anyone ever Kerberized Thrift?.. or know how it would be done?
Thanks, in advance.

**So, I guess there is a way to do this via the SASL/GSS APIs. It confuses me as to why I don't see any great examples of this on the internet anywhere. However, I post an example of what I have created in the hopes that it will be a help to others... or that someone can correct my delusion of doing something useful here.
Sample Server code:
TServerSocket serverTransport = new TServerSocket(7911); // new server on port 7911
HelloWorldService.Processor<Iface> processor = new HelloWorldService.Processer<Iface>(new ThriftServerImpl()); // This is my thrift implementation for my server
Map<String, String> saslProperties = new HashMap<String, String>(); // need a map for properties
saslProperties.put(Sasl.QOP, "true");
saslProperties.put(Sasl.QOP, "auth-conf"); // authorization and confidentiality
TSaslServerTransport.Factory saslTransportFactory = new TSaslServerTransport.Factory(); // Creating the server definition
saslTransportFactory.addServerDefinition(
"GSSAPI", // tell SASL to use GSSAPI, which supports Kerberos
"myserviceprincipal", // base kerberos principal name - myprincipal/my.server.com#MY.REALM
"my.server.com", // kerberos principal server - myprincipal/my.server.com#MY.REALM
saslProps, // Properties set, above
new SaslRpcServer.SaslGssCallbackHandler())); // I don't know what this really does... but I stole it from Hadoop and it works.. so there.
Tserver server = new TThreadPoolServer(newTThreadPoolSErver.Args(serverTransport).transportFactory(saslTrasnportFactory).processor(processor));
server.serve(); // Thrift server start
Sample Client Code
TTransport transport = new TSocket("my.server.com", 7911); // client to connect to server and port
saslProperties.put(Sasl.QOP, "true");
saslProperties.put(Sasl.QOP, "auth-conf"); // authorization and confidentiality
TTransport saslTransport = new TSaslTransport(
"GSSAPI", // tell SASL to use GSSAPI, which supports Kerberos
null, // authorizationid - null
"myserviceprincipal", // base kerberos principal name - myprincipal/my.client.com#MY.REALM
"my.server.com", // kerberos principal server - myprincipal/my.server.com#MY.REALM
saslProps, // Properties set, above
null, // callback handler - null
transport); // underlying transport
TProtocol protocol = new TBinaryProtocol(saslTransport); // set up our new Thrift protocol
HelloWorldService.Client client = new HelloWorldService.Client(protocol); // Setup our thrift client
saslTransport.open();
String response = client.hello("Hi There"); // send message
System.out.println("response = " + response);
transport.close();
Other condsiderations:
* I set several java properties on both the client and the server.
- java.security.krb5.realm = MY.REALM // realm name
- java.security.krb5.kdc = my.kdc.com // kdc server
- javax.security.auth.useSubjectCredsOnly = false // Allow JAAS to get the TGT.
- java.security.auth.login.config = /etc/myapp/conf/jaas.conf - required jaas file
- sun.security.krb5.debug = true // helped with diagnosing problems.
* The jaas.conf file specified, above, needs to have two entries (maybe only one per server...). I cannot remember where I gleaned this information from.. but here is my file:
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
keyTab="/etc/myapp/conf/myapp.keytab"
useTicketCache=true
principal="myuserprincipal"
debug=true;
};
com.sun.security.jgss.accept {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
keyTab="/etc/myapp/conf/myapp.keytab"
useTicketCache=false
principal="myserviceprincipal/my.server.com"
debug=true;
};
(back to considerations....)
* Despite having a Sasl.QOP of "auth-conf".. the first(?) message that gets transmitted is not encrypted. Maybe this is just a handshake, or something. The remaining messages appear to be encrypted, but this first one prints an ugly message to the console of "No encryption was performed by peer". It would be nice to not get that message, as it will cause grief down the road (warranted or not).
Anyway, I hope this helps someone... or alternatively can provoke some improvements that will help me. :) Hard to believe I spend 2-3 days doing this, and only a small amount of code came out of it , but I knew neither Kerberos or Thrift very well when I started.
Thanks for reading.

Related

Read email from office365 inbox without using of IMAP and POP3

There is a need to read email from office365 inbox without using of IMAP and POP3 protocol, Because these protocols are disabled in my case and also I can't enable them due to some security issue. Please suggest me if any other way to do the same without these two protocol.
Thanks in advance!
Yes there is another way (several in fact, but I will suggest one). There is an API called EWS (Exchange Web Services). It is pretty straightforward to use. You just need the Credentials of the account.
Here is a small example:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2010_SP2); // This is the latest version of this library
ExchangeCredentials credentials = new WebCredentials("email", "password");
service.setCredentials(credentials);
// this.exchangeService.setWebProxy(new WebProxy("xx.xxx.xxx.xx", 8080)); // If you're behind a proxy
service.setUrl(new URI("https://outlook.office365.com/EWS/Exchange.asmx")); // This is the standard URL
Folder inboxFolder = Folder.bind(service, WellKnownFolderName.Inbox);
FindItemsResults<Item> results = service.findItems(inboxFolder.getId(), new ItemView(10)); // 10 is the number of items to fetch (pagesize)
for (Item result : results)
{
EmailMessage currentEmail = (EmailMessage) result;
System.out.println(currentEmail.getFrom());
// And so on
}
The library is capable of doing a lot more such as reading/booking appointments, sending Emails, and so on.
The library uses SOAP as far as my knowledge goes, so you're safe from POP and IMAP.

Is There an Authentication Token Stored in Active Directory? Single-Sign On and WAFFLE/KERBEROS

I am tasked with creating SSO (single-sign on) for my company's application. I am straight out of college so am still fresh to the majority of the things at play here. I have done tons of research, I don't fully understand all of it but am doing my best.
So the scenario I am in, I have a Windows Java Application(Swing) (NOT A WEB BASED APP), and a LINUX based server. Both have access to the same Active Directory(AD). I need to authenticate who the client is saying they are. I have attempted to use both Kerberos and WAFFLE to no avail. Kerberos has zero useful code examples or information online to even begin to try and use that form of authentication. WAFFLE I have set up but can't use because it requires a Windows server where mine is LINUX. What I am now trying to do is find out if there is some token that is stored in the AD that the server can authenticate against.
What I want to do is send the currently logged in Windows user along with a token to the server and the server to do a look up in the active directory to see if the username and token match. If they do then you are good to log in. Is this possible and does such a thing exist in the AD?
Does a Kerberos token get stored in the AD? If so am I able to access such a token to send to the server to authenticate with? Does an SSPI token get stored there because that is what this WAFFLE code I have working seems to be using but I haven't been able to find how to query such a token in AD.
In the following code WAFFLE does some authentication using SSPI. I don't fully understand how it works but I wanted to see if I could send the token it is using here to the LINUX server to look up in the AD to check if it is valid but it doesn't seem to be something stored in the AD.
Any help is greatly appreciated.
private void negotiate() {
IWindowsSecurityContext clientContext = WindowsSecurityContextImpl.getCurrent( "NTLM", "localhost" );
String securityPackage = "Kerberos";
int count = 0;
// initialize a security context on the client
clientContext = WindowsSecurityContextImpl.getCurrent( securityPackage, clientContext.getPrincipalName() );
// create an auth provider and a security context for the client
// on the server
WindowsAuthProviderImpl provider = new WindowsAuthProviderImpl();
// now you would send the byte[] token to the server and the server will
// response with another byte[] token, which the client needs to answer again
IWindowsSecurityContext serverContext = null;
// Step 1: accept the token on the server and build a security context
// representing the client on the server
byte[] tokenForTheServerOnTheClient = clientContext.getToken();
serverContext = provider.acceptSecurityToken("server-connection", tokenForTheServerOnTheClient, securityPackage);
do {
count++;
// Step 2: If you have already build an initial security context for the client
// on the server, send a token back to the client, which the client needs to
// accept and send back to the server again (a handshake)
if (serverContext != null) {
byte[] tokenForTheClientOnTheServer = serverContext.getToken();
SecBufferDesc continueToken = new SecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenForTheClientOnTheServer);
clientContext.initialize(clientContext.getHandle(), continueToken, clientContext.getPrincipalName());
System.out.println(tokenForTheClientOnTheServer);
}
tokenForTheServerOnTheClient = clientContext.getToken();
serverContext = provider.acceptSecurityToken("server-connection", tokenForTheServerOnTheClient, securityPackage);
} while (clientContext.isContinue() && count < 5);
if(count >= 5) {
System.out.println("Unable to authenticate the user.");
}else {
// at the end of this handshake, we know on the server side who the
// client is, only by exchanging byte[] arrays
System.out.println(serverContext.getIdentity().getFqn());
}
}

How do you use asymmetric keys or certificate authentication in SNMP4J?

I am working on a project that would like to be able to use certificates or keys as a method of authentication for SNMPv3. We are using the java library SNMP4J.
During my research I have found that SNMP uses TLS/DTLS for message encryption and supposedly also for authentication. Source 1 | Source 2 | Source 3
Looking into the little documentation SNMP4J has, I found that it allows the usage of TLS certificates for encrypting traffic. But I am not sure how the authentication is done, if possible, using a public/private key pair. TLS Traffic Encryption Example | SNMP4J Documentation
Any help would be appreciated.
I was able to authenticate using a similar method as described in the example TLS Traffic Encryption Example.
So as one would expect from the example, I can confirm that SNMP4J uses the keystore set in the Java Property javax.net.ssl.keystore, javax.net.ssl.keyStorePassword, javax.net.ssl.trustStore, and javax.net.ssl.trustStorePassword.
Below are the changes I made to the example to make it work.
The alias (or security name in the documentation) needs to be set in the CertifiedTarget constructor so it knows which certificate to use.
CertifiedTarget ct = new CertifiedTarget(new OctetString(alias));
The security level must be set or the SNMP agent will complain and fail authentication.
ct.setSecurityLevel(SecurityLevel.AUTH_PRIV);
The SecurityCallback subject DN must match the server certificate subject EXACTLY the way it wants otherwise it will deny all responses.
securityCallback.addAcceptedSubjectDN("EMAILADDRESS=admin#net-snmp.org, CN=snmpagent, OU=Development, O=Net-SNMP, L=Davis, ST=CA, C=US");
Lastly, you must register the server public certificate alias (Security Name) with the address.
securityCallback.addLocalCertMapping(ct.getAddress(), "snmpagent");
It comes together to look something like this.
// Set java keystore manually
System.setProperty("javax.net.ssl.keyStore", KEYSTORE_DIR);
System.setProperty("javax.net.ssl.keyStorePassword", "changeit");
System.setProperty("javax.net.ssl.trustStore", KEYSTORE_DIR);
System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
// create the TLS transport mapping:
TLSTM transport = new TLSTM();
// set the security callback (only required for command responder,
// but also recommended for command generators) -
// the callback will be configured later:
DefaultTlsTmSecurityCallback securityCallback = new DefaultTlsTmSecurityCallback();
((TLSTM) transport).setSecurityCallback(securityCallback);
MessageDispatcher md = new MessageDispatcherImpl();
// we need MPv3 for TLSTM:
MPv3 mpv3 = new MPv3();
md.addMessageProcessingModel(mpv3);
Snmp snmp = new Snmp(md, transport);
// create and initialize the TransportSecurityModel TSM:
SecurityModels.getInstance().addSecurityModel(new TSM(new OctetString(mpv3.getLocalEngineID()), false));
// do not forget to listen for responses:
snmp.listen();
CertifiedTarget ct = new CertifiedTarget(new OctetString("alias"));
ct.setVersion(SnmpConstants.version3);
ct.setSecurityModel(SecurityModel.SECURITY_MODEL_TSM);
ct.setAddress(GenericAddress.parse(myAddress));
ct.setSecurityLevel(SecurityLevel.AUTH_PRIV);
securityCallback.addAcceptedSubjectDN("EMAILADDRESS=admin#net-snmp.org, CN=snmpagent, OU=Development, O=Net-SNMP, L=Davis, ST=CA, C=US");
securityCallback.addLocalCertMapping(ct.getAddress(), "snmpagentalias");
PDU pdu = new ScopedPDU();
pdu.add(new VariableBinding(new OID(someOid)));
pdu.setType(PDU.GET);
ResponseEvent response = snmp.send(pdu, ct);
You also have to make sure all the certificates are properly configured so that it actually takes them.
As a side-note, in the discovery of this my team and I discovered several bugs in the TLS handling by SNMP4J, mostly in the transport layer. It seems to be a timing issue (race condition maybe?) where it will get the SNMP data but then ignore it. We were able to get around it by setting the CertifiedTarget timeout and retries really high. We will officially report on this when we have more information.

Connecting to ldap using GSSAPI. Wrong service principal

I'm trying to connect to ldap server using SASL. I'm connecting using url ldaps://ldap.example.com but server hostname is host.example.com. ldap.example.com is cname for host.example.com. My program is trying to get service ticket for ldap/ldap.example.com instead of performing reverse dns request and getting ticket for ldap/host.example.com. Everything works fine when I'm using ldap://host.example.com but I prefer to use service CNAME.
There is my code for creating connection factory:
public DefaultConnectionFactory connectionFactory(){
return new DefaultConnectionFactory(connectionConfig());
}
private ConnectionConfig connectionConfig(){
final SaslConfig saslConfig = new SaslConfig();
saslConfig.setMechanism(Mechanism.GSSAPI);
final BindConnectionInitializer connectionInitializer = new BindConnectionInitializer();
connectionInitializer.setBindSaslConfig(saslConfig);
ConnectionConfig connConfig = new ConnectionConfig("ldaps://ldap.example.com");
connConfig.setConnectionInitializer(connectionInitializer);
return connConfig;
}
and jaas.config:
com.sun.security.jgss.initiate {
com.sun.security.auth.module.Krb5LoginModule required
doNotPrompt=true
keyTab="/etc/ldap.keytab"
principal="ldap#EXAMPLE.COM"
storeKey=true
useKeyTab=true
debug=true
;
};
Is there any way to change this behavior?
You should request a new certificate with ldap.example.com as the subject name and with host.example.com as a subject alternative name. The certificate negotiation is handled right before Kerberos.
A couple more suggestions:
All SPNs should be defined in your KDC:
LDAP/ldap.example.com
LDAP/host.example.com
Both of these A records should be set in DNS. Avoid use of CNAMES, while it might be OK at any given time, different browser versions and future updates could cause inconsistent behavior:
ldap.example.com
host.example.com
The principal in jaas.config and the keytab should match. You have:
principal="ldap#EXAMPLE.COM"
I suggest it should be: principal=“ldap/host.example.com“;
Finally, ldap/host.example.com should be defined as the SPN in your keytab. If it is not, it might be OK, as long as you either (1) add it as an additional SPN related in the keytab: How do you add multiple SPNs to the same keytab file for Spnego or Kerberos Configuration? or (2) see Setspn if you are using Active Directory and you application server supports it.
See further reading on GSSAPI.

CXF + wsdl2java + authentication

I created the jar from the WSDL for my client using the wsdl2java command. Now, I need to know how I can authenticate my client in order to complete an operation?
I am using CXF 2.7.16. I created my service using the generated class MyApp_Service, I am struggling with this. Isn't there a simple way to tell my client the credentials it should use to gain access to the web service?
I read about the Spring configuration, however I am unable to figure out if it applies to my case and how if yes. I tried to cast the MyApp_Service class to BindingProvider in order to use the method which consist to put the USERNAME and PASSWORD properties in the context with a value. However, MyApp_Service cannot be cast to BindingProvider.
This is my first web service client application ever. So, any help will be welcomed.
Update 2015-05-28: I tried to define the AuthenticationPolicy but is seems not working. Here is the code:
Client client = JaxWsDynamicClientFactory.newInstance().createClient(wsdlUrl);
ClientImpl clt = (ClientImpl) client;
HTTPConduit cc = (HTTPConduit) clt.getConduit();
org.apache.cxf.configuration.security.ObjectFactory secOF = new org.apache.cxf.configuration.security.ObjectFactory();
AuthorizationPolicy ap = secOF.createAuthorizationPolicy();
ap.setUserName(usagerWS);
ap.setPassword(mdpWS);
ap.setAuthorizationType("Basic");
cc.setAuthorization(ap);
Sniffing with WireShark, the Authorization header is clearly missing in the HTTP request.
What is missing?
Problem solved, here is the solution:
MyApp_Service service = new MyApp_Service(wsdlUrl, new QName(namespace, serviceName));
MyApp port = service.getMyApp();
// Set credentials
Map<String, Object> reqCtxt = ((javax.xml.ws.BindingProvider) port).getRequestContext();
reqCtxt.put(javax.xml.ws.BindingProvider.USERNAME_PROPERTY, username);
reqCtxt.put(javax.xml.ws.BindingProvider.PASSWORD_PROPERTY, password);
No more usage of the dynamic client. Only the classes generated with wsdl2java are used.

Categories

Resources