The branch main has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=7f912714c53644ea18fc58ffa364918ccfa22999
commit 7f912714c53644ea18fc58ffa364918ccfa22999 Author: John Baldwin <j...@freebsd.org> AuthorDate: 2025-08-04 19:38:06 +0000 Commit: John Baldwin <j...@freebsd.org> CommitDate: 2025-08-04 19:38:06 +0000 ctld: Convert struct auth to a C++ class Use private std::string to hold secret and mutual authentication strings along with accessors to retrieve constant C versions of those strings. Add a helper function to determine if an auth object contains mutual credentials. Instead of storing the user name in the structure, use an unordered_map<> with the username as the key for the ag_auths member of auth_group. Add a parse error if multiple credentials specify the same user. Previously the code always used the first credential when verifying and ignored additional credentials silently. Sponsored by: Chelsio Communications Pull Request: https://github.com/freebsd/freebsd-src/pull/1794 --- usr.sbin/ctld/ctld.cc | 72 +++++++++++++--------------------------------- usr.sbin/ctld/ctld.hh | 28 +++++++++++++----- usr.sbin/ctld/discovery.cc | 2 +- usr.sbin/ctld/login.cc | 31 ++++++++++---------- 4 files changed, 57 insertions(+), 76 deletions(-) diff --git a/usr.sbin/ctld/ctld.cc b/usr.sbin/ctld/ctld.cc index ba65befa2d0a..558ddb8ac6aa 100644 --- a/usr.sbin/ctld/ctld.cc +++ b/usr.sbin/ctld/ctld.cc @@ -143,42 +143,14 @@ conf_delete(struct conf *conf) free(conf); } -static struct auth * -auth_new(struct auth_group *ag) -{ - struct auth *auth; - - auth = reinterpret_cast<struct auth *>(calloc(1, sizeof(*auth))); - if (auth == NULL) - log_err(1, "calloc"); - auth->a_auth_group = ag; - TAILQ_INSERT_TAIL(&ag->ag_auths, auth, a_next); - return (auth); -} - -static void -auth_delete(struct auth *auth) -{ - TAILQ_REMOVE(&auth->a_auth_group->ag_auths, auth, a_next); - - free(auth->a_user); - free(auth->a_secret); - free(auth->a_mutual_user); - free(auth->a_mutual_secret); - free(auth); -} - const struct auth * auth_find(const struct auth_group *ag, const char *user) { - const struct auth *auth; + auto it = ag->ag_auths.find(user); + if (it == ag->ag_auths.end()) + return (nullptr); - TAILQ_FOREACH(auth, &ag->ag_auths, a_next) { - if (strcmp(auth->a_user, user) == 0) - return (auth); - } - - return (NULL); + return (&it->second); } static void @@ -188,6 +160,7 @@ auth_check_secret_length(const struct auth_group *ag, const char *user, size_t len; len = strlen(secret); + assert(len != 0); if (len > 16) { log_warnx("%s for user \"%s\", %s, is too long; it should be " "at most 16 characters long", secret_type, user, @@ -204,8 +177,6 @@ bool auth_new_chap(struct auth_group *ag, const char *user, const char *secret) { - struct auth *auth; - if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP; if (ag->ag_type != AG_TYPE_CHAP) { @@ -216,9 +187,12 @@ auth_new_chap(struct auth_group *ag, const char *user, auth_check_secret_length(ag, user, secret, "secret"); - auth = auth_new(ag); - auth->a_user = checked_strdup(user); - auth->a_secret = checked_strdup(secret); + const auto &pair = ag->ag_auths.try_emplace(user, secret); + if (!pair.second) { + log_warnx("duplicate credentials for user \"%s\" for %s", + user, ag->ag_label); + return (false); + } return (true); } @@ -227,8 +201,6 @@ bool auth_new_chap_mutual(struct auth_group *ag, const char *user, const char *secret, const char *user2, const char *secret2) { - struct auth *auth; - if (ag->ag_type == AG_TYPE_UNKNOWN) ag->ag_type = AG_TYPE_CHAP_MUTUAL; if (ag->ag_type != AG_TYPE_CHAP_MUTUAL) { @@ -240,11 +212,13 @@ auth_new_chap_mutual(struct auth_group *ag, const char *user, auth_check_secret_length(ag, user, secret, "secret"); auth_check_secret_length(ag, user, secret2, "mutual secret"); - auth = auth_new(ag); - auth->a_user = checked_strdup(user); - auth->a_secret = checked_strdup(secret); - auth->a_mutual_user = checked_strdup(user2); - auth->a_mutual_secret = checked_strdup(secret2); + const auto &pair = ag->ag_auths.try_emplace(user, secret, user2, + secret2); + if (!pair.second) { + log_warnx("duplicate credentials for user \"%s\" for %s", + user, ag->ag_label); + return (false); + } return (true); } @@ -442,13 +416,10 @@ auth_group_create(struct conf *conf, const char *name, char *label) { struct auth_group *ag; - ag = reinterpret_cast<struct auth_group *>(calloc(1, sizeof(*ag))); - if (ag == NULL) - log_err(1, "calloc"); + ag = new auth_group(); if (name != NULL) ag->ag_name = checked_strdup(name); ag->ag_label = label; - TAILQ_INIT(&ag->ag_auths); TAILQ_INIT(&ag->ag_names); TAILQ_INIT(&ag->ag_portals); ag->ag_conf = conf; @@ -485,14 +456,11 @@ auth_group_new(struct conf *conf, struct target *target) void auth_group_delete(struct auth_group *ag) { - struct auth *auth, *auth_tmp; struct auth_name *auth_name, *auth_name_tmp; struct auth_portal *auth_portal, *auth_portal_tmp; TAILQ_REMOVE(&ag->ag_conf->conf_auth_groups, ag, ag_next); - TAILQ_FOREACH_SAFE(auth, &ag->ag_auths, a_next, auth_tmp) - auth_delete(auth); TAILQ_FOREACH_SAFE(auth_name, &ag->ag_names, an_next, auth_name_tmp) auth_name_delete(auth_name); TAILQ_FOREACH_SAFE(auth_portal, &ag->ag_portals, ap_next, @@ -500,7 +468,7 @@ auth_group_delete(struct auth_group *ag) auth_portal_delete(auth_portal); free(ag->ag_label); free(ag->ag_name); - free(ag); + delete ag; } struct auth_group * diff --git a/usr.sbin/ctld/ctld.hh b/usr.sbin/ctld/ctld.hh index 61e453d8dc23..a2cedeaf77da 100644 --- a/usr.sbin/ctld/ctld.hh +++ b/usr.sbin/ctld/ctld.hh @@ -41,6 +41,10 @@ #include <libiscsiutil.h> #include <libutil.h> +#include <string> +#include <string_view> +#include <unordered_map> + #define DEFAULT_CONFIG_PATH "/etc/ctl.conf" #define DEFAULT_PIDFILE "/var/run/ctld.pid" #define DEFAULT_BLOCKSIZE 512 @@ -50,12 +54,22 @@ #define SOCKBUF_SIZE 1048576 struct auth { - TAILQ_ENTRY(auth) a_next; - struct auth_group *a_auth_group; - char *a_user; - char *a_secret; - char *a_mutual_user; - char *a_mutual_secret; + auth(std::string_view secret) : a_secret(secret) {} + auth(std::string_view secret, std::string_view mutual_user, + std::string_view mutual_secret) : + a_secret(secret), a_mutual_user(mutual_user), + a_mutual_secret(mutual_secret) {} + + bool mutual() const { return !a_mutual_user.empty(); } + + const char *secret() const { return a_secret.c_str(); } + const char *mutual_user() const { return a_mutual_user.c_str(); } + const char *mutual_secret() const { return a_mutual_secret.c_str(); } + +private: + std::string a_secret; + std::string a_mutual_user; + std::string a_mutual_secret; }; struct auth_name { @@ -84,7 +98,7 @@ struct auth_group { char *ag_name; char *ag_label; int ag_type; - TAILQ_HEAD(, auth) ag_auths; + std::unordered_map<std::string, auth> ag_auths; TAILQ_HEAD(, auth_name) ag_names; TAILQ_HEAD(, auth_portal) ag_portals; }; diff --git a/usr.sbin/ctld/discovery.cc b/usr.sbin/ctld/discovery.cc index 3cf1b94a9619..c4bfb94669f9 100644 --- a/usr.sbin/ctld/discovery.cc +++ b/usr.sbin/ctld/discovery.cc @@ -196,7 +196,7 @@ discovery_target_filtered_out(const struct ctld_connection *conn, return (true); } - error = chap_authenticate(conn->conn_chap, auth->a_secret); + error = chap_authenticate(conn->conn_chap, auth->secret()); if (error != 0) { log_debugx("password for CHAP user \"%s\" doesn't " "match target \"%s\"; skipping", diff --git a/usr.sbin/ctld/login.cc b/usr.sbin/ctld/login.cc index e891a3ed4b67..549fa0c397ad 100644 --- a/usr.sbin/ctld/login.cc +++ b/usr.sbin/ctld/login.cc @@ -336,7 +336,7 @@ login_send_chap_c(struct pdu *request, struct chap *chap) static struct pdu * login_receive_chap_r(struct connection *conn, struct auth_group *ag, - struct chap *chap, const struct auth **authp) + struct chap *chap, const struct auth **authp, std::string &user) { struct pdu *request; struct keys *request_keys; @@ -376,15 +376,13 @@ login_receive_chap_r(struct connection *conn, struct auth_group *ag, chap_n); } - assert(auth->a_secret != NULL); - assert(strlen(auth->a_secret) > 0); - - error = chap_authenticate(chap, auth->a_secret); + error = chap_authenticate(chap, auth->secret()); if (error != 0) { login_send_error(request, 0x02, 0x01); log_errx(1, "CHAP authentication failed for user \"%s\"", - auth->a_user); + chap_n); } + user = chap_n; keys_delete(request_keys); @@ -394,7 +392,7 @@ login_receive_chap_r(struct connection *conn, struct auth_group *ag, static void login_send_chap_success(struct pdu *request, - const struct auth *auth) + const struct auth *auth, const std::string &user) { struct pdu *response; struct keys *request_keys, *response_keys; @@ -424,17 +422,17 @@ login_send_chap_success(struct pdu *request, log_errx(1, "initiator requested target " "authentication, but didn't send CHAP_C"); } - if (auth->a_auth_group->ag_type != AG_TYPE_CHAP_MUTUAL) { + if (!auth->mutual()) { login_send_error(request, 0x02, 0x01); log_errx(1, "initiator requests target authentication " "for user \"%s\", but mutual user/secret " - "is not set", auth->a_user); + "is not set", user.c_str()); } log_debugx("performing mutual authentication as user \"%s\"", - auth->a_mutual_user); + auth->mutual_user()); - rchap = rchap_new(auth->a_mutual_secret); + rchap = rchap_new(auth->mutual_secret()); error = rchap_receive(rchap, chap_i, chap_c); if (error != 0) { login_send_error(request, 0x02, 0x07); @@ -444,7 +442,7 @@ login_send_chap_success(struct pdu *request, chap_r = rchap_get_response(rchap); rchap_delete(rchap); response_keys = keys_new(); - keys_add(response_keys, "CHAP_N", auth->a_mutual_user); + keys_add(response_keys, "CHAP_N", auth->mutual_user()); keys_add(response_keys, "CHAP_R", chap_r); free(chap_r); keys_save_pdu(response_keys, response); @@ -461,6 +459,7 @@ login_send_chap_success(struct pdu *request, static void login_chap(struct ctld_connection *conn, struct auth_group *ag) { + std::string user; const struct auth *auth; struct chap *chap; struct pdu *request; @@ -488,20 +487,20 @@ login_chap(struct ctld_connection *conn, struct auth_group *ag) * Receive CHAP_N/CHAP_R PDU and authenticate. */ log_debugx("waiting for CHAP_N/CHAP_R"); - request = login_receive_chap_r(&conn->conn, ag, chap, &auth); + request = login_receive_chap_r(&conn->conn, ag, chap, &auth, user); /* * Yay, authentication succeeded! */ log_debugx("authentication succeeded for user \"%s\"; " - "transitioning to operational parameter negotiation", auth->a_user); - login_send_chap_success(request, auth); + "transitioning to operational parameter negotiation", user.c_str()); + login_send_chap_success(request, auth, user); pdu_delete(request); /* * Leave username and CHAP information for discovery(). */ - conn->conn_user = auth->a_user; + conn->conn_user = checked_strdup(user.c_str()); conn->conn_chap = chap; }