Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added curve-authentication #445

Merged
merged 2 commits into from Jan 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Expand Up @@ -31,6 +31,7 @@ Steven McCoy
Tamara Kustarova
Tero Marttila
Terry Wilson
Thomas Trocha
Trevor Bernard
Vitaly Mayatskikh

Expand Down
76 changes: 73 additions & 3 deletions jzmq-core/src/main/java/org/zeromq/ZAuth.java
Expand Up @@ -5,6 +5,7 @@
import java.io.FileReader;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.zeromq.ZMQ.PollItem;
import org.zeromq.ZMQ.Poller;
import org.zeromq.ZMQ.Socket;
Expand All @@ -22,7 +23,8 @@
* @author cbusbey (at) connamara (dot) com
*/
public class ZAuth {

public static final String CURVE_ALLOW_ANY = "*";

private Socket pipe; //pipe to backend agent
private boolean verbose;

Expand Down Expand Up @@ -67,7 +69,9 @@ static ZAPRequest recvRequest(Socket handler) {
self.username = request.popString();
self.password = request.popString();
} else if (self.mechanism.equals("CURVE")) {
// TODO: Handle CURVE authentication
ZFrame frame = request.pop();
byte[] clientPublicKey = frame.getData();
self.clientKey = ZMQ.Curve.z85Encode(clientPublicKey);
} else if (self.mechanism.equals("GSSAPI")) {
self.principal = request.popString();
}
Expand Down Expand Up @@ -118,6 +122,10 @@ private static class ZAuthAgent implements IAttachedRunnable {
private File passwords_file;
private long passwords_modified;
final private ZAuth auth; //our parent auth, used for authorization callbacks
private boolean allow_any;
private ZCertStore certStore = null;



private ZAuthAgent(ZAuth auth) {
this.auth = auth;
Expand All @@ -132,14 +140,23 @@ private boolean controlMessage() {
ZMsg msg = ZMsg.recvMsg(pipe);

String command = msg.popString();
if (verbose) {
System.out.printf("ZAuth: API command=%s\n",command);
}
if (command == null) {
return false; //interrupted
}
if (command.equals("ALLOW")) {
String address = msg.popString();
if (verbose) {
System.out.printf("ZAuth: - whitelisting ipaddress=%s\n", address);
}
whitelist.put(address, "OK");
} else if (command.equals("DENY")) {
String address = msg.popString();
if (verbose) {
System.out.printf("ZAuth: - blacklisting ipaddress=%s\n", address);
}
blacklist.put(address, "OK");
} else if (command.equals("PLAIN")) {
// For now we don't do anything with domains
Expand All @@ -154,6 +171,16 @@ private boolean controlMessage() {
reply.add("OK");
reply.send(pipe);
reply.destroy();
} else if (command.equals("CURVE")) {
// If location is CURVE_ALLOW_ANY, allow all clients. Otherwise
// treat location as a directory that holds the certificates.
String location = msg.popString();
if (location.equals(CURVE_ALLOW_ANY)){
allow_any=true;
} else {
this.certStore = new ZCertStore(location);
this.allow_any = false;
}
} else if (command.equals("GSSAPI")) {
//for now, we don't do anything with domains
String domain = msg.popString();
Expand Down Expand Up @@ -222,7 +249,7 @@ private boolean authenticate() {
allowed = authenticatePlain(request);
} else if (request.mechanism.equals("CURVE")) {
// For CURVE, even a whitelisted address must authenticate
// TODO: Handle CURVE authentication
allowed = authenticateCurve(request);
} else if (request.mechanism.equals("GSSAPI")) {
// At this point, the request is authenticated, send to
//zauth callback for complete authorization
Expand Down Expand Up @@ -262,6 +289,33 @@ private boolean authenticatePlain(ZAPRequest request) {
return false;
}
}

private boolean authenticateCurve(ZAPRequest request) {
if (this.allow_any) {
if (this.verbose) {
System.out.println("zauth: - allowed (CURVE allow any client)");
}
return true;
} else {
if (this.certStore!=null) {
this.certStore.checkAndReload();
if (this.certStore.containsPublicKey(request.clientKey)) {
// login allowed
if (verbose) {
System.out.printf("zauth: - allowed (CURVE) client_key=%s\n",request.clientKey);
}
return true;
} else {
// login not allowed. couldn't find certificate
if (verbose) {
System.out.printf("zauth: - denied (CURVE) client_key=%s\n",request.clientKey);
}
return false;
}
}
}
return false;
}

@Override
public void run(Object[] args, ZContext ctx, Socket pipe) {
Expand Down Expand Up @@ -415,6 +469,22 @@ public void configurePlain(String domain, String filename) {
msg.destroy();
}

/**
* Configure CURVE authentication
*
* @param location Can be ZAuth.CURVE_ALLOW_ANY or a directory with public-keys that will be accepted
*/
public void configureCurve(String location) {
assert (location != null);

ZMsg msg = new ZMsg();
msg.add("CURVE");
msg.add(location);
msg.send(pipe);
msg.destroy();
}


/**
* Destructor.
*/
Expand Down
94 changes: 94 additions & 0 deletions jzmq-core/src/main/java/org/zeromq/ZCert.java
@@ -0,0 +1,94 @@
package org.zeromq;

import java.util.HashMap;
import java.util.Map;

/**
* Minimal ZCert-class to handle public and secret keys
*
* @author thomas (dot) trocha (at) gmail (dot) com
*
*/
public class ZCert {
private byte[] public_key;// Public key in binary
private byte[] secret_key;// Secret key in binary
private String public_txt;// Public key in Z85 text
private String secret_txt;// Secret key in Z85 text
private Map<String,String> metadata=new HashMap<String, String>(); // Certificate metadata

public ZCert(String publicKey) {
if (publicKey.length()==32) { // in binary-format
public_key = publicKey.getBytes();
public_txt = ZMQ.Curve.z85Encode(public_key);
} else { // Z85-Coded
public_key = ZMQ.Curve.z85Decode(publicKey);
public_txt = publicKey;
}
}

public ZCert() {
ZMQ.Curve.KeyPair keypair = ZMQ.Curve.generateKeyPair();
public_key = ZMQ.Curve.z85Decode(keypair.publicKey);
public_txt = keypair.publicKey;
secret_key = ZMQ.Curve.z85Decode(keypair.secretKey);
secret_txt = keypair.secretKey;
}



public byte[] getPublicKey() {
return public_key;
}

public byte[] getSecretKey() {
return secret_key;
}

public String getPublicKeyAsZ85() {
return public_txt;
}


public String getSecretKeyAsZ85() {
return secret_txt;
}

public void setMeta(String key,String value) {
metadata.put(key, value);
}

private void metaToZConfig(Map<String,String> meta,ZConfig zconf) {
for (String key : meta.keySet()) {
zconf.putValue("metadata/"+key, meta.get(key));
}
}

/**
* Save the public-key to disk
* @param filename
*/
public void savePublic(String filename) {
ZConfig zconf = new ZConfig("root",null);
metaToZConfig(metadata, zconf);
zconf.addComment(" ZeroMQ CURVE Public Certificate");
zconf.addComment(" Exchange securely, or use a secure mechanism to verify the contents");
zconf.addComment(" of this file after exchange. Store public certificates in your home");
zconf.addComment(" directory, in the .curve subdirectory.");
zconf.putValue("/curve/public-key", public_txt);
zconf.save(filename);
}

/**
* save the public- and secret-key to disk
* @param filename
*/
public void saveSecret(String filename) {
ZConfig zconf = new ZConfig("root",null);
metaToZConfig(metadata, zconf);
zconf.addComment(" ZeroMQ CURVE **Secret** Certificate");
zconf.addComment(" DO NOT PROVIDE THIS FILE TO OTHER USERS nor change its permissions.");
zconf.putValue("/curve/public-key", public_txt);
zconf.putValue("/curve/secret-key", secret_txt);
zconf.save(filename);
}
}