Implement initiator-name and initiator-portal restrictions.
Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
5d5a95f138
commit
8cb2e95863
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=261754
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
|
@ -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) {
|
||||
|
@ -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; }
|
||||
|
Loading…
Reference in New Issue
Block a user