From f908d4215f5170800cb76ea28b371ba63f69c17e Mon Sep 17 00:00:00 2001 From: trasz Date: Sat, 29 Nov 2014 15:32:15 +0000 Subject: [PATCH] MFC r273813: Add discovery-filter. This makes it possible to restrict which targets are returned during discovery based on initiator portal, name, and CHAP credentials. git-svn-id: svn://svn.freebsd.org/base/stable/10@275244 ccf9f872-aa2e-dd11-9fc8-001c23d0bc1f --- usr.sbin/ctld/ctl.conf.5 | 39 ++++++++++++++++++++- usr.sbin/ctld/ctld.c | 50 +++++++++++++++++++++++++++ usr.sbin/ctld/ctld.h | 11 ++++++ usr.sbin/ctld/discovery.c | 72 +++++++++++++++++++++++++++++++++++++-- usr.sbin/ctld/login.c | 7 +++- usr.sbin/ctld/parse.y | 21 +++++++++--- usr.sbin/ctld/token.l | 1 + 7 files changed, 193 insertions(+), 8 deletions(-) diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5 index 0f0f06b82..db1a32bcb 100644 --- a/usr.sbin/ctld/ctl.conf.5 +++ b/usr.sbin/ctld/ctl.conf.5 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd October 28, 2014 +.Dd October 29, 2014 .Dt CTL.CONF 5 .Os .Sh NAME @@ -175,6 +175,43 @@ Another predefined .Qq Ar no-authentication , may be used to permit discovery without authentication. +.It Ic discovery-filter Ar filter +Determines which targets are returned during discovery. +Filter can be either +.Qq Ar none , +.Qq Ar portal , +.Qq Ar portal-name , +or +.Qq Ar portal-name-auth . +When set to +.Qq Ar none , +discovery will return all targets assigned to that portal group. +When set to +.Qq Ar portal , +discovery will not return targets that cannot be accessed by the +initiator because of their +.Sy initiator-portal . +When set to +.Qq Ar portal-name , +the check will include both +.Sy initiator-portal +and +.Sy initiator-name . +When set to +.Qq Ar portal-name-auth , +the check will include +.Sy initiator-portal , +.Sy initiator-name , +and authentication credentials, ie. if the target does not require +CHAP authentication, or if CHAP user and secret used during discovery +match CHAP user and secret required to access the target. +Note that when using +.Qq Ar portal-name-auth , +targets that require CHAP authentication will only be returned if +.Sy discovery-auth-group +requires CHAP. +The default is +.Qq Ar none . .It Ic listen Ar address An IPv4 or IPv6 address and port to listen on for incoming connections. .\".It Ic listen-iser Ar address diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c index b8fd29bea..9e9022140 100644 --- a/usr.sbin/ctld/ctld.c +++ b/usr.sbin/ctld/ctld.c @@ -979,6 +979,53 @@ isns_deregister(struct isns *isns) set_timeout(0, false); } +static int +portal_group_set_filter(struct portal_group *pg, int filter) +{ + + if (pg->pg_discovery_filter == PG_FILTER_UNKNOWN) { + pg->pg_discovery_filter = filter; + return (0); + } + + if (pg->pg_discovery_filter == filter) + return (0); + + return (1); +} + +int +portal_group_set_filter_str(struct portal_group *pg, const char *str) +{ + int error, filter; + + if (strcmp(str, "none") == 0) { + filter = PG_FILTER_NONE; + } else if (strcmp(str, "portal") == 0) { + filter = PG_FILTER_PORTAL; + } else if (strcmp(str, "portal-name") == 0) { + filter = PG_FILTER_PORTAL_NAME; + } else if (strcmp(str, "portal-name-auth") == 0) { + filter = PG_FILTER_PORTAL_NAME_AUTH; + } else { + log_warnx("invalid discovery-filter \"%s\" for portal-group " + "\"%s\"; valid values are \"none\", \"portal\", " + "\"portal-name\", and \"portal-name-auth\"", + str, pg->pg_name); + return (1); + } + + error = portal_group_set_filter(pg, filter); + if (error != 0) { + log_warnx("cannot set discovery-filter to \"%s\" for " + "portal-group \"%s\"; already has a different " + "value", str, pg->pg_name); + return (1); + } + + return (error); +} + static bool valid_hex(const char ch) { @@ -1478,6 +1525,9 @@ conf_verify(struct conf *conf) assert(pg->pg_discovery_auth_group != NULL); } + if (pg->pg_discovery_filter == PG_FILTER_UNKNOWN) + pg->pg_discovery_filter = PG_FILTER_NONE; + TAILQ_FOREACH(targ, &conf->conf_targets, t_next) { if (targ->t_portal_group == pg) break; diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h index 1c7d4cb26..94301688b 100644 --- a/usr.sbin/ctld/ctld.h +++ b/usr.sbin/ctld/ctld.h @@ -103,11 +103,18 @@ struct portal { int p_socket; }; +#define PG_FILTER_UNKNOWN 0 +#define PG_FILTER_NONE 1 +#define PG_FILTER_PORTAL 2 +#define PG_FILTER_PORTAL_NAME 3 +#define PG_FILTER_PORTAL_NAME_AUTH 4 + struct portal_group { TAILQ_ENTRY(portal_group) pg_next; struct conf *pg_conf; char *pg_name; struct auth_group *pg_discovery_auth_group; + int pg_discovery_filter; bool pg_unassigned; TAILQ_HEAD(, portal) pg_portals; @@ -200,6 +207,8 @@ struct connection { int conn_immediate_data; int conn_header_digest; int conn_data_digest; + const char *conn_user; + struct chap *conn_chap; }; struct pdu { @@ -290,6 +299,8 @@ struct portal_group *portal_group_find(const struct conf *conf, const char *name); int portal_group_add_listen(struct portal_group *pg, const char *listen, bool iser); +int portal_group_set_filter_str(struct portal_group *pg, + const char *filter); int isns_new(struct conf *conf, const char *addr); void isns_delete(struct isns *is); diff --git a/usr.sbin/ctld/discovery.c b/usr.sbin/ctld/discovery.c index c900da7ba..01c991305 100644 --- a/usr.sbin/ctld/discovery.c +++ b/usr.sbin/ctld/discovery.c @@ -201,6 +201,65 @@ discovery_add_target(struct keys *response_keys, const struct target *targ) } } +static bool +discovery_target_filtered_out(const struct connection *conn, + const struct target *targ) +{ + const struct auth_group *ag; + const struct portal_group *pg; + const struct auth *auth; + int error; + + ag = targ->t_auth_group; + pg = conn->conn_portal->p_portal_group; + + assert(pg->pg_discovery_auth_group != PG_FILTER_UNKNOWN); + + if (pg->pg_discovery_filter >= PG_FILTER_PORTAL && + auth_portal_check(ag, &conn->conn_initiator_sa) != 0) { + log_debugx("initiator does not match initiator portals " + "allowed for target \"%s\"; skipping", targ->t_name); + return (true); + } + + if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME && + auth_name_check(ag, conn->conn_initiator_name) != 0) { + log_debugx("initiator does not match initiator names " + "allowed for target \"%s\"; skipping", targ->t_name); + return (true); + } + + if (pg->pg_discovery_filter >= PG_FILTER_PORTAL_NAME_AUTH && + ag->ag_type != AG_TYPE_NO_AUTHENTICATION) { + if (conn->conn_chap == NULL) { + assert(pg->pg_discovery_auth_group->ag_type == + AG_TYPE_NO_AUTHENTICATION); + + log_debugx("initiator didn't authenticate, but target " + "\"%s\" requires CHAP; skipping", targ->t_name); + return (true); + } + + assert(conn->conn_user != NULL); + auth = auth_find(ag, conn->conn_user); + if (auth == NULL) { + log_debugx("CHAP user \"%s\" doesn't match target " + "\"%s\"; skipping", conn->conn_user, targ->t_name); + return (true); + } + + error = chap_authenticate(conn->conn_chap, auth->a_secret); + if (error != 0) { + log_debugx("password for CHAP user \"%s\" doesn't " + "match target \"%s\"; skipping", + conn->conn_user, targ->t_name); + return (true); + } + } + + return (false); +} + void discovery(struct connection *conn) { @@ -232,6 +291,10 @@ discovery(struct connection *conn) targ->t_name); continue; } + if (discovery_target_filtered_out(conn, targ)) { + /* Ignore this target. */ + continue; + } discovery_add_target(response_keys, targ); } } else { @@ -239,8 +302,13 @@ discovery(struct connection *conn) if (targ == NULL) { log_debugx("initiator requested information on unknown " "target \"%s\"; returning nothing", send_targets); - } else - discovery_add_target(response_keys, targ); + } else { + if (discovery_target_filtered_out(conn, targ)) { + /* Ignore this target. */ + } else { + discovery_add_target(response_keys, targ); + } + } } keys_save(response_keys, response); diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c index 575707fe4..c59cea01b 100644 --- a/usr.sbin/ctld/login.c +++ b/usr.sbin/ctld/login.c @@ -441,7 +441,12 @@ login_chap(struct connection *conn, struct auth_group *ag) "transitioning to Negotiation Phase", auth->a_user); login_send_chap_success(request, auth); pdu_delete(request); - chap_delete(chap); + + /* + * Leave username and CHAP information for discovery(). + */ + conn->conn_user = auth->a_user; + conn->conn_chap = chap; } static void diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y index a66f200fb..2713f1773 100644 --- a/usr.sbin/ctld/parse.y +++ b/usr.sbin/ctld/parse.y @@ -58,10 +58,10 @@ extern void yyrestart(FILE *); %} %token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL -%token CLOSING_BRACKET DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP INITIATOR_NAME -%token INITIATOR_PORTAL LISTEN LISTEN_ISER LUN MAXPROC OPENING_BRACKET -%token OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET TIMEOUT -%token ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT +%token CLOSING_BRACKET DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP DISCOVERY_FILTER +%token INITIATOR_NAME INITIATOR_PORTAL LISTEN LISTEN_ISER LUN MAXPROC +%token OPENING_BRACKET OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR +%token TARGET TIMEOUT ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT %union { @@ -327,6 +327,8 @@ portal_group_entries: portal_group_entry: portal_group_discovery_auth_group | + portal_group_discovery_filter + | portal_group_listen | portal_group_listen_iser @@ -352,6 +354,17 @@ portal_group_discovery_auth_group: DISCOVERY_AUTH_GROUP STR } ; +portal_group_discovery_filter: DISCOVERY_FILTER STR + { + int error; + + error = portal_group_set_filter_str(portal_group, $2); + free($2); + if (error != 0) + return (1); + } + ; + portal_group_listen: LISTEN STR { int error; diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l index 6d85dc70b..c411a64ab 100644 --- a/usr.sbin/ctld/token.l +++ b/usr.sbin/ctld/token.l @@ -58,6 +58,7 @@ chap-mutual { return CHAP_MUTUAL; } debug { return DEBUG; } device-id { return DEVICE_ID; } discovery-auth-group { return DISCOVERY_AUTH_GROUP; } +discovery-filter { return DISCOVERY_FILTER; } initiator-name { return INITIATOR_NAME; } initiator-portal { return INITIATOR_PORTAL; } listen { return LISTEN; } -- 2.45.0