svn commit: r273813 - head/usr.sbin/ctld

Edward Tomasz Napierala trasz at FreeBSD.org
Wed Oct 29 09:26:58 UTC 2014


Author: trasz
Date: Wed Oct 29 09:26:55 2014
New Revision: 273813
URL: https://svnweb.freebsd.org/changeset/base/273813

Log:
  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

Modified:
  head/usr.sbin/ctld/ctl.conf.5
  head/usr.sbin/ctld/ctld.c
  head/usr.sbin/ctld/ctld.h
  head/usr.sbin/ctld/discovery.c
  head/usr.sbin/ctld/login.c
  head/usr.sbin/ctld/parse.y
  head/usr.sbin/ctld/token.l

Modified: head/usr.sbin/ctld/ctl.conf.5
==============================================================================
--- head/usr.sbin/ctld/ctl.conf.5	Wed Oct 29 09:06:05 2014	(r273812)
+++ head/usr.sbin/ctld/ctl.conf.5	Wed Oct 29 09:26:55 2014	(r273813)
@@ -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

Modified: head/usr.sbin/ctld/ctld.c
==============================================================================
--- head/usr.sbin/ctld/ctld.c	Wed Oct 29 09:06:05 2014	(r273812)
+++ head/usr.sbin/ctld/ctld.c	Wed Oct 29 09:26:55 2014	(r273813)
@@ -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;

Modified: head/usr.sbin/ctld/ctld.h
==============================================================================
--- head/usr.sbin/ctld/ctld.h	Wed Oct 29 09:06:05 2014	(r273812)
+++ head/usr.sbin/ctld/ctld.h	Wed Oct 29 09:26:55 2014	(r273813)
@@ -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(c
 			    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);

Modified: head/usr.sbin/ctld/discovery.c
==============================================================================
--- head/usr.sbin/ctld/discovery.c	Wed Oct 29 09:06:05 2014	(r273812)
+++ head/usr.sbin/ctld/discovery.c	Wed Oct 29 09:26:55 2014	(r273813)
@@ -201,6 +201,65 @@ discovery_add_target(struct keys *respon
 	}
 }
 
+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);
 

Modified: head/usr.sbin/ctld/login.c
==============================================================================
--- head/usr.sbin/ctld/login.c	Wed Oct 29 09:06:05 2014	(r273812)
+++ head/usr.sbin/ctld/login.c	Wed Oct 29 09:26:55 2014	(r273813)
@@ -441,7 +441,12 @@ login_chap(struct connection *conn, stru
 	    "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

Modified: head/usr.sbin/ctld/parse.y
==============================================================================
--- head/usr.sbin/ctld/parse.y	Wed Oct 29 09:06:05 2014	(r273812)
+++ head/usr.sbin/ctld/parse.y	Wed Oct 29 09:26:55 2014	(r273813)
@@ -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:	DISCO
 	}
 	;
 
+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;

Modified: head/usr.sbin/ctld/token.l
==============================================================================
--- head/usr.sbin/ctld/token.l	Wed Oct 29 09:06:05 2014	(r273812)
+++ head/usr.sbin/ctld/token.l	Wed Oct 29 09:26:55 2014	(r273813)
@@ -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; }


More information about the svn-src-head mailing list