diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2021-06-25 10:44:35 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2021-06-25 11:15:22 +0200 |
commit | 0fb88527ce38e8b791a48802e160225aa38743cd (patch) | |
tree | 35597e6ce0745fb98616bbb3200cc35decfe8af3 /src | |
parent | 7a0020f067922a8616e5ac313bbc59f07683e4a8 (diff) | |
download | weechat-0fb88527ce38e8b791a48802e160225aa38743cd.zip |
irc: implement IRCv3.2 SASL authentication, add command /auth (closes #413)
Diffstat (limited to 'src')
-rw-r--r-- | src/plugins/irc/irc-command.c | 114 | ||||
-rw-r--r-- | src/plugins/irc/irc-protocol.c | 21 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.c | 60 | ||||
-rw-r--r-- | src/plugins/irc/irc-server.h | 6 |
4 files changed, 180 insertions, 21 deletions
diff --git a/src/plugins/irc/irc-command.c b/src/plugins/irc/irc-command.c index 71e7771e7..0df532365 100644 --- a/src/plugins/irc/irc-command.c +++ b/src/plugins/irc/irc-command.c @@ -793,6 +793,95 @@ IRC_COMMAND_CALLBACK(allserv) } /* + * Callback for command "/auth": authenticates with SASL. + */ + +IRC_COMMAND_CALLBACK(auth) +{ + char str_msg_auth[512]; + int sasl_mechanism; + + IRC_BUFFER_GET_SERVER(buffer); + IRC_COMMAND_CHECK_SERVER("auth", 1, 1); + + /* make C compiler happy */ + (void) pointer; + (void) data; + + if (ptr_server->sasl_temp_username) + { + free (ptr_server->sasl_temp_username); + ptr_server->sasl_temp_username = NULL; + } + if (ptr_server->sasl_temp_password) + { + free (ptr_server->sasl_temp_password); + ptr_server->sasl_temp_password = NULL; + } + + if ((argc < 3) && !irc_server_sasl_enabled (ptr_server)) + { + weechat_printf ( + ptr_server->buffer, + _("%s%s: \"%s\" command can only be executed if SASL is enabled " + "via server options \"sasl_*\" (or you must give username and " + "password)"), + weechat_prefix ("error"), IRC_PLUGIN_NAME, "auth"); + return WEECHAT_RC_OK; + } + + if (weechat_hashtable_has_key (ptr_server->cap_list, "sasl")) + { + /* SASL capability already enabled, authenticate */ + sasl_mechanism = IRC_SERVER_OPTION_INTEGER( + ptr_server, IRC_SERVER_OPTION_SASL_MECHANISM); + if ((sasl_mechanism >= 0) + && (sasl_mechanism < IRC_NUM_SASL_MECHANISMS)) + { + if (argc > 2) + { + ptr_server->sasl_temp_username = strdup (argv[1]); + ptr_server->sasl_temp_password = strdup (argv_eol[2]); + } + snprintf (str_msg_auth, sizeof (str_msg_auth), + "AUTHENTICATE %s", + irc_sasl_mechanism_string[sasl_mechanism]); + weechat_string_toupper (str_msg_auth); + irc_server_sendf (ptr_server, IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL, + str_msg_auth); + } + } + else + { + /* "sasl" capability supported by the server? */ + if (weechat_hashtable_has_key (ptr_server->cap_ls, "sasl")) + { + /* + * request "sasl" capability, then the server should ask + * immediately to authenticate by sending a message + * "AUTHENTICATE +" + */ + if (argc > 2) + { + ptr_server->sasl_temp_username = strdup (argv[1]); + ptr_server->sasl_temp_password = strdup (argv_eol[2]); + } + irc_server_sendf (ptr_server, IRC_SERVER_SEND_OUTQ_PRIO_HIGH, NULL, + "CAP REQ sasl"); + } + else + { + weechat_printf ( + ptr_server->buffer, + _("%s%s: SASL is not supported by the server"), + weechat_prefix ("error"), IRC_PLUGIN_NAME); + } + } + + return WEECHAT_RC_OK; +} + +/* * Displays a ctcp action on a channel. */ @@ -6474,6 +6563,31 @@ irc_command_init () " do a whois on my nick on all servers:\n" " /allserv /whois $nick"), NULL, &irc_command_allserv, NULL, NULL); + weechat_hook_command ( + "auth", + N_("authenticate with SASL"), + N_("[<username> <password>]"), + N_("username: SASL username (content is evaluated, see /help eval; " + "server options are evaluated with ${irc_server.xxx} and ${server} " + "is replaced by the server name)\n" + "password: SASL password or path to file with private key " + "(content is evaluated, see /help eval; server options are " + "evaluated with ${irc_server.xxx} and ${server} is replaced by the " + "server name)\n" + "\n" + "If username and password are not provided, the values from server " + "options \"sasl_username\" and \"sasl_password\" (or \"sasl_key\") " + "are used.\n" + "\n" + "Examples:\n" + " authenticate with username/password defined in the server:\n" + " /auth\n" + " authenticate as a different user:\n" + " /auth user2 password2\n" + " authenticate as a different user with mechanism " + "ecdsa-nist256p-challenge:\n" + " /auth user2 ${weechat_config_dir}/ecdsa2.pem"), + NULL, &irc_command_auth, NULL, NULL); weechat_hook_command_run ("/away", &irc_command_run_away, NULL, NULL); weechat_hook_command ( "ban", diff --git a/src/plugins/irc/irc-protocol.c b/src/plugins/irc/irc-protocol.c index 9a003e13a..7cb1bfa3d 100644 --- a/src/plugins/irc/irc-protocol.c +++ b/src/plugins/irc/irc-protocol.c @@ -398,23 +398,19 @@ IRC_PROTOCOL_CALLBACK(account) IRC_PROTOCOL_CALLBACK(authenticate) { int sasl_mechanism; - char *sasl_username, *sasl_password, *answer, *sasl_error; - const char *sasl_key; + char *sasl_username, *sasl_password, *sasl_key, *answer, *sasl_error; IRC_PROTOCOL_MIN_ARGS(2); if (!irc_server_sasl_enabled (server)) return WEECHAT_RC_OK; + irc_server_sasl_get_creds (server, &sasl_username, &sasl_password, + &sasl_key); + sasl_mechanism = IRC_SERVER_OPTION_INTEGER( server, IRC_SERVER_OPTION_SASL_MECHANISM); - sasl_username = irc_server_eval_expression ( - server, - IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_USERNAME)); - sasl_password = irc_server_eval_expression ( - server, - IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_PASSWORD)); - sasl_key = IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_KEY); + answer = NULL; sasl_error = NULL; switch (sasl_mechanism) @@ -471,6 +467,8 @@ IRC_PROTOCOL_CALLBACK(authenticate) free (sasl_username); if (sasl_password) free (sasl_password); + if (sasl_key) + free (sasl_key); if (sasl_error) free (sasl_error); @@ -6621,8 +6619,9 @@ IRC_PROTOCOL_CALLBACK(sasl_end_fail) ignored, argc, argv, argv_eol); sasl_fail = IRC_SERVER_OPTION_INTEGER(server, IRC_SERVER_OPTION_SASL_FAIL); - if ((sasl_fail == IRC_SERVER_SASL_FAIL_RECONNECT) - || (sasl_fail == IRC_SERVER_SASL_FAIL_DISCONNECT)) + if (!server->is_connected + && ((sasl_fail == IRC_SERVER_SASL_FAIL_RECONNECT) + || (sasl_fail == IRC_SERVER_SASL_FAIL_DISCONNECT))) { irc_server_disconnect ( server, 0, diff --git a/src/plugins/irc/irc-server.c b/src/plugins/irc/irc-server.c index d20c7479c..09146a92f 100644 --- a/src/plugins/irc/irc-server.c +++ b/src/plugins/irc/irc-server.c @@ -93,7 +93,7 @@ char *irc_server_options[IRC_SERVER_NUM_OPTIONS][2] = { "sasl_password", "" }, { "sasl_key", "", }, { "sasl_timeout", "15" }, - { "sasl_fail", "continue" }, + { "sasl_fail", "reconnect" }, { "autoconnect", "off" }, { "autoreconnect", "on" }, { "autoreconnect_delay", "10" }, @@ -509,6 +509,33 @@ irc_server_eval_fingerprint (struct t_irc_server *server) } /* + * Gets SASL credentials on server (uses temporary SASL username/password if + * set by the command /auth <user> <pass>). + */ + +void +irc_server_sasl_get_creds (struct t_irc_server *server, + char **username, char **password, char **key) +{ + const char *ptr_username, *ptr_password, *ptr_key; + + ptr_username = (server->sasl_temp_username) ? + server->sasl_temp_username : + IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_USERNAME); + ptr_password = (server->sasl_temp_password) ? + server->sasl_temp_password : + IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_PASSWORD); + /* temporary password can also be a path to file with private key */ + ptr_key = (server->sasl_temp_password) ? + server->sasl_temp_password : + IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_KEY); + + *username = irc_server_eval_expression (server, ptr_username); + *password = irc_server_eval_expression (server, ptr_password); + *key = irc_server_eval_expression (server, ptr_key); +} + +/* * Checks if SASL is enabled on server. * * Returns: @@ -520,18 +547,13 @@ int irc_server_sasl_enabled (struct t_irc_server *server) { int sasl_mechanism, rc; - char *sasl_username, *sasl_password; - const char *sasl_key; + char *sasl_username, *sasl_password, *sasl_key; + + irc_server_sasl_get_creds (server, + &sasl_username, &sasl_password, &sasl_key); sasl_mechanism = IRC_SERVER_OPTION_INTEGER( server, IRC_SERVER_OPTION_SASL_MECHANISM); - sasl_username = irc_server_eval_expression ( - server, - IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_USERNAME)); - sasl_password = irc_server_eval_expression ( - server, - IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_PASSWORD)); - sasl_key = IRC_SERVER_OPTION_STRING(server, IRC_SERVER_OPTION_SASL_KEY); /* * SASL is enabled if one of these conditions is true: @@ -550,6 +572,8 @@ irc_server_sasl_enabled (struct t_irc_server *server) free (sasl_username); if (sasl_password) free (sasl_password); + if (sasl_key) + free (sasl_key); return rc; } @@ -1468,6 +1492,8 @@ irc_server_alloc (const char *name) new_server->sasl_scram_salted_pwd = NULL; new_server->sasl_scram_salted_pwd_size = 0; new_server->sasl_scram_auth_message = NULL; + new_server->sasl_temp_username = NULL; + new_server->sasl_temp_password = NULL; new_server->is_connected = 0; new_server->ssl_connected = 0; new_server->disconnected = 0; @@ -1970,6 +1996,16 @@ irc_server_free_sasl_data (struct t_irc_server *server) free (server->sasl_scram_auth_message); server->sasl_scram_auth_message = NULL; } + if (server->sasl_temp_username) + { + free (server->sasl_temp_username); + server->sasl_temp_username = NULL; + } + if (server->sasl_temp_password) + { + free (server->sasl_temp_password); + server->sasl_temp_password = NULL; + } } /* @@ -5914,6 +5950,8 @@ irc_server_hdata_server_cb (const void *pointer, void *data, WEECHAT_HDATA_VAR(struct t_irc_server, sasl_scram_salted_pwd, OTHER, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_irc_server, sasl_scram_salted_pwd_size, INTEGER, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_irc_server, sasl_scram_auth_message, STRING, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_server, sasl_temp_username, STRING, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_server, sasl_temp_password, STRING, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_irc_server, is_connected, INTEGER, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_irc_server, ssl_connected, INTEGER, 0, NULL, NULL); WEECHAT_HDATA_VAR(struct t_irc_server, disconnected, INTEGER, 0, NULL, NULL); @@ -6554,6 +6592,8 @@ irc_server_print_log () weechat_log_printf (" sasl_scram_salted_pwd . . : (hidden)"); weechat_log_printf (" sasl_scram_salted_pwd_size: %d", ptr_server->sasl_scram_salted_pwd_size); weechat_log_printf (" sasl_scram_auth_message . : (hidden)"); + weechat_log_printf (" sasl_temp_username. . . . : '%s'", ptr_server->sasl_temp_username); + weechat_log_printf (" sasl_temp_password. . . . : (hidden)"); weechat_log_printf (" is_connected. . . . . . . : %d", ptr_server->is_connected); weechat_log_printf (" ssl_connected . . . . . . : %d", ptr_server->ssl_connected); weechat_log_printf (" disconnected. . . . . . . : %d", ptr_server->disconnected); diff --git a/src/plugins/irc/irc-server.h b/src/plugins/irc/irc-server.h index 2dbb98ff9..7058f3d00 100644 --- a/src/plugins/irc/irc-server.h +++ b/src/plugins/irc/irc-server.h @@ -197,6 +197,8 @@ struct t_irc_server char *sasl_scram_salted_pwd; /* salted password for SASL SCRAM */ int sasl_scram_salted_pwd_size; /* size of salted password for SASL SCRAM*/ char *sasl_scram_auth_message; /* auth message for SASL SCRAM */ + char *sasl_temp_username; /* temp SASL username (set by /auth cmd) */ + char *sasl_temp_password; /* temp SASL password (set by /auth cmd) */ int is_connected; /* 1 if WeeChat is connected to server */ int ssl_connected; /* = 1 if connected with SSL */ int disconnected; /* 1 if server has been disconnected */ @@ -310,6 +312,10 @@ extern int irc_server_strncasecmp (struct t_irc_server *server, int max); extern char *irc_server_eval_expression (struct t_irc_server *server, const char *string); +extern void irc_server_sasl_get_creds (struct t_irc_server *server, + char **username, + char **password, + char **key); extern int irc_server_sasl_enabled (struct t_irc_server *server); extern char *irc_server_get_name_without_port (const char *name); extern int irc_server_set_addresses (struct t_irc_server *server, |