From owner-svn-src-all@FreeBSD.ORG Tue Mar 25 12:01:57 2014 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 94848D6E; Tue, 25 Mar 2014 12:01:57 +0000 (UTC) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:1900:2254:2068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 661D913C; Tue, 25 Mar 2014 12:01:57 +0000 (UTC) Received: from svn.freebsd.org ([127.0.1.70]) by svn.freebsd.org (8.14.8/8.14.8) with ESMTP id s2PC1vcH068072; Tue, 25 Mar 2014 12:01:57 GMT (envelope-from trasz@svn.freebsd.org) Received: (from trasz@localhost) by svn.freebsd.org (8.14.8/8.14.8/Submit) id s2PC1u8X068066; Tue, 25 Mar 2014 12:01:56 GMT (envelope-from trasz@svn.freebsd.org) Message-Id: <201403251201.s2PC1u8X068066@svn.freebsd.org> From: Edward Tomasz Napierala Date: Tue, 25 Mar 2014 12:01:56 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-10@freebsd.org Subject: svn commit: r263720 - stable/10/usr.sbin/ctld X-SVN-Group: stable-10 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 25 Mar 2014 12:01:57 -0000 Author: trasz Date: Tue Mar 25 12:01:55 2014 New Revision: 263720 URL: http://svnweb.freebsd.org/changeset/base/263720 Log: MFC r261754: Implement initiator-name and initiator-portal restrictions. Sponsored by: The FreeBSD Foundation Modified: stable/10/usr.sbin/ctld/ctl.conf.5 stable/10/usr.sbin/ctld/ctld.c stable/10/usr.sbin/ctld/ctld.h stable/10/usr.sbin/ctld/login.c stable/10/usr.sbin/ctld/parse.y stable/10/usr.sbin/ctld/token.l Directory Properties: stable/10/ (props changed) Modified: stable/10/usr.sbin/ctld/ctl.conf.5 ============================================================================== --- stable/10/usr.sbin/ctld/ctl.conf.5 Tue Mar 25 12:00:05 2014 (r263719) +++ stable/10/usr.sbin/ctld/ctl.conf.5 Tue Mar 25 12:01:55 2014 (r263720) @@ -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 credential 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 configura 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 Modified: stable/10/usr.sbin/ctld/ctld.c ============================================================================== --- stable/10/usr.sbin/ctld/ctld.c Tue Mar 25 12:00:05 2014 (r263719) +++ stable/10/usr.sbin/ctld/ctld.c Tue Mar 25 12:01:55 2014 (r263720) @@ -149,6 +149,94 @@ auth_find(struct auth_group *ag, const c 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 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 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) { Modified: stable/10/usr.sbin/ctld/ctld.h ============================================================================== --- stable/10/usr.sbin/ctld/ctld.h Tue Mar 25 12:00:05 2014 (r263719) +++ stable/10/usr.sbin/ctld/ctld.h Tue Mar 25 12:01:55 2014 (r263720) @@ -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( 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); Modified: stable/10/usr.sbin/ctld/login.c ============================================================================== --- stable/10/usr.sbin/ctld/login.c Tue Mar 25 12:00:05 2014 (r263719) +++ stable/10/usr.sbin/ctld/login.c Tue Mar 25 12:01:55 2014 (r263720) @@ -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. */ Modified: stable/10/usr.sbin/ctld/parse.y ============================================================================== --- stable/10/usr.sbin/ctld/parse.y Tue Mar 25 12:00:05 2014 (r263719) +++ stable/10/usr.sbin/ctld/parse.y Tue Mar 25 12:01:55 2014 (r263720) @@ -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 } ; +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 S } ; +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) { Modified: stable/10/usr.sbin/ctld/token.l ============================================================================== --- stable/10/usr.sbin/ctld/token.l Tue Mar 25 12:00:05 2014 (r263719) +++ stable/10/usr.sbin/ctld/token.l Tue Mar 25 12:01:55 2014 (r263720) @@ -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; }