summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--bounce.c15
-rw-r--r--bounce.h2
-rw-r--r--pounce.111
-rw-r--r--state.c39
4 files changed, 40 insertions, 27 deletions
diff --git a/bounce.c b/bounce.c
index 0e7e421..bb7e59c 100644
--- a/bounce.c
+++ b/bounce.c
@@ -195,7 +195,8 @@ int main(int argc, char *argv[]) {
 	const char *host = NULL;
 	const char *port = "6697";
 	char *pass = NULL;
-	char *auth = NULL;
+	bool sasl = false;
+	char *plain = NULL;
 	const char *nick = NULL;
 	const char *user = NULL;
 	const char *real = NULL;
@@ -203,7 +204,7 @@ int main(int argc, char *argv[]) {
 	const char *away = "pounced :3";
 	const char *quit = "connection reset by purr";
 
-	const char *Opts = "!A:C:H:K:NP:Q:U:W:a:c:f:h:j:k:n:p:r:s:u:vw:x";
+	const char *Opts = "!A:C:H:K:NP:Q:U:W:a:c:ef:h:j:k:n:p:r:s:u:vw:x";
 	const struct option LongOpts[] = {
 		{ "insecure", no_argument, NULL, '!' },
 		{ "away", required_argument, NULL, 'A' },
@@ -215,8 +216,9 @@ int main(int argc, char *argv[]) {
 		{ "quit", required_argument, NULL, 'Q' },
 		{ "bind-path", required_argument, NULL, 'U' },
 		{ "client-pass", required_argument, NULL, 'W' },
-		{ "sasl", required_argument, NULL, 'a' },
+		{ "sasl-plain", required_argument, NULL, 'a' },
 		{ "client-cert", required_argument, NULL, 'c' },
+		{ "sasl-external", no_argument, NULL, 'e' },
 		{ "save", required_argument, NULL, 'f' },
 		{ "host", required_argument, NULL, 'h' },
 		{ "join", required_argument, NULL, 'j' },
@@ -244,8 +246,9 @@ int main(int argc, char *argv[]) {
 			break; case 'Q': quit = optarg;
 			break; case 'U': strlcpy(bindPath, optarg, sizeof(bindPath));
 			break; case 'W': clientPass = optarg;
-			break; case 'a': auth = optarg;
+			break; case 'a': sasl = true; plain = optarg;
 			break; case 'c': clientCert = optarg;
+			break; case 'e': sasl = true;
 			break; case 'f': save = optarg;
 			break; case 'h': host = optarg;
 			break; case 'j': join = optarg;
@@ -333,9 +336,9 @@ int main(int argc, char *argv[]) {
 	if (error) err(EX_OSERR, "cap_rights_limit");
 #endif
 
-	stateLogin(pass, auth, nick, user, real);
+	stateLogin(pass, sasl, plain, nick, user, real);
 	if (pass) explicit_bzero(pass, strlen(pass));
-	if (auth) explicit_bzero(auth, strlen(auth));
+	if (plain) explicit_bzero(plain, strlen(plain));
 
 	while (!stateReady()) serverRecv();
 	serverFormat("AWAY :%s\r\n", away);
diff --git a/bounce.h b/bounce.h
index 33c1fee..42b8ed5 100644
--- a/bounce.h
+++ b/bounce.h
@@ -99,7 +99,7 @@ void clientConsume(struct Client *client);
 
 bool stateJoinNames;
 void stateLogin(
-	const char *pass, const char *auth,
+	const char *pass, bool sasl, const char *plain,
 	const char *nick, const char *user, const char *real
 );
 bool stateReady(void);
diff --git a/pounce.1 b/pounce.1
index 213575b..ee4451b 100644
--- a/pounce.1
+++ b/pounce.1
@@ -1,4 +1,4 @@
-.Dd November 4, 2019
+.Dd November 5, 2019
 .Dt POUNCE 1
 .Os
 .
@@ -8,7 +8,7 @@
 .
 .Sh SYNOPSIS
 .Nm
-.Op Fl Nv
+.Op Fl Nev
 .Op Fl A Ar mesg
 .Op Fl C Ar path
 .Op Fl H Ar host
@@ -133,7 +133,7 @@ string must be hashed using the
 .Fl x
 flag.
 .
-.It Fl a Ar user : Ns Ar pass , Cm sasl = Ar user : Ns Ar pass
+.It Fl a Ar user : Ns Ar pass , Cm sasl-plain = Ar user : Ns Ar pass
 Authenticate as
 .Ar user
 with
@@ -147,6 +147,11 @@ If the certificate key is in a separate file,
 set it with
 .Fl k .
 .
+.It Fl e , Cm sasl-external
+Authenticate using SASL EXTERNAL.
+Set the client TLS client certificate path with
+.Fl c .
+.
 .It Fl f Ar path , Cm save = Ar path
 Load the contents of the buffer from
 .Ar path ,
diff --git a/state.c b/state.c
index a0336dc..ba6f8d6 100644
--- a/state.c
+++ b/state.c
@@ -40,21 +40,23 @@ static void require(const struct Message *msg, bool origin, size_t len) {
 static char *plainBase64;
 
 void stateLogin(
-	const char *pass, const char *auth,
+	const char *pass, bool sasl, const char *plain,
 	const char *nick, const char *user, const char *real
 ) {
-	if (auth) {
-		byte plain[1 + strlen(auth)];
-		plain[0] = 0;
-		for (size_t i = 0; auth[i]; ++i) {
-			plain[1 + i] = (auth[i] == ':' ? 0 : auth[i]);
-		}
-		plainBase64 = malloc(BASE64_SIZE(sizeof(plain)));
-		if (!plainBase64) err(EX_OSERR, "malloc");
-		base64(plainBase64, plain, sizeof(plain));
+	if (pass) serverFormat("PASS :%s\r\n", pass);
+	if (sasl) {
 		serverFormat("CAP REQ :sasl\r\n");
+		if (plain) {
+			byte buf[1 + strlen(plain)];
+			buf[0] = 0;
+			for (size_t i = 0; plain[i]; ++i) {
+				buf[1 + i] = (plain[i] == ':' ? 0 : plain[i]);
+			}
+			plainBase64 = malloc(BASE64_SIZE(sizeof(buf)));
+			if (!plainBase64) err(EX_OSERR, "malloc");
+			base64(plainBase64, buf, sizeof(buf));
+		}
 	}
-	if (pass) serverFormat("PASS :%s\r\n", pass);
 	serverFormat("NICK %s\r\n", nick);
 	serverFormat("USER %s 0 * :%s\r\n", user, real);
 }
@@ -64,16 +66,19 @@ static void handleCap(struct Message *msg) {
 	if (strcmp(msg->params[1], "ACK") || strncmp(msg->params[2], "sasl", 4)) {
 		errx(EX_CONFIG, "server does not support SASL");
 	}
-	serverFormat("AUTHENTICATE PLAIN\r\n");
+	serverFormat("AUTHENTICATE %s\r\n", (plainBase64 ? "PLAIN" : "EXTERNAL"));
 }
 
 static void handleAuthenticate(struct Message *msg) {
 	(void)msg;
-	if (!plainBase64) errx(EX_PROTOCOL, "unsolicited AUTHENTICATE");
-	serverFormat("AUTHENTICATE %s\r\n", plainBase64);
-	explicit_bzero(plainBase64, strlen(plainBase64));
-	free(plainBase64);
-	plainBase64 = NULL;
+	if (plainBase64) {
+		serverFormat("AUTHENTICATE %s\r\n", plainBase64);
+		explicit_bzero(plainBase64, strlen(plainBase64));
+		free(plainBase64);
+		plainBase64 = NULL;
+	} else {
+		serverFormat("AUTHENTICATE +\r\n");
+	}
 }
 
 static void handleReplyLoggedIn(struct Message *msg) {