~rcr/rirc

3847d59f9c5984177e90cc84972388f880b5aa4c — Richard Robbins a month ago 3b5b394
add --tls-cert-ca flag
8 files changed, 67 insertions(+), 23 deletions(-)

M README.md
M docs/rirc.1
M src/io.c
M src/io.h
M src/rirc.c
M src/state.c
M test/io.mock.c
M test/state.c
M README.md => README.md +1 -0
@@ 83,6 83,7 @@ Server connection options:
   --ipv6                   Connect to server using only ipv6 addresses
   --tls-disable            Set server TLS disabled
   --tls-verify=MODE        Set server TLS peer certificate verification mode
   --tls-cert-ca=PATH       Set server TLS ca cert file path
```

Commands:

M docs/rirc.1 => docs/rirc.1 +3 -0
@@ 59,6 59,9 @@ Set \fIserver\fP TLS peer certificate verification \fImode\fP:
\(bu \fIoptional\fP - cert is verified, handshake continues on error
\(bu \fIrequired\fP - cert is verified, handshake is aborted on error (default)
.EE
.TP
.BI --tls-cert-ca= path
Set \fIserver\fP TLS ca cert file \fIpath\fP
.SH USAGE
.TS
l .

M src/io.c => src/io.c +17 -2
@@ 105,6 105,7 @@ struct connection
	const void *obj;
	const char *host;
	const char *port;
	const char *tls_cert_ca;
	enum io_state {
		IO_ST_INVALID,
		IO_ST_DXED, /* Socket disconnected, passive */


@@ 164,7 165,12 @@ const char *ca_cert_paths[] = {
};

struct connection*
connection(const void *obj, const char *host, const char *port, uint32_t flags)
connection(
	const void *obj,
	const char *host,
	const char *port,
	const char *tls_cert_ca,
	uint32_t flags)
{
	struct connection *cx;



@@ 175,6 181,7 @@ connection(const void *obj, const char *host, const char *port, uint32_t flags)
	cx->flags = flags;
	cx->host = strdup(host);
	cx->port = strdup(port);
	cx->tls_cert_ca = (tls_cert_ca ? strdup(tls_cert_ca) : NULL);
	cx->st_cur = IO_ST_DXED;
	cx->st_new = IO_ST_INVALID;
	PT_CF(pthread_mutex_init(&(cx->mtx), NULL));


@@ 188,6 195,7 @@ connection_free(struct connection *cx)
	PT_CF(pthread_mutex_destroy(&(cx->mtx)));
	free((void*)cx->host);
	free((void*)cx->port);
	free((void*)cx->tls_cert_ca);
	free(cx);
}



@@ 820,7 828,14 @@ io_tls_establish(struct connection *cx)
		goto err;
	}

	if (ca_cert_path && *ca_cert_path) {
	if (cx->tls_cert_ca) {

		if ((ret = mbedtls_x509_crt_parse_file(&(cx->tls_x509_crt), cx->tls_cert_ca)) < 0) {
			io_error(cx, "  .. Failed to load ca cert: '%s': %s", cx->tls_cert_ca, io_tls_err(ret));
			goto err;
		}

	} else if (ca_cert_path && *ca_cert_path) {

		if ((ret = mbedtls_x509_crt_parse_file(&(cx->tls_x509_crt), ca_cert_path)) < 0) {
			io_error(cx, "  .. Failed to load ca cert: '%s': %s", ca_cert_path, io_tls_err(ret));

M src/io.h => src/io.h +1 -0
@@ 89,6 89,7 @@ struct connection* connection(
	const void*, /* callback object */
	const char*, /* host */
	const char*, /* port */
	const char*, /* tls_cert_ca */
	uint32_t);   /* flags */

void connection_free(struct connection*);

M src/rirc.c => src/rirc.c +22 -11
@@ 80,6 80,7 @@ static const char *const rirc_help =
"\n   --ipv6                   Connect to server using only ipv6 addresses"
"\n   --tls-disable            Set server TLS disabled"
"\n   --tls-verify=MODE        Set server TLS peer certificate verification mode"
"\n   --tls-cert-ca=PATH       Set server TLS ca cert file path"
"\n";

static const char *const rirc_version =


@@ 105,6 106,7 @@ rirc_opt_str(char c)
		case '6': return "--ipv6";
		case 'x': return "--tls-disable";
		case 'y': return "--tls-verify";
		case 'z': return "--tls-cert-ca";
		default:
			fatal("unknown option flag '%c'", c);
	}


@@ 141,6 143,7 @@ rirc_parse_args(int argc, char **argv)
		const char *mode;
		const char *nicks;
		const char *chans;
		const char *tls_cert_ca;
		int ipv;
		int tls;
		int tls_vrfy;


@@ 162,6 165,7 @@ rirc_parse_args(int argc, char **argv)
		{"ipv6",        no_argument,       0, '6'},
		{"tls-disable", no_argument,       0, 'x'},
		{"tls-verify",  required_argument, 0, 'y'},
		{"tls-cert-ca", required_argument, 0, 'z'},
		{0, 0, 0, 0}
	};



@@ 183,17 187,18 @@ rirc_parse_args(int argc, char **argv)
					return -1;
				}

				cli_servers[n_servers - 1].host     = optarg;
				cli_servers[n_servers - 1].port     = NULL;
				cli_servers[n_servers - 1].pass     = NULL;
				cli_servers[n_servers - 1].username = default_username;
				cli_servers[n_servers - 1].realname = default_realname;
				cli_servers[n_servers - 1].mode     = NULL;
				cli_servers[n_servers - 1].nicks    = default_nicks;
				cli_servers[n_servers - 1].chans    = NULL;
				cli_servers[n_servers - 1].ipv      = IO_IPV_UNSPEC;
				cli_servers[n_servers - 1].tls      = IO_TLS_ENABLED;
				cli_servers[n_servers - 1].tls_vrfy = IO_TLS_VRFY_REQUIRED;
				cli_servers[n_servers - 1].host        = optarg;
				cli_servers[n_servers - 1].port        = NULL;
				cli_servers[n_servers - 1].pass        = NULL;
				cli_servers[n_servers - 1].username    = default_username;
				cli_servers[n_servers - 1].realname    = default_realname;
				cli_servers[n_servers - 1].mode        = NULL;
				cli_servers[n_servers - 1].nicks       = default_nicks;
				cli_servers[n_servers - 1].chans       = NULL;
				cli_servers[n_servers - 1].tls_cert_ca = NULL;
				cli_servers[n_servers - 1].ipv         = IO_IPV_UNSPEC;
				cli_servers[n_servers - 1].tls         = IO_TLS_ENABLED;
				cli_servers[n_servers - 1].tls_vrfy    = IO_TLS_VRFY_REQUIRED;
				break;

			#define CHECK_SERVER_OPTARG(OPT_C, REQ) \


@@ 273,6 278,11 @@ rirc_parse_args(int argc, char **argv)
				arg_error("invalid option for '--tls-verify' '%s'", optarg);
				return -1;

			case 'z': /* Set server TLS ca cert file path */
				CHECK_SERVER_OPTARG(opt_c, 1);
				cli_servers[n_servers - 1].tls_cert_ca = optarg;
				break;

			#undef CHECK_SERVER_OPTARG

			case 'h':


@@ 325,6 335,7 @@ rirc_parse_args(int argc, char **argv)
			cli_servers[i].s,
			cli_servers[i].host,
			cli_servers[i].port,
			cli_servers[i].tls_cert_ca,
			flags);

		if (server_list_add(state_server_list(), cli_servers[i].s)) {

M src/state.c => src/state.c +15 -9
@@ 630,14 630,15 @@ command(struct channel *c, char *buf)
			int ret;
			struct server *s;

			const char *host     = str;
			const char *port     = NULL;
			const char *pass     = NULL;
			const char *username = default_username;
			const char *realname = default_realname;
			const char *mode     = NULL;
			const char *nicks    = default_nicks;
			const char *chans    = NULL;
			const char *host        = str;
			const char *port        = NULL;
			const char *pass        = NULL;
			const char *username    = default_username;
			const char *realname    = default_realname;
			const char *mode        = NULL;
			const char *nicks       = default_nicks;
			const char *chans       = NULL;
			const char *tls_cert_ca = NULL;
			int ipv      = IO_IPV_UNSPEC;
			int tls      = IO_TLS_ENABLED;
			int tls_vrfy = IO_TLS_VRFY_REQUIRED;


@@ 702,6 703,11 @@ command(struct channel *c, char *buf)
						action(action_error, "connect: invalid option for '--tls-verify' '%s'", str);
						return;
					}
				} else if (!strcmp(str, "--tls-cert-ca")) {
					if (!(tls_cert_ca = irc_strsep(&buf))) {
						action(action_error, "connect: '--tls-cert-ca' requires an argument");
						return;
					}
				} else {
					action(action_error, "connect: unknown option '%s'", str);
					return;


@@ 731,7 737,7 @@ command(struct channel *c, char *buf)
				return;
			}

			s->connection = connection(s, host, port, (ipv | tls | tls_vrfy));
			s->connection = connection(s, host, port, tls_cert_ca, (ipv | tls | tls_vrfy));

			if ((ret = io_cx(s->connection)))
				server_error(s, "failed to connect: %s", io_err(ret));

M test/io.mock.c => test/io.mock.c +2 -1
@@ 37,11 37,12 @@ io_sendf(struct connection *c, const char *fmt, ...)
}

struct connection*
connection(const void *o, const char *h, const char *p, uint32_t f)
connection(const void *o, const char *h, const char *p, const char *ca, uint32_t f)
{
	UNUSED(o);
	UNUSED(h);
	UNUSED(p);
	UNUSED(ca);
	UNUSED(f);
	return NULL;
}

M test/state.c => test/state.c +6 -0
@@ 246,6 246,11 @@ test_command_connect(void)
	assert_strcmp(current_channel()->name, "host-1");
	INP_C(0x0A);

	INP_COMMAND(":connect host --tls-cert-ca");
	assert_strcmp(action_message(), "connect: '--tls-cert-ca' requires an argument");
	assert_strcmp(current_channel()->name, "host-1");
	INP_C(0x0A);

	/* Test invalid arguments */
	INP_COMMAND(":connect host xyz");
	assert_strcmp(action_message(), ":connect [hostname [options]]");


@@ 310,6 315,7 @@ test_command_connect(void)
		" --tls-verify disabled"
		" --tls-verify optional"
		" --tls-verify required"
		" --tls-cert-ca path"
	);

	s = current_channel()->server;