mirror of
https://github.com/irssi/irssi.git
synced 2024-09-29 04:45:57 -04:00
Add x509 certificate and public key pinning support.
This patch adds two new options to /CONNECT and /SERVER to let the user pin either an x509 certificate and/or the public key of a given server. It is possible to fetch the certificate outside of Irssi itself to verify the checksum. To fetch the certificate call: $ openssl s_client -connect chat.freenode.net:6697 < /dev/null 2>/dev/null | \ openssl x509 > freenode.cert This will download chat.freenode.net:6697's TLS certificate and put it into the file freenode.cert. -tls_pinned_cert ---------------- This option allows you to specify the SHA-256 hash of the x509 certificate. When succesfully connected to the server, irssi will verify that the given server certificate matches the pin set by the user. The SHA-256 hash of a given certificate can be verified outside of irssi using the OpenSSL command line tool: $ openssl x509 -in freenode.cert -fingerprint -sha256 -noout -tls_pinned_pubkey ------------------ This option allows you to specify the SHA-256 hash of the subject public key information section of the server certificate. This section contains both the cryptographic parameters for the public key, but also information about the algorithm used together with the public key parameters. When succesfully connected to the server, irssi will verify that the given public key matches the pin set by the user. The SHA-256 hash of a public key can be verified outside of irssi using the OpenSSL command line tool: $ openssl x509 -in freenode.cert -pubkey -noout | \ openssl pkey -pubin -outform der | \ openssl dgst -sha256 -c | \ tr a-z A-Z It is possible to specify both -tls_pinned_cert and -tls_pinned_pubkey together.
This commit is contained in:
parent
c6c2e79537
commit
5146ce9631
21
NEWS
21
NEWS
@ -25,6 +25,27 @@ v0.8.21-head 2016-xx-xx The Irssi team <staff@irssi.org>
|
|||||||
configuration.
|
configuration.
|
||||||
+ Display TLS connection information upon connect. You can disable this by
|
+ Display TLS connection information upon connect. You can disable this by
|
||||||
setting tls_verbose_connect to FALSE.
|
setting tls_verbose_connect to FALSE.
|
||||||
|
+ Add -tls_pinned_cert and -tls_pinned_pubkey for x509 and public key pinning.
|
||||||
|
|
||||||
|
The values needed for -tls_pinned_cert and -tls_pinned_pubkey is shown
|
||||||
|
when connecting to a TLS enabled IRC server, but you can also find the
|
||||||
|
values like this: Start by downloading the certificate from a given IRC
|
||||||
|
server:
|
||||||
|
|
||||||
|
$ openssl s_client -connect chat.freenode.net:6697 < /dev/null 2>/dev/null | \
|
||||||
|
openssl x509 > freenode.cert
|
||||||
|
|
||||||
|
Find the value for -tls_pinned_cert:
|
||||||
|
|
||||||
|
$ openssl x509 -in freenode.cert -fingerprint -sha256 -noout
|
||||||
|
|
||||||
|
Find the value for -tls_pinned_pubkey:
|
||||||
|
|
||||||
|
$ openssl x509 -in freenode.cert -pubkey -noout | \
|
||||||
|
openssl pkey -pubin -outform der | \
|
||||||
|
openssl dgst -sha256 -c | \
|
||||||
|
tr a-z A-Z
|
||||||
|
|
||||||
- IP addresses are no longer stored when resolve_reverse_lookup is
|
- IP addresses are no longer stored when resolve_reverse_lookup is
|
||||||
used.
|
used.
|
||||||
- /names and $[...] now uses utf8 string operations (#40, #411).
|
- /names and $[...] now uses utf8 string operations (#40, #411).
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
-tls_cafile: The file with the list of CA certificates.
|
-tls_cafile: The file with the list of CA certificates.
|
||||||
-tls_capath: The directory which contains the CA certificates.
|
-tls_capath: The directory which contains the CA certificates.
|
||||||
-tls_ciphers: TLS cipher suite preference lists.
|
-tls_ciphers: TLS cipher suite preference lists.
|
||||||
|
-tls_pinned_cert: Pinned x509 certificate fingerprint.
|
||||||
|
-tls_pinned_pubkey: Pinned public key fingerprint.
|
||||||
-noproxy: Ignores the global proxy configuration.
|
-noproxy: Ignores the global proxy configuration.
|
||||||
-network: The network this connection belongs to.
|
-network: The network this connection belongs to.
|
||||||
-host: The hostname you would like to connect from.
|
-host: The hostname you would like to connect from.
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
-tls_cafile: The file with the list of CA certificates.
|
-tls_cafile: The file with the list of CA certificates.
|
||||||
-tls_capath: The directory which contains the CA certificates.
|
-tls_capath: The directory which contains the CA certificates.
|
||||||
-tls_ciphers: TLS cipher suite preference lists.
|
-tls_ciphers: TLS cipher suite preference lists.
|
||||||
|
-tls_pinned_cert: Pinned x509 certificate fingerprint.
|
||||||
|
-tls_pinned_pubkey: Pinned public key fingerprint.
|
||||||
-auto: Automatically connects to the server on startup.
|
-auto: Automatically connects to the server on startup.
|
||||||
-noauto: Doesn't connect to the server on startup.
|
-noauto: Doesn't connect to the server on startup.
|
||||||
-network: The network the server belongs to.
|
-network: The network the server belongs to.
|
||||||
|
@ -115,6 +115,10 @@ static SERVER_CONNECT_REC *get_server_connect(const char *data, int *plus_addr,
|
|||||||
conn->tls_capath = g_strdup(tmp);
|
conn->tls_capath = g_strdup(tmp);
|
||||||
if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
|
if ((tmp = g_hash_table_lookup(optlist, "tls_ciphers")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_ciphers")) != NULL)
|
||||||
conn->tls_ciphers = g_strdup(tmp);
|
conn->tls_ciphers = g_strdup(tmp);
|
||||||
|
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_cert")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_cert")) != NULL)
|
||||||
|
conn->tls_pinned_cert = g_strdup(tmp);
|
||||||
|
if ((tmp = g_hash_table_lookup(optlist, "tls_pinned_pubkey")) != NULL || (tmp = g_hash_table_lookup(optlist, "ssl_pinned_pubkey")) != NULL)
|
||||||
|
conn->tls_pinned_pubkey = g_strdup(tmp);
|
||||||
if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0')
|
if ((conn->tls_capath != NULL && conn->tls_capath[0] != '\0')
|
||||||
|| (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
|
|| (conn->tls_cafile != NULL && conn->tls_cafile[0] != '\0'))
|
||||||
conn->tls_verify = TRUE;
|
conn->tls_verify = TRUE;
|
||||||
@ -494,7 +498,7 @@ void chat_commands_init(void)
|
|||||||
signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
|
signal_add("default command server", (SIGNAL_FUNC) sig_default_command_server);
|
||||||
signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
|
signal_add("server sendmsg", (SIGNAL_FUNC) sig_server_sendmsg);
|
||||||
|
|
||||||
command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +host noproxy -rawlog noautosendcmd");
|
command_set_options("connect", "4 6 !! -network ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_pinned_cert +ssl_pinned_pubkey tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey +host noproxy -rawlog noautosendcmd");
|
||||||
command_set_options("msg", "channel nick");
|
command_set_options("msg", "channel nick");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,6 +753,8 @@ int irssi_ssl_handshake(GIOChannel *handle)
|
|||||||
unsigned int pubkey_fingerprint_size;
|
unsigned int pubkey_fingerprint_size;
|
||||||
unsigned char cert_fingerprint[EVP_MAX_MD_SIZE];
|
unsigned char cert_fingerprint[EVP_MAX_MD_SIZE];
|
||||||
unsigned int cert_fingerprint_size;
|
unsigned int cert_fingerprint_size;
|
||||||
|
const char *pinned_cert_fingerprint = chan->server->connrec->tls_pinned_cert;
|
||||||
|
const char *pinned_pubkey_fingerprint = chan->server->connrec->tls_pinned_pubkey;
|
||||||
TLS_REC *tls = NULL;
|
TLS_REC *tls = NULL;
|
||||||
|
|
||||||
ERR_clear_error();
|
ERR_clear_error();
|
||||||
@ -814,6 +816,24 @@ int irssi_ssl_handshake(GIOChannel *handle)
|
|||||||
ret = 1;
|
ret = 1;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
if (pinned_cert_fingerprint != NULL && pinned_cert_fingerprint[0] != '\0') {
|
||||||
|
ret = g_ascii_strcasecmp(pinned_cert_fingerprint, tls->certificate_fingerprint) == 0;
|
||||||
|
|
||||||
|
if (! ret) {
|
||||||
|
g_warning(" Pinned certificate mismatch");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pinned_pubkey_fingerprint != NULL && pinned_pubkey_fingerprint[0] != '\0') {
|
||||||
|
ret = g_ascii_strcasecmp(pinned_pubkey_fingerprint, tls->public_key_fingerprint) == 0;
|
||||||
|
|
||||||
|
if (! ret) {
|
||||||
|
g_warning(" Pinned public key mismatch");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (chan->verify) {
|
if (chan->verify) {
|
||||||
ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls);
|
ret = irssi_ssl_verify(chan->ssl, chan->ctx, chan->server->connrec->address, chan->port, cert, chan->server, tls);
|
||||||
|
|
||||||
|
@ -29,6 +29,8 @@ char *tls_pass;
|
|||||||
char *tls_cafile;
|
char *tls_cafile;
|
||||||
char *tls_capath;
|
char *tls_capath;
|
||||||
char *tls_ciphers;
|
char *tls_ciphers;
|
||||||
|
char *tls_pinned_cert;
|
||||||
|
char *tls_pinned_pubkey;
|
||||||
|
|
||||||
GIOChannel *connect_handle; /* connect using this handle */
|
GIOChannel *connect_handle; /* connect using this handle */
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ char *tls_pass;
|
|||||||
char *tls_cafile;
|
char *tls_cafile;
|
||||||
char *tls_capath;
|
char *tls_capath;
|
||||||
char *tls_ciphers;
|
char *tls_ciphers;
|
||||||
|
char *tls_pinned_cert;
|
||||||
|
char *tls_pinned_pubkey;
|
||||||
|
|
||||||
char *own_host; /* address to use when connecting this server */
|
char *own_host; /* address to use when connecting this server */
|
||||||
IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
|
IPADDR *own_ip4, *own_ip6; /* resolved own_address if not NULL */
|
||||||
|
@ -199,6 +199,8 @@ server_connect_copy_skeleton(SERVER_CONNECT_REC *src, int connect_info)
|
|||||||
dest->tls_cafile = g_strdup(src->tls_cafile);
|
dest->tls_cafile = g_strdup(src->tls_cafile);
|
||||||
dest->tls_capath = g_strdup(src->tls_capath);
|
dest->tls_capath = g_strdup(src->tls_capath);
|
||||||
dest->tls_ciphers = g_strdup(src->tls_ciphers);
|
dest->tls_ciphers = g_strdup(src->tls_ciphers);
|
||||||
|
dest->tls_pinned_cert = g_strdup(src->tls_pinned_cert);
|
||||||
|
dest->tls_pinned_pubkey = g_strdup(src->tls_pinned_pubkey);
|
||||||
|
|
||||||
return dest;
|
return dest;
|
||||||
}
|
}
|
||||||
|
@ -181,6 +181,10 @@ static void server_setup_fill_server(SERVER_CONNECT_REC *conn,
|
|||||||
conn->tls_capath = g_strdup(sserver->tls_capath);
|
conn->tls_capath = g_strdup(sserver->tls_capath);
|
||||||
if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0')
|
if (conn->tls_ciphers == NULL && sserver->tls_ciphers != NULL && sserver->tls_ciphers[0] != '\0')
|
||||||
conn->tls_ciphers = g_strdup(sserver->tls_ciphers);
|
conn->tls_ciphers = g_strdup(sserver->tls_ciphers);
|
||||||
|
if (conn->tls_pinned_cert == NULL && sserver->tls_pinned_cert != NULL && sserver->tls_pinned_cert[0] != '\0')
|
||||||
|
conn->tls_pinned_cert = g_strdup(sserver->tls_pinned_cert);
|
||||||
|
if (conn->tls_pinned_pubkey == NULL && sserver->tls_pinned_pubkey != NULL && sserver->tls_pinned_pubkey[0] != '\0')
|
||||||
|
conn->tls_pinned_pubkey = g_strdup(sserver->tls_pinned_pubkey);
|
||||||
|
|
||||||
server_setup_fill_reconn(conn, sserver);
|
server_setup_fill_reconn(conn, sserver);
|
||||||
|
|
||||||
@ -435,6 +439,16 @@ static SERVER_SETUP_REC *server_setup_read(CONFIG_NODE *node)
|
|||||||
value = config_node_get_str(node, "ssl_ciphers", NULL);
|
value = config_node_get_str(node, "ssl_ciphers", NULL);
|
||||||
rec->tls_ciphers = g_strdup(value);
|
rec->tls_ciphers = g_strdup(value);
|
||||||
|
|
||||||
|
value = config_node_get_str(node, "tls_pinned_cert", NULL);
|
||||||
|
if (value == NULL)
|
||||||
|
value = config_node_get_str(node, "ssl_pinned_cert", NULL);
|
||||||
|
rec->tls_pinned_cert = g_strdup(value);
|
||||||
|
|
||||||
|
value = config_node_get_str(node, "tls_pinned_pubkey", NULL);
|
||||||
|
if (value == NULL)
|
||||||
|
value = config_node_get_str(node, "ssl_pinned_pubkey", NULL);
|
||||||
|
rec->tls_pinned_pubkey = g_strdup(value);
|
||||||
|
|
||||||
if (rec->tls_cafile || rec->tls_capath)
|
if (rec->tls_cafile || rec->tls_capath)
|
||||||
rec->tls_verify = TRUE;
|
rec->tls_verify = TRUE;
|
||||||
if (rec->tls_cert != NULL || rec->tls_verify)
|
if (rec->tls_cert != NULL || rec->tls_verify)
|
||||||
@ -500,6 +514,8 @@ static void server_setup_save(SERVER_SETUP_REC *rec)
|
|||||||
iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile);
|
iconfig_node_set_str(node, "tls_cafile", rec->tls_cafile);
|
||||||
iconfig_node_set_str(node, "tls_capath", rec->tls_capath);
|
iconfig_node_set_str(node, "tls_capath", rec->tls_capath);
|
||||||
iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers);
|
iconfig_node_set_str(node, "tls_ciphers", rec->tls_ciphers);
|
||||||
|
iconfig_node_set_str(node, "tls_pinned_cert", rec->tls_pinned_cert);
|
||||||
|
iconfig_node_set_str(node, "tls_pinned_pubkey", rec->tls_pinned_pubkey);
|
||||||
|
|
||||||
iconfig_node_set_str(node, "own_host", rec->own_host);
|
iconfig_node_set_str(node, "own_host", rec->own_host);
|
||||||
|
|
||||||
@ -550,6 +566,8 @@ static void server_setup_destroy(SERVER_SETUP_REC *rec)
|
|||||||
g_free_not_null(rec->tls_cafile);
|
g_free_not_null(rec->tls_cafile);
|
||||||
g_free_not_null(rec->tls_capath);
|
g_free_not_null(rec->tls_capath);
|
||||||
g_free_not_null(rec->tls_ciphers);
|
g_free_not_null(rec->tls_ciphers);
|
||||||
|
g_free_not_null(rec->tls_pinned_cert);
|
||||||
|
g_free_not_null(rec->tls_pinned_pubkey);
|
||||||
g_free(rec->address);
|
g_free(rec->address);
|
||||||
g_free(rec);
|
g_free(rec);
|
||||||
}
|
}
|
||||||
|
@ -633,6 +633,8 @@ void server_connect_unref(SERVER_CONNECT_REC *conn)
|
|||||||
g_free_not_null(conn->tls_cafile);
|
g_free_not_null(conn->tls_cafile);
|
||||||
g_free_not_null(conn->tls_capath);
|
g_free_not_null(conn->tls_capath);
|
||||||
g_free_not_null(conn->tls_ciphers);
|
g_free_not_null(conn->tls_ciphers);
|
||||||
|
g_free_not_null(conn->tls_pinned_cert);
|
||||||
|
g_free_not_null(conn->tls_pinned_pubkey);
|
||||||
|
|
||||||
g_free_not_null(conn->channels);
|
g_free_not_null(conn->channels);
|
||||||
g_free_not_null(conn->away_reason);
|
g_free_not_null(conn->away_reason);
|
||||||
|
@ -165,6 +165,8 @@ static void session_save_server(SERVER_REC *server, CONFIG_REC *config,
|
|||||||
config_node_set_str(config, node, "tls_cafile", server->connrec->tls_cafile);
|
config_node_set_str(config, node, "tls_cafile", server->connrec->tls_cafile);
|
||||||
config_node_set_str(config, node, "tls_capath", server->connrec->tls_capath);
|
config_node_set_str(config, node, "tls_capath", server->connrec->tls_capath);
|
||||||
config_node_set_str(config, node, "tls_ciphers", server->connrec->tls_ciphers);
|
config_node_set_str(config, node, "tls_ciphers", server->connrec->tls_ciphers);
|
||||||
|
config_node_set_str(config, node, "tls_pinned_cert", server->connrec->tls_pinned_cert);
|
||||||
|
config_node_set_str(config, node, "tls_pinned_pubkey", server->connrec->tls_pinned_pubkey);
|
||||||
|
|
||||||
handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
|
handle = g_io_channel_unix_get_fd(net_sendbuffer_handle(server->handle));
|
||||||
config_node_set_int(config, node, "handle", handle);
|
config_node_set_int(config, node, "handle", handle);
|
||||||
|
@ -196,6 +196,17 @@ static void cmd_server_add_modify(const char *data, gboolean add)
|
|||||||
if (value != NULL && *value != '\0')
|
if (value != NULL && *value != '\0')
|
||||||
rec->tls_ciphers = g_strdup(value);
|
rec->tls_ciphers = g_strdup(value);
|
||||||
|
|
||||||
|
value = g_hash_table_lookup(optlist, "tls_pinned_cert");
|
||||||
|
if (value == NULL)
|
||||||
|
value = g_hash_table_lookup(optlist, "ssl_pinned_cert");
|
||||||
|
if (value != NULL && *value != '\0')
|
||||||
|
rec->tls_pinned_cert = g_strdup(value);
|
||||||
|
|
||||||
|
value = g_hash_table_lookup(optlist, "tls_pinned_pubkey");
|
||||||
|
if (value == NULL)
|
||||||
|
value = g_hash_table_lookup(optlist, "ssl_pinned_pubkey");
|
||||||
|
if (value != NULL && *value != '\0')
|
||||||
|
rec->tls_pinned_pubkey = g_strdup(value);
|
||||||
|
|
||||||
if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0')
|
if ((rec->tls_cafile != NULL && rec->tls_cafile[0] != '\0')
|
||||||
|| (rec->tls_capath != NULL && rec->tls_capath[0] != '\0'))
|
|| (rec->tls_capath != NULL && rec->tls_capath[0] != '\0'))
|
||||||
@ -423,8 +434,8 @@ void fe_server_init(void)
|
|||||||
command_bind_first("server", NULL, (SIGNAL_FUNC) server_command);
|
command_bind_first("server", NULL, (SIGNAL_FUNC) server_command);
|
||||||
command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command);
|
command_bind_first("disconnect", NULL, (SIGNAL_FUNC) server_command);
|
||||||
|
|
||||||
command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
|
command_set_options("server add", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
|
||||||
command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers auto noauto proxy noproxy -host -port noautosendcmd");
|
command_set_options("server modify", "4 6 !! ssl +ssl_cert +ssl_pkey +ssl_pass ssl_verify +ssl_cafile +ssl_capath +ssl_ciphers +ssl_fingerprint tls +tls_cert +tls_pkey +tls_pass tls_verify +tls_cafile +tls_capath +tls_ciphers +tls_pinned_cert +tls_pinned_pubkey auto noauto proxy noproxy -host -port noautosendcmd");
|
||||||
|
|
||||||
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
|
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
|
||||||
signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
|
signal_add("server connecting", (SIGNAL_FUNC) sig_server_connecting);
|
||||||
|
@ -125,6 +125,10 @@ static void cmd_server_list(const char *data)
|
|||||||
g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath);
|
g_string_append_printf(str, "tls_capath: %s, ", rec->tls_capath);
|
||||||
if (rec->tls_ciphers)
|
if (rec->tls_ciphers)
|
||||||
g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers);
|
g_string_append_printf(str, "tls_ciphers: %s, ", rec->tls_ciphers);
|
||||||
|
if (rec->tls_pinned_cert)
|
||||||
|
g_string_append_printf(str, "tls_pinned_cert: %s, ", rec->tls_pinned_cert);
|
||||||
|
if (rec->tls_pinned_pubkey)
|
||||||
|
g_string_append_printf(str, "tls_pinned_pubkey: %s, ", rec->tls_pinned_pubkey);
|
||||||
|
|
||||||
}
|
}
|
||||||
if (rec->max_cmds_at_once > 0)
|
if (rec->max_cmds_at_once > 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user