summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2021-01-09 19:11:57 -0500
committerJune McEnroe <june@causal.agency>2021-01-09 19:11:57 -0500
commite42b3aa08e3706ecb87ca76254fbab51cccf3390 (patch)
treeab0232c0962f1f7ca6649ff1b0767b5c2f2ee120
parentAllow configuring the upper bound of the hash function (diff)
downloadcatgirl-e42b3aa08e3706ecb87ca76254fbab51cccf3390.tar.gz
catgirl-e42b3aa08e3706ecb87ca76254fbab51cccf3390.zip
Add -o and -t options to trust self-signed certificates
-rw-r--r--catgirl.137
-rw-r--r--chat.c9
-rw-r--r--chat.h5
-rw-r--r--irc.c29
4 files changed, 77 insertions, 3 deletions
diff --git a/catgirl.1 b/catgirl.1
index 04216a6..7912831 100644
--- a/catgirl.1
+++ b/catgirl.1
@@ -21,9 +21,11 @@
 .Op Fl j Ar join
 .Op Fl k Ar priv
 .Op Fl n Ar nick
+.Op Fl o Ar chain
 .Op Fl p Ar port
 .Op Fl r Ar real
 .Op Fl s Ar save
+.Op Fl t Ar trust
 .Op Fl u Ar user
 .Op Fl w Ar pass
 .Op Ar config ...
@@ -216,6 +218,12 @@ Set nickname to
 .Ar nick .
 The default nickname is the user's name.
 .
+.It Fl o Ar chain
+Write the server certificate chain to
+.Ar chain
+in PEM format.
+This temporarily disables certificate verification!
+.
 .It Fl p Ar port , Cm port = Ar port
 Connect to
 .Ar port .
@@ -238,6 +246,17 @@ starts with
 or
 .Ql \&. .
 .
+.It Fl t Ar path , Cm trust = Ar path
+Trust the certificate loaded from
+.Ar path .
+Server name verification is disabled.
+The
+.Ar path
+is searched for in the same manner
+as configuration files.
+See
+.Sx Connecting to Servers with Self-signed Certificates .
+.
 .It Fl u Ar user , Cm user = Ar user
 Set username to
 .Ar user .
@@ -287,6 +306,24 @@ sasl-external
 .Ed
 .El
 .
+.Ss Connecting to Servers with Self-signed Certificates
+.Bl -enum
+.It
+Connect to the server
+and write its certificate to a file:
+.Bd -literal -offset indent
+catgirl -h irc.example.org -o ~/.config/catgirl/example.pem
+.Ed
+.It
+Configure
+.Nm
+to trust the certificate:
+.Bd -literal -offset indent
+trust = example.pem
+# or: catgirl -t example.pem
+.Ed
+.El
+.
 .Sh COMMANDS
 Any unique prefix can be used to abbreviate a command.
 For example,
