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:
Edward Tomasz Napierala 2014-10-29 09:26:55 +00:00
parent 1db6552d17
commit 0537488353
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=273813
7 changed files with 193 additions and 8 deletions

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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;

View File

@ -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; }