From 4c6e259681206d0b4ad49879cede6bdc19dc6360 Mon Sep 17 00:00:00 2001 From: Bram Matthys Date: Sun, 21 Sep 2025 11:31:31 +0200 Subject: [PATCH] You can now use "password" multiple times in the conf (eg in allow::password). allow { mask *; password "secret"; password "letmein"; } This is always an "OR" type of match, any match means you pass. I was actually doing this for the dual-cert stuff from previous commit, where this can come in handy: link irc1.example.org { ... password "AHMYBevUxXKU/S3pdBSjXP4zi4VOetYQQVJXoNYiBR0=" { spkifp; }; password "jNw8P4QMg9tqjEJ4/lFikXBNHdIGSeN2B4/T322VjIo=" { spkifp; }; ... } --- include/h.h | 2 +- include/struct.h | 1 + src/auth.c | 93 ++++++++++++++++++++++++++----------------- src/conf.c | 51 +++++++++--------------- src/modules/rpc/rpc.c | 2 +- src/modules/vhost.c | 2 +- 6 files changed, 78 insertions(+), 73 deletions(-) diff --git a/include/h.h b/include/h.h index b9f2b6e7e..72aec5a70 100644 --- a/include/h.h +++ b/include/h.h @@ -538,7 +538,7 @@ extern int b64_encode(unsigned char const *src, size_t srclength, char *target, extern int b64_decode(char const *src, unsigned char *target, size_t targsize); extern AuthenticationType Auth_FindType(const char *hash, const char *type); -extern AuthConfig *AuthBlockToAuthConfig(ConfigEntry *ce); +extern void AuthBlockToAuthConfig(ConfigEntry *ce, AuthConfig **list); extern void Auth_FreeAuthConfig(AuthConfig *as); extern int Auth_Check(Client *cptr, AuthConfig *as, const char *para); extern const char *Auth_Hash(AuthenticationType type, const char *text); diff --git a/include/struct.h b/include/struct.h index 29f53a16b..f00cd6ce7 100644 --- a/include/struct.h +++ b/include/struct.h @@ -1638,6 +1638,7 @@ typedef struct AuthConfig AuthConfig; * configuration file. */ struct AuthConfig { + AuthConfig *prev, *next; AuthenticationType type; /**< Type of data, one of AUTHTYPE_* */ char *data; /**< Data associated with this record */ }; diff --git a/src/auth.c b/src/auth.c index 1f30ffab8..1610db6f9 100644 --- a/src/auth.c +++ b/src/auth.c @@ -265,7 +265,7 @@ int Auth_CheckError(ConfigEntry *ce, int warn_on_plaintext) /** Convert an authentication block from the configuration file * into an AuthConfig structure so it can be used at runtime. */ -AuthConfig *AuthBlockToAuthConfig(ConfigEntry *ce) +void AuthBlockToAuthConfig(ConfigEntry *ce, AuthConfig **list) { AuthenticationType type = AUTHTYPE_PLAINTEXT; AuthConfig *as = NULL; @@ -277,14 +277,17 @@ AuthConfig *AuthBlockToAuthConfig(ConfigEntry *ce) as = safe_alloc(sizeof(AuthConfig)); safe_strdup(as->data, ce->value); as->type = type; - return as; + + AddListItem(as, *list); } /** Free an AuthConfig struct */ void Auth_FreeAuthConfig(AuthConfig *as) { - if (as) + AuthConfig *as_next; + for (; as; as = as_next) { + as_next = as->next; safe_free(as->data); safe_free(as); } @@ -448,48 +451,64 @@ int Auth_Check(Client *client, AuthConfig *as, const char *para) if (!as || !as->data) return 0; /* Should not happen, but better be safe.. */ - switch (as->type) + for (; as; as = as->next) { - case AUTHTYPE_PLAINTEXT: - if (!para) - return 0; - if (!strcmp(as->data, "changemeplease") && !strcmp(para, as->data)) - { - unreal_log(ULOG_INFO, "auth", "AUTH_REJECT_DEFAULT_PASSWORD", client, - "Rejecting default password 'changemeplease'. " - "Please change the password in the configuration file."); - return 0; - } - /* plain text compare */ - if (!strcmp(para, as->data)) - return 1; - return 0; + switch (as->type) + { + case AUTHTYPE_PLAINTEXT: + if (!para) + return 0; + if (!strcmp(as->data, "changemeplease") && !strcmp(para, as->data)) + { + unreal_log(ULOG_INFO, "auth", "AUTH_REJECT_DEFAULT_PASSWORD", client, + "Rejecting default password 'changemeplease'. " + "Please change the password in the configuration file."); + return 0; + } + /* plain text compare */ + if (!strcmp(para, as->data)) + return 1; + break; - case AUTHTYPE_ARGON2: - return authcheck_argon2(client, as, para); + case AUTHTYPE_ARGON2: + if (authcheck_argon2(client, as, para)) + return 1; + break; - case AUTHTYPE_BCRYPT: - return authcheck_bcrypt(client, as, para); + case AUTHTYPE_BCRYPT: + if (authcheck_bcrypt(client, as, para)) + return 1; + break; - case AUTHTYPE_UNIXCRYPT: - if (!para) - return 0; - res = crypt(para, as->data); - if (res && !strcmp(res, as->data)) - return 1; - return 0; + case AUTHTYPE_UNIXCRYPT: + if (!para) + return 0; + res = crypt(para, as->data); + if (res && !strcmp(res, as->data)) + return 1; + break; - case AUTHTYPE_TLS_CLIENTCERT: - return authcheck_tls_clientcert(client, as, para); + case AUTHTYPE_TLS_CLIENTCERT: + if (authcheck_tls_clientcert(client, as, para)) + return 1; + break; - case AUTHTYPE_TLS_CLIENTCERTFP: - return authcheck_tls_clientcert_fingerprint(client, as, para); + case AUTHTYPE_TLS_CLIENTCERTFP: + if (authcheck_tls_clientcert_fingerprint(client, as, para)) + return 1; + break; - case AUTHTYPE_SPKIFP: - return authcheck_spkifp(client, as, para); + case AUTHTYPE_SPKIFP: + if (authcheck_spkifp(client, as, para)) + return 1; + break; - case AUTHTYPE_INVALID: - return 0; /* Should never happen */ + case AUTHTYPE_INVALID: +#ifdef DEBUGMODE + abort(); +#endif + break; /* Should never happen */ + } } return 0; } diff --git a/src/conf.c b/src/conf.c index 03cecbac1..35fa7811b 100644 --- a/src/conf.c +++ b/src/conf.c @@ -4247,7 +4247,7 @@ int _conf_oper(ConfigFile *conf, ConfigEntry *ce) if (!strcmp(cep->name, "operclass")) safe_strdup(oper->operclass, cep->value); if (!strcmp(cep->name, "password")) - oper->auth = AuthBlockToAuthConfig(cep); + AuthBlockToAuthConfig(cep, &oper->auth); else if (!strcmp(cep->name, "class")) { oper->class = find_class(cep->value); @@ -4351,12 +4351,6 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) /* oper::password */ if (!strcmp(cep->name, "password")) { - if (has_password) - { - config_warn_duplicate(cep->file->filename, - cep->line_number, "oper::password"); - continue; - } has_password = 1; if (ce->value && cep->value && @@ -4567,12 +4561,6 @@ int _test_oper(ConfigFile *conf, ConfigEntry *ce) } else if (!strcmp(cep->name, "password")) { - if (has_password) - { - config_warn_duplicate(cep->file->filename, - cep->line_number, "oper::password"); - continue; - } has_password = 1; if (Auth_CheckError(cep, 1) < 0) errors++; @@ -4700,7 +4688,7 @@ int _test_proxy(ConfigFile *conf, ConfigEntry *ce) } else if (!strcmp(cep->name, "password")) { - config_detect_duplicate(&has_password, cep, &errors); + has_password = 1; if (Auth_CheckError(cep, 0) < 0) errors++; } @@ -4785,7 +4773,7 @@ int _conf_proxy(ConfigFile *conf, ConfigEntry *ce) if (!strcmp(cep->name, "mask") || !strcmp(cep->name, "match")) conf_match_block(conf, cep, &proxy->mask); else if (!strcmp(cep->name, "password")) - proxy->auth = AuthBlockToAuthConfig(cep); + AuthBlockToAuthConfig(cep, &proxy->auth); else if (!strcmp(cep->name, "type")) proxy->type = proxy_type_string_to_value(cep->value); } @@ -5076,19 +5064,9 @@ int _conf_drpass(ConfigFile *conf, ConfigEntry *ce) for (cep = ce->items; cep; cep = cep->next) { if (!strcmp(cep->name, "restart")) - { - if (conf_drpass->restartauth) - Auth_FreeAuthConfig(conf_drpass->restartauth); - - conf_drpass->restartauth = AuthBlockToAuthConfig(cep); - } + AuthBlockToAuthConfig(cep, &conf_drpass->restartauth); else if (!strcmp(cep->name, "die")) - { - if (conf_drpass->dieauth) - Auth_FreeAuthConfig(conf_drpass->dieauth); - - conf_drpass->dieauth = AuthBlockToAuthConfig(cep); - } + AuthBlockToAuthConfig(cep, &conf_drpass->dieauth); } return 1; } @@ -5940,7 +5918,7 @@ int _conf_allow(ConfigFile *conf, ConfigEntry *ce) conf_match_block(conf, cep, &allow->match); } else if (!strcmp(cep->name, "password")) - allow->auth = AuthBlockToAuthConfig(cep); + AuthBlockToAuthConfig(cep, &allow->auth); else if (!strcmp(cep->name, "class")) { allow->class = find_class(cep->value); @@ -6146,8 +6124,7 @@ int _test_allow(ConfigFile *conf, ConfigEntry *ce) } else if (!strcmp(cep->name, "password")) { - config_detect_duplicate(&has_password, cep, &errors); - /* some auth check stuff? */ + has_password = 1; if (Auth_CheckError(cep, 0) < 0) errors++; } @@ -6614,7 +6591,7 @@ int _conf_link(ConfigFile *conf, ConfigEntry *ce) } } else if (!strcmp(cep->name, "password")) - link->auth = AuthBlockToAuthConfig(cep); + AuthBlockToAuthConfig(cep, &link->auth); else if (!strcmp(cep->name, "hub")) safe_strdup(link->hub, cep->value); else if (!strcmp(cep->name, "leaf")) @@ -6795,12 +6772,13 @@ int _test_link(ConfigFile *conf, ConfigEntry *ce) } else if (!strcmp(cep->name, "password")) { - config_detect_duplicate(&has_password, cep, &errors); + has_password = 1; if (Auth_CheckError(cep, 0) < 0) { errors++; } else { - AuthConfig *auth = AuthBlockToAuthConfig(cep); + AuthConfig *auth; + AuthBlockToAuthConfig(cep, &auth); /* hm. would be nicer if handled @auth-system I think. ah well.. */ if ((auth->type != AUTHTYPE_PLAINTEXT) && (auth->type != AUTHTYPE_TLS_CLIENTCERT) && (auth->type != AUTHTYPE_TLS_CLIENTCERTFP) && (auth->type != AUTHTYPE_SPKIFP)) @@ -10915,6 +10893,13 @@ int _test_secret(ConfigFile *conf, ConfigEntry *ce) if (!strcmp(cep->name, "password")) { int n; + if (has_password) + { + config_error("%s:%d: you can only have one password here", + cep->file->filename, cep->line_number); + errors++; + continue; + } has_password = 1; CheckNull(cep); if (cep->items || diff --git a/src/modules/rpc/rpc.c b/src/modules/rpc/rpc.c index 042d5a433..f395c5314 100644 --- a/src/modules/rpc/rpc.c +++ b/src/modules/rpc/rpc.c @@ -474,7 +474,7 @@ int rpc_config_run_rpc_user(ConfigFile *cf, ConfigEntry *ce, int type) } else if (!strcmp(cep->name, "password")) { - e->auth = AuthBlockToAuthConfig(cep); + AuthBlockToAuthConfig(cep, &e->auth); } else if (!strcmp(cep->name, "rpc-class")) { diff --git a/src/modules/vhost.c b/src/modules/vhost.c index 1ce13ce47..42c2f55ed 100644 --- a/src/modules/vhost.c +++ b/src/modules/vhost.c @@ -282,7 +282,7 @@ int vhost_config_run(ConfigFile *conf, ConfigEntry *ce, int type) else if (!strcmp(cep->name, "login")) safe_strdup(vhost->login, cep->value); else if (!strcmp(cep->name, "password")) - vhost->auth = AuthBlockToAuthConfig(cep); + AuthBlockToAuthConfig(cep, &vhost->auth); else if (!strcmp(cep->name, "match") || !strcmp(cep->name, "mask")) { conf_match_block(conf, cep, &vhost->match);