Implement initiator-name and initiator-portal restrictions.

Sponsored by:	The FreeBSD Foundation
This commit is contained in:
Edward Tomasz Napierala 2014-02-11 11:08:04 +00:00
parent 5d5a95f138
commit 8cb2e95863
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=261754
6 changed files with 278 additions and 6 deletions

View File

@ -27,7 +27,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd December 2, 2013
.Dd February 11, 2014
.Dt CTL.CONF 5
.Os
.Sh NAME
@ -107,6 +107,18 @@ Specifies CHAP authentication credentials.
Specifies mutual CHAP authentication credentials.
Note that for any auth-group, configuration may contain either chap,
or chap-mutual entries; it's an error to mix them.
.It Ic initiator-name Ao Ar initiator-name Ac
Specifies iSCSI initiator name.
If not defined, there will be no restrictions based on initiator
name.
Otherwise, only initiators with names matching one of defined
ones will be allowed to connect.
.It Ic initiator-portal Ao Ar address Ac
Specifies iSCSI initiator portal - IPv4 or IPv6 address.
If not defined, there will be no restrictions based on initiator
address.
Otherwise, only initiators with addresses matching one of defined
ones will be allowed to connect.
.El
.Ss portal-group level
The following statements are available at the portal-group level:
@ -143,6 +155,22 @@ or chap-mutual clauses; it's a configuration error to mix them in one target.
Specifies mutual CHAP authentication credentials.
Note that targets must use either auth-group, chap, or
chap-mutual clauses; it's a configuration error to mix them in one target.
.It Ic initiator-name Ao Ar initiator-name Ac
Specifies iSCSI initiator name.
If not defined, there will be no restrictions based on initiator
name.
Otherwise, only initiators with names matching one of defined
ones will be allowed to connect.
This clause is mutually exclusive with auth-group; one cannot use
both in a single target.
.It Ic initiator-portal Ao Ar address Ac
Specifies iSCSI initiator portal - IPv4 or IPv6 address.
If not defined, there will be no restrictions based on initiator
address.
Otherwise, only initiators with addresses matching one of defined
ones will be allowed to connect.
This clause is mutually exclusive with auth-group; one cannot use
both in a single target.
.It Ic portal-group Aq Ar name
Assigns previously defined portal group to that target.
Default portal group is "default", which makes the target available

View File

