From c67809725b91564418db05d81c4defd4948baa01 Mon Sep 17 00:00:00 2001 From: trasz Date: Tue, 25 Mar 2014 12:01:55 +0000 Subject: MFC r261754: Implement initiator-name and initiator-portal restrictions. Sponsored by: The FreeBSD Foundation --- usr.sbin/ctld/ctl.conf.5 | 30 ++++++++++++- usr.sbin/ctld/ctld.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++- usr.sbin/ctld/ctld.h | 26 +++++++++++ usr.sbin/ctld/login.c | 27 ++++++++++++ usr.sbin/ctld/parse.y | 90 ++++++++++++++++++++++++++++++++++++-- usr.sbin/ctld/token.l | 2 + 6 files changed, 278 insertions(+), 6 deletions(-) diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5 index a0f6ad8..187a3bd 100644 --- a/usr.sbin/ctld/ctl.conf.5 +++ b/usr.sbin/ctld/ctl.conf.5 @@ -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 diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c index 6051348..039f7f0 100644 --- a/usr.sbin/ctld/ctld.c +++ b/usr.sbin/ctld/ctld.c @@ -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) { diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h index 3557978..ef1731f 100644 --- a/usr.sbin/ctld/ctld.h +++ b/usr.sbin/ctld/ctld.h @@ -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); diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c index b528fc0..c3e7532 100644 --- a/usr.sbin/ctld/login.c +++ b/usr.sbin/ctld/login.c @@ -940,6 +940,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. */ diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y index a7e01f6..3080bf4 100644 --- a/usr.sbin/ctld/parse.y +++ b/usr.sbin/ctld/parse.y @@ -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) { diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l index 41ef1e2..899a819 100644 --- a/usr.sbin/ctld/token.l +++ b/usr.sbin/ctld/token.l @@ -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; } -- cgit v1.1