Add discovery-filter. This makes it possible to restrict which targets
are returned during discovery based on initiator portal, name, and CHAP credentials. Reviewed by: mav@ MFC after: 1 month Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
1db6552d17
commit
0537488353
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=273813
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
Loading…
Reference in New Issue
Block a user