@ -149,6 +149,94 @@ auth_find(struct auth_group *ag, const char *user)
return (NULL);
}
const struct auth_name *
auth_name_new(struct auth_group *ag, const char *name)
{
struct auth_name *an;
an = calloc(1, sizeof(*an));
if (an == NULL)
log_err(1, "calloc");
an->an_auth_group = ag;
an->an_initator_name = checked_strdup(name);
TAILQ_INSERT_TAIL(&ag->ag_names, an, an_next);
return (an);
}
static void
auth_name_delete(struct auth_name *an)
{
TAILQ_REMOVE(&an->an_auth_group->ag_names, an, an_next);
free(an->an_initator_name);
free(an);
}
bool
auth_name_defined(const struct auth_group *ag)
{
if (TAILQ_EMPTY(&ag->ag_names))
return (false);
return (true);
}
const struct auth_name *
auth_name_find(const struct auth_group *ag, const char *name)
{
const struct auth_name *auth_name;
TAILQ_FOREACH(auth_name, &ag->ag_names, an_next) {
if (strcmp(auth_name->an_initator_name, name) == 0)
return (auth_name);
}
return (NULL);
}
const struct auth_portal *
auth_portal_new(struct auth_group *ag, const char *portal)
{
struct auth_portal *ap;
ap = calloc(1, sizeof(*ap));
if (ap == NULL)
log_err(1, "calloc");
ap->ap_auth_group = ag;
ap->ap_initator_portal = checked_strdup(portal);
TAILQ_INSERT_TAIL(&ag->ag_portals, ap, ap_next);
return (ap);
}
static void
auth_portal_delete(struct auth_portal *ap)
{
TAILQ_REMOVE(&ap->ap_auth_group->ag_portals, ap, ap_next);
free(ap->ap_initator_portal);
free(ap);
}
bool
auth_portal_defined(const struct auth_group *ag)
{
if (TAILQ_EMPTY(&ag->ag_portals))
return (false);
return (true);
}
const struct auth_portal *
auth_portal_find(const struct auth_group *ag, const char *portal)
{
const struct auth_portal *auth_portal;
TAILQ_FOREACH(auth_portal, &ag->ag_portals, ap_next) {
if (strcmp(auth_portal->ap_initator_portal, portal) == 0)
return (auth_portal);
}
return (NULL);
}
struct auth_group *
auth_group_new(struct conf *conf, const char *name)
{
@ -168,6 +256,8 @@ auth_group_new(struct conf *conf, const char *name)
if (name != NULL)
ag->ag_name = checked_strdup(name);
TAILQ_INIT(&ag->ag_auths);
TAILQ_INIT(&ag->ag_names);
TAILQ_INIT(&ag->ag_portals);
ag->ag_conf = conf;
TAILQ_INSERT_TAIL(&conf->conf_auth_groups, ag, ag_next);
@ -177,12 +267,19 @@ auth_group_new(struct conf *conf, const char *name)
void
auth_group_delete(struct auth_group *ag)
{
struct auth *auth, *tmp;
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, tmp)
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,
auth_portal_tmp)
auth_portal_delete(auth_portal);
free(ag->ag_name);
free(ag);
}
@ -832,6 +929,8 @@ conf_print(struct conf *conf)
{
struct auth_group *ag;
struct auth *auth;
struct auth_name *auth_name;
struct auth_portal *auth_portal;
struct portal_group *pg;
struct portal *portal;
struct target *targ;
@ -844,6 +943,12 @@ conf_print(struct conf *conf)
fprintf(stderr, "\t chap-mutual %s %s %s %s\n",
auth->a_user, auth->a_secret,
auth->a_mutual_user, auth->a_mutual_secret);
TAILQ_FOREACH(auth_name, &ag->ag_names, an_next)
fprintf(stderr, "\t initiator-name %s\n",
auth_name->an_initator_name);
TAILQ_FOREACH(auth_portal, &ag->ag_portals, an_next)
fprintf(stderr, "\t initiator-portal %s\n",
auth_portal->an_initator_portal);
fprintf(stderr, "}\n");
}
TAILQ_FOREACH(pg, &conf->conf_portal_groups, pg_next) {

View File

@ -53,6 +53,18 @@ struct auth {
char *a_mutual_secret;
};
struct auth_name {
TAILQ_ENTRY(auth_name) an_next;
struct auth_group *an_auth_group;
char *an_initator_name;
};
struct auth_portal {
TAILQ_ENTRY(auth_portal) ap_next;
struct auth_group *ap_auth_group;
char *ap_initator_portal;
};
#define AG_TYPE_UNKNOWN 0
#define AG_TYPE_NO_AUTHENTICATION 1
#define AG_TYPE_CHAP 2
@ -65,6 +77,8 @@ struct auth_group {
struct target *ag_target;
int ag_type;
TAILQ_HEAD(, auth) ag_auths;
TAILQ_HEAD(, auth_name) ag_names;
TAILQ_HEAD(, auth_portal) ag_portals;
};
struct portal {
@ -192,6 +206,18 @@ const struct auth *auth_new_chap_mutual(struct auth_group *ag,
const struct auth *auth_find(struct auth_group *ag,
const char *user);
const struct auth_name *auth_name_new(struct auth_group *ag,
const char *initiator_name);
bool auth_name_defined(const struct auth_group *ag);
const struct auth_name *auth_name_find(const struct auth_group *ag,
const char *initiator_name);
const struct auth_portal *auth_portal_new(struct auth_group *ag,
const char *initiator_portal);
bool auth_portal_defined(const struct auth_group *ag);
const struct auth_portal *auth_portal_find(const struct auth_group *ag,
const char *initiator_portal);
struct portal_group *portal_group_new(struct conf *conf, const char *name);
void portal_group_delete(struct portal_group *pg);
struct portal_group *portal_group_find(struct conf *conf, const char *name);

View File

@ -935,6 +935,33 @@ login(struct connection *conn)
}
}
/*
* Enforce initiator-name and initiator-portal.
*/
if (auth_name_defined(ag)) {
if (auth_name_find(ag, initiator_name) == NULL) {
login_send_error(request, 0x02, 0x02);
log_errx(1, "initiator does not match allowed "
"initiator names");
}
log_debugx("initiator matches allowed initiator names");
} else {
log_debugx("auth-group does not define initiator name "
"restrictions");
}
if (auth_portal_defined(ag)) {
if (auth_portal_find(ag, conn->conn_initiator_addr) == NULL) {
login_send_error(request, 0x02, 0x02);
log_errx(1, "initiator does not match allowed "
"initiator portals");
}
log_debugx("initiator matches allowed initiator portals");
} else {
log_debugx("auth-group does not define initiator portal "
"restrictions");
}
/*
* Let's see if the initiator intends to do any kind of authentication
* at all.

View File

@ -58,9 +58,9 @@ extern void yyrestart(FILE *);
%}
%token ALIAS AUTH_GROUP BACKEND BLOCKSIZE CHAP CHAP_MUTUAL CLOSING_BRACKET
%token DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP LISTEN LISTEN_ISER LUN MAXPROC NUM
%token OPENING_BRACKET OPTION PATH PIDFILE PORTAL_GROUP SERIAL SIZE STR TARGET
%token TIMEOUT
%token DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP INITIATOR_NAME INITIATOR_PORTAL
%token LISTEN LISTEN_ISER LUN MAXPROC NUM OPENING_BRACKET OPTION PATH PIDFILE
%token PORTAL_GROUP SERIAL SIZE STR TARGET TIMEOUT
%union
{
@ -148,6 +148,10 @@ auth_group_entry:
auth_group_chap
|
auth_group_chap_mutual
|
auth_group_initiator_name
|
auth_group_initiator_portal
;
auth_group_chap: CHAP STR STR
@ -176,6 +180,28 @@ auth_group_chap_mutual: CHAP_MUTUAL STR STR STR STR
}
;
auth_group_initiator_name: INITIATOR_NAME STR
{
const struct auth_name *an;
an = auth_name_new(auth_group, $2);
free($2);
if (an == NULL)
return (1);
}
;
auth_group_initiator_portal: INITIATOR_PORTAL STR
{
const struct auth_portal *ap;
ap = auth_portal_new(auth_group, $2);
free($2);
if (ap == NULL)
return (1);
}
;
portal_group_definition: PORTAL_GROUP portal_group_name
OPENING_BRACKET portal_group_entries CLOSING_BRACKET
{
@ -277,6 +303,10 @@ target_entry:
|
chap_mutual_statement
|
initiator_name_statement
|
initiator_portal_statement
|
portal_group_statement
|
lun_statement
@ -382,6 +412,60 @@ chap_mutual_statement: CHAP_MUTUAL STR STR STR STR
}
;
initiator_name_statement: INITIATOR_NAME STR
{
const struct auth_name *an;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot mix auth-group with "
"initiator-name for target \"%s\"",
target->t_iqn);
free($2);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
an = auth_name_new(target->t_auth_group, $2);
free($2);
if (an == NULL)
return (1);
}
;
initiator_portal_statement: INITIATOR_PORTAL STR
{
const struct auth_portal *ap;
if (target->t_auth_group != NULL) {
if (target->t_auth_group->ag_name != NULL) {
log_warnx("cannot mix auth-group with "
"initiator-portal for target \"%s\"",
target->t_iqn);
free($2);
return (1);
}
} else {
target->t_auth_group = auth_group_new(conf, NULL);
if (target->t_auth_group == NULL) {
free($2);
return (1);
}
target->t_auth_group->ag_target = target;
}
ap = auth_portal_new(target->t_auth_group, $2);
free($2);
if (ap == NULL)
return (1);
}
;
portal_group_statement: PORTAL_GROUP STR
{
if (target->t_portal_group != NULL) {

View File

@ -57,6 +57,8 @@ chap-mutual { return CHAP_MUTUAL; }
debug { return DEBUG; }
device-id { return DEVICE_ID; }
discovery-auth-group { return DISCOVERY_AUTH_GROUP; }
initiator-name { return INITIATOR_NAME; }
initiator-portal { return INITIATOR_PORTAL; }
listen { return LISTEN; }
listen-iser { return LISTEN_ISER; }
lun { return LUN; }