summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2014-11-29 15:32:15 +0000
committertrasz <trasz@FreeBSD.org>2014-11-29 15:32:15 +0000
commitaa126c68f3102918dd0447c491a82c1d42d4d06e (patch)
tree51acd97f81d9f3ced06a2f4663bed23fbe6767b9
parent0475e04762f5dd444faf64924251cd3466d84c98 (diff)
downloadFreeBSD-src-aa126c68f3102918dd0447c491a82c1d42d4d06e.zip
FreeBSD-src-aa126c68f3102918dd0447c491a82c1d42d4d06e.tar.gz
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.
-rw-r--r--usr.sbin/ctld/ctl.conf.539
-rw-r--r--usr.sbin/ctld/ctld.c50
-rw-r--r--usr.sbin/ctld/ctld.h11
-rw-r--r--usr.sbin/ctld/discovery.c72
-rw-r--r--usr.sbin/ctld/login.c7
-rw-r--r--usr.sbin/ctld/parse.y21
-rw-r--r--usr.sbin/ctld/token.l1
7 files changed, 193 insertions, 8 deletions
diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5
index 0f0f06b..db1a32b 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 b8fd29b..9e90221 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 1c7d4cb..9430168 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 c900da7..01c9913 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 575707f..c59cea0 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 a66f200..2713f17 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 6d85dc7..c411a64 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; }
OpenPOWER on IntegriCloud