diff --git a/chat.c b/chat.c
index fd2b89a..a9c204d 100644
--- a/chat.c
+++ b/chat.c
@@ -140,6 +140,8 @@ int main(int argc, char *argv[]) {
 	const char *bind = NULL;
 	const char *host = NULL;
 	const char *port = "6697";
+	const char *chain = NULL;
+	const char *trust = NULL;
 	const char *cert = NULL;
 	const char *priv = NULL;
 
@@ -167,9 +169,11 @@ int main(int argc, char *argv[]) {
 		{ .val = 'k', .name = "priv", required_argument },
 		{ .val = 'l', .name = "log", no_argument },
 		{ .val = 'n', .name = "nick", required_argument },
+		{ .val = 'o', .name = "write-chain", required_argument },
 		{ .val = 'p', .name = "port", required_argument },
 		{ .val = 'r', .name = "real", required_argument },
 		{ .val = 's', .name = "save", required_argument },
+		{ .val = 't', .name = "trust", required_argument },
 		{ .val = 'u', .name = "user", required_argument },
 		{ .val = 'v', .name = "debug", no_argument },
 		{ .val = 'w', .name = "pass", required_argument },
@@ -200,9 +204,11 @@ int main(int argc, char *argv[]) {
 			break; case 'k': priv = optarg;
 			break; case 'l': logEnable = true;
 			break; case 'n': nick = optarg;
+			break; case 'o': insecure = true; chain = optarg;
 			break; case 'p': port = optarg;
 			break; case 'r': real = optarg;
 			break; case 's': save = optarg;
+			break; case 't': trust = optarg;
 			break; case 'u': user = optarg;
 			break; case 'v': self.debug = true;
 			break; case 'w': pass = optarg;
@@ -231,7 +237,7 @@ int main(int argc, char *argv[]) {
 	editCompleteAdd();
 	commandCompleteAdd();
 
-	ircConfig(insecure, cert, priv);
+	ircConfig(insecure, trust, cert, priv);
 
 	uiInitEarly();
 	if (save) {
@@ -249,6 +255,7 @@ int main(int argc, char *argv[]) {
 	uiDraw();
 	
 	int irc = ircConnect(bind, host, port);
+	if (chain) ircWriteChain(chain);
 	if (pass) ircFormat("PASS :%s\r\n", pass);
 	if (sasl) ircFormat("CAP REQ :sasl\r\n");
 	ircFormat("CAP LS\r\n");
diff --git a/chat.h b/chat.h
index 8de5d55..34c450f 100644
--- a/chat.h
+++ b/chat.h
@@ -222,8 +222,11 @@ struct Message {
 	char *params[ParamCap];
 };
 
-void ircConfig(bool insecure, const char *cert, const char *priv);
+void ircConfig(
+	bool insecure, const char *trust, const char *cert, const char *priv
+);
 int ircConnect(const char *bind, const char *host, const char *port);
+void ircWriteChain(const char *path);
 void ircRecv(void);
 void ircSend(const char *ptr, size_t len);
 void ircFormat(const char *format, ...)
diff --git a/irc.c b/irc.c
index 5acc69f..cbe1808 100644
--- a/irc.c
+++ b/irc.c
@@ -43,7 +43,9 @@
 
 struct tls *client;
 
-void ircConfig(bool insecure, const char *cert, const char *priv) {
+void ircConfig(
+	bool insecure, const char *trust, const char *cert, const char *priv
+) {
 	struct tls_config *config = tls_config_new();
 	if (!config) errx(EX_SOFTWARE, "tls_config_new");
 
@@ -59,6 +61,15 @@ void ircConfig(bool insecure, const char *cert, const char *priv) {
 		tls_config_insecure_noverifycert(config);
 		tls_config_insecure_noverifyname(config);
 	}
+	if (trust) {
+		tls_config_insecure_noverifyname(config);
+		const char *dirs = NULL;
+		for (const char *path; NULL != (path = configPath(&dirs, trust));) {
+			error = tls_config_set_ca_file(config, path);
+			if (!error) break;
+		}
+		if (error) errx(EX_NOINPUT, "%s: %s", trust, tls_config_error(config));
+	}
 
 	if (cert) {
 		const char *dirs = NULL;
@@ -149,6 +160,22 @@ int ircConnect(const char *bindHost, const char *host, const char *port) {
 	return sock;
 }
 
+void ircWriteChain(const char *path) {
+	FILE *file = fopen(path, "w");
+	if (!file) err(EX_CANTCREAT, "%s", path);
+
+	int n = fprintf(file, "subject= %s\n", tls_peer_cert_subject(client));
+	if (n < 0) err(EX_IOERR, "%s", path);
+
+	size_t len;
+	const byte *pem = tls_peer_cert_chain_pem(client, &len);
+	len = fwrite(pem, len, 1, file);
+	if (!len) err(EX_IOERR, "%s", path);
+
+	int error = fclose(file);
+	if (error) err(EX_IOERR, "%s", path);
+}
+
 enum { MessageCap = 8191 + 512 };
 
 static void debug(const char *pre, const char *line) {