--- /dev/null
+package dolda.dolcon;
+
+public class AuthException extends Exception {
+ public AuthException(String msg) {
+ super(msg);
+ }
+
+ public AuthException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
+}
--- /dev/null
+package dolda.dolcon;
+
+import dolda.dolcon.protocol.Command;
+import dolda.dolcon.protocol.Response;
+import java.util.List;
+
+public interface Authenticator {
+ public String handles(List<String> name);
+ public Command step(Response resp) throws AuthException, ProtocolException, InterruptedException;
+}
--- /dev/null
+package dolda.dolcon;
+
+import java.util.List;
+import dolda.dolcon.protocol.Response;
+import dolda.dolcon.protocol.Command;
+
+public abstract class InteractiveAuth implements Authenticator {
+ public String handles(List<String> name) {
+ if(name.contains("pam"))
+ return("pam");
+ return(null);
+ }
+
+ public Command step(Response resp) throws AuthException, ProtocolException, InterruptedException {
+ if(resp.code == 301) {
+ return(new Command("pass", promptnoecho(resp.token(0, 0))));
+ } else if(resp.code == 302) {
+ return(new Command("pass", promptecho(resp.token(0, 0))));
+ } else if(resp.code == 303) {
+ info(resp.token(0, 0));
+ return(new Command("pass", ""));
+ } else if(resp.code == 304) {
+ error(resp.token(0, 0));
+ return(new Command("pass", ""));
+ } else {
+ throw(new ResponseException(resp, 0));
+ }
+ }
+
+ public abstract String promptecho(String msg) throws AuthException;
+ public abstract String promptnoecho(String msg) throws AuthException;
+ public abstract void info(String msg) throws AuthException;
+ public abstract void error(String msg) throws AuthException;
+}
--- /dev/null
+package dolda.dolcon;
+
+public class NoMechException extends AuthException {
+ public NoMechException() {
+ super("No supported authentication mechanism was offered by the server");
+ }
+}
--- /dev/null
+package dolda.dolcon;
+
+import java.util.List;
+import dolda.dolcon.protocol.Response;
+import dolda.dolcon.protocol.Command;
+
+public class PasswordAuth implements Authenticator {
+ private String password;
+
+ public PasswordAuth(String password) {
+ this.password = password;
+ }
+
+ public String handles(List<String> name) {
+ System.out.println(name);
+ if(name.contains("pam"))
+ return("pam");
+ return(null);
+ }
+
+ public Command step(Response resp) throws ProtocolException {
+ if((password != null) && (resp.code == 301)) {
+ try {
+ return(new Command("pass", password));
+ } finally {
+ password = null;
+ }
+ } else {
+ throw(new ResponseException(resp, 0));
+ }
+ }
+}
--- /dev/null
+package dolda.dolcon;
+
+/**
+ * The purpose of this exception is to wrap together all the low-level
+ * protocol exceptions, that the programmer is unlikely to want to
+ * differentiate between.
+ */
+public class ProtocolException extends Exception {
+ public ProtocolException(String msg) {
+ super(msg);
+ }
+
+ public ProtocolException(Exception cause) {
+ super("Unhandled DC protocol condition", cause);
+ }
+}
--- /dev/null
+package dolda.dolcon;
+
+import dolda.dolcon.protocol.Response;
+
+public class ResponseException extends ProtocolException {
+ Response resp;
+ int expected;
+
+ public ResponseException(Response resp, int expected) {
+ super("Unhandled DC protocol response (" + resp.code + " != " + expected + ")");
+ this.resp = resp;
+ this.expected = expected;
+ }
+
+ public ResponseException(String msg, Response resp, int expected) {
+ super(msg);
+ this.resp = resp;
+ this.expected = expected;
+ }
+
+ public static Response check(Response resp, int expect) throws ResponseException {
+ if(resp.code != expect)
+ throw(new ResponseException(resp, expect));
+ return(resp);
+ }
+}
--- /dev/null
+package dolda.dolcon;
+
+import java.util.*;
+import dolda.dolcon.protocol.*;
+
+public class Session {
+ private Connection conn;
+
+ public Session(String aspec, String username, List<Authenticator> auth) throws AuthException, ProtocolException, InterruptedException {
+ conn = new Connection(aspec);
+ conn.expectVersion(2);
+ try {
+ conn.syncConnect();
+ } catch(ConnectException e) {
+ throw(new ProtocolException(e));
+ }
+ authenticate(username, auth);
+ }
+
+ public Session(String aspec, String username, Authenticator... auth) throws AuthException, ProtocolException, InterruptedException {
+ this(aspec, username, Arrays.asList(auth));
+ }
+
+ private void authenticate(String username, List<Authenticator> auth) throws AuthException, ProtocolException, InterruptedException {
+ Response resp;
+
+ try {
+ resp = ResponseException.check(conn.ecmd("lsauth"), 200);
+ List<String> mechs = new LinkedList<String>();
+ for(List<String> mech : resp.lines)
+ mechs.add(mech.get(0).intern());
+ String use = null;
+ Authenticator au = null;
+ for(Authenticator a : auth) {
+ System.out.println(a);
+ use = a.handles(mechs);
+ if(use != null) {
+ au = a;
+ break;
+ }
+ }
+ if(use == null)
+ throw(new NoMechException());
+ resp = conn.ecmd("login", use, username);
+ while(true) {
+ if(resp.code == 200) {
+ return;
+ } else if((resp.code / 100) == 3) {
+ resp = conn.ecmd(au.step(resp));
+ } else if((resp.code / 100) == 5) {
+ throw(new AuthException(resp.token(0, 0)));
+ } else {
+ throw(new ResponseException(resp, 0));
+ }
+ }
+ } catch(ClosedException e) {
+ throw(new ProtocolException(e));
+ }
+ }
+}
code = Integer.parseInt(ct.toString());
ct.setLength(0);
state = "start";
- continue eat;
} else {
ct.append(c);
}
import java.util.*;
public class Response {
- List<List<String>> lines;
- Command cmd;
- int code;
+ public List<List<String>> lines;
+ public Command cmd;
+ public int code;
public Response(int code, List<List<String>> lines) {
this.code = code;
public String token(int line, int token) {
return(lines.get(line).get(token));
}
+
+ public List<String> line(int line) {
+ return(lines.get(line));
+ }
}