diff --git a/doc/cvtsudoers.cat b/doc/cvtsudoers.cat
index 7b72eb7f6..4e7283d0e 100644
--- a/doc/cvtsudoers.cat
+++ b/doc/cvtsudoers.cat
@@ -4,7 +4,7 @@ NNAAMMEE
      ccvvttssuuddooeerrss - convert between sudoers file formats
 
 SSYYNNOOPPSSIISS
-     ccvvttssuuddooeerrss [--eehhMMVV] [--bb _d_n] [--cc _c_o_n_f___f_i_l_e] [--dd _d_e_f_t_y_p_e_s]
+     ccvvttssuuddooeerrss [--eehhMMppVV] [--bb _d_n] [--cc _c_o_n_f___f_i_l_e] [--dd _d_e_f_t_y_p_e_s]
                 [--ff _o_u_t_p_u_t___f_o_r_m_a_t] [--ii _i_n_p_u_t___f_o_r_m_a_t] [--II _i_n_c_r_e_m_e_n_t]
                 [--mm _f_i_l_t_e_r] [--oo _o_u_t_p_u_t___f_i_l_e] [--OO _s_t_a_r_t___p_o_i_n_t] [--ss _s_e_c_t_i_o_n_s]
                 [_i_n_p_u_t___f_i_l_e]
@@ -147,6 +147,11 @@ DDEESSCCRRIIPPTTIIOONN
                  point of 0 will disable the generation of sudoOrder
                  attributes in the resulting LDIF file.
 
+     --pp, ----pprruunnee--mmaattcchheess
+                 When the --mm option is also specified, ccvvttssuuddooeerrss will prune
+                 out non-matching users, groups and hosts from matching
+                 entries.
+
      --ss _s_e_c_t_i_o_n_s, ----ssuupppprreessss=_s_e_c_t_i_o_n_s
                  Suppress the output of specific _s_e_c_t_i_o_n_s of the security
                  policy.  One or more section names may be specified,
@@ -182,6 +187,9 @@ DDEESSCCRRIIPPTTIIOONN
      oouuttppuutt__ffoorrmmaatt == _j_s_o_n | _l_d_i_f | _s_u_d_o_e_r_s
            See the description of the --ff command line option.
 
+     pprruunnee__mmaattcchheess == _y_e_s | _n_o
+           See the description of the --pp command line option.
+
      ssuuddooeerrss__bbaassee == _d_n
            See the description of the --bb command line option.
 
@@ -223,4 +231,4 @@ DDIISSCCLLAAIIMMEERR
      file distributed with ssuuddoo or https://www.sudo.ws/license.html for
      complete details.
 
-Sudo 1.8.23                     March 30, 2018                     Sudo 1.8.23
+Sudo 1.8.23                      April 4, 2018                     Sudo 1.8.23
diff --git a/doc/cvtsudoers.man.in b/doc/cvtsudoers.man.in
index 754dcac6e..de472a19a 100644
--- a/doc/cvtsudoers.man.in
+++ b/doc/cvtsudoers.man.in
@@ -16,7 +16,7 @@
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.TH "CVTSUDOERS" "1" "March 30, 2018" "Sudo @PACKAGE_VERSION@" "General Commands Manual"
+.TH "CVTSUDOERS" "1" "April 4, 2018" "Sudo @PACKAGE_VERSION@" "General Commands Manual"
 .nh
 .if n .ad l
 .SH "NAME"
@@ -25,7 +25,7 @@
 .SH "SYNOPSIS"
 .HP 11n
 \fBcvtsudoers\fR
-[\fB\-ehMV\fR]
+[\fB\-ehMpV\fR]
 [\fB\-b\fR\ \fIdn\fR]
 [\fB\-c\fR\ \fIconf_file\fR]
 [\fB\-d\fR\ \fIdeftypes\fR]
@@ -277,6 +277,14 @@ Defaults to a starting point of 1.
 A starting point of 0 will disable the generation of sudoOrder
 attributes in the resulting LDIF file.
 .TP 12n
+\fB\-p\fR, \fB\--prune-matches\fR
+When the
+\fB\-m\fR
+option is also specified,
+\fBcvtsudoers\fR
+will prune out non-matching users, groups and hosts from
+matching entries.
+.TP 12n
 \fB\-s\fR \fIsections\fR, \fB\--suppress\fR=\fIsections\fR
 Suppress the output of specific
 \fIsections\fR
@@ -340,6 +348,11 @@ See the description of the
 \fB\-f\fR
 command line option.
 .TP 6n
+\fBprune_matches =\fR \fIyes\fR | \fIno\fR
+See the description of the
+\fB\-p\fR
+command line option.
+.TP 6n
 \fBsudoers_base =\fR \fIdn\fR
 See the description of the
 \fB\-b\fR
diff --git a/doc/cvtsudoers.mdoc.in b/doc/cvtsudoers.mdoc.in
index 4b6e2ff22..467fe8dac 100644
--- a/doc/cvtsudoers.mdoc.in
+++ b/doc/cvtsudoers.mdoc.in
@@ -14,7 +14,7 @@
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 .\"
-.Dd March 30, 2018
+.Dd April 4, 2018
 .Dt CVTSUDOERS 1
 .Os Sudo @PACKAGE_VERSION@
 .Sh NAME
@@ -22,7 +22,7 @@
 .Nd convert between sudoers file formats
 .Sh SYNOPSIS
 .Nm cvtsudoers
-.Op Fl ehMV
+.Op Fl ehMpV
 .Op Fl b Ar dn
 .Op Fl c Ar conf_file
 .Op Fl d Ar deftypes
@@ -228,6 +228,13 @@ option for details.
 Defaults to a starting point of 1.
 A starting point of 0 will disable the generation of sudoOrder
 attributes in the resulting LDIF file.
+.It Fl p , Fl -prune-matches
+When the
+.Fl m
+option is also specified,
+.Nm
+will prune out non-matching users, groups and hosts from
+matching entries.
 .It Fl s Ar sections , Fl -suppress Ns = Ns Ar sections
 Suppress the output of specific
 .Ar sections
@@ -284,6 +291,10 @@ command line option.
 See the description of the
 .Fl f
 command line option.
+.It Sy prune_matches = Ar yes | no
+See the description of the
+.Fl p
+command line option.
 .It Sy sudoers_base = Ar dn
 See the description of the
 .Fl b
diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c
index c432c572b..dbe07a545 100644
--- a/plugins/sudoers/cvtsudoers.c
+++ b/plugins/sudoers/cvtsudoers.c
@@ -56,7 +56,7 @@
 struct cvtsudoers_filter *filters;
 struct sudo_user sudo_user;
 struct passwd *list_pw;
-static const char short_opts[] =  "b:c:d:ef:hi:I:m:Mo:O:s:V";
+static const char short_opts[] =  "b:c:d:ef:hi:I:m:Mo:O:ps:V";
 static struct option long_opts[] = {
     { "base",		required_argument,	NULL,	'b' },
     { "config",		required_argument,	NULL,	'c' },
@@ -68,6 +68,7 @@ static struct option long_opts[] = {
     { "increment",	required_argument,	NULL,	'I' },
     { "match",		required_argument,	NULL,	'm' },
     { "match-local",	no_argument,		NULL,	'M' },
+    { "prune-matches",	no_argument,		NULL,	'p' },
     { "order-start",	required_argument,	NULL,	'O' },
     { "output",		required_argument,	NULL,	'o' },
     { "suppress",	required_argument,	NULL,	's' },
@@ -85,7 +86,7 @@ static struct cvtsudoers_config *cvtsudoers_conf_read(const char *conf_file);
 static void cvtsudoers_conf_free(struct cvtsudoers_config *conf);
 static int cvtsudoers_parse_defaults(char *expression);
 static int cvtsudoers_parse_suppression(char *expression);
-static void filter_userspecs(void);
+static void filter_userspecs(struct cvtsudoers_config *conf);
 static void filter_defaults(struct cvtsudoers_config *conf);
 static void alias_remove_unused(void);
 
@@ -207,6 +208,9 @@ main(int argc, char *argv[])
 		usage(1);
 	    }
 	    break;
+	case 'p':
+	    conf->prune_matches = true;
+	    break;
 	case 's':
 	    conf->supstr = optarg;
 	    break;
@@ -317,7 +321,7 @@ main(int argc, char *argv[])
     }
 
     /* Apply filters. */
-    filter_userspecs();
+    filter_userspecs(conf);
     filter_defaults(conf);
     if (filters != NULL || conf->defaults != CVT_DEFAULTS_ALL)
 	alias_remove_unused();
@@ -355,7 +359,8 @@ static struct cvtsudoers_conf_table cvtsudoers_conf_vars[] = {
     { "match", CONF_STR, &cvtsudoers_config.filter },
     { "defaults", CONF_STR, &cvtsudoers_config.defstr },
     { "suppress", CONF_STR, &cvtsudoers_config.supstr },
-    { "expand_aliases", CONF_BOOL, &cvtsudoers_config.expand_aliases }
+    { "expand_aliases", CONF_BOOL, &cvtsudoers_config.expand_aliases },
+    { "prune_matches", CONF_BOOL, &cvtsudoers_config.prune_matches }
 };
 
 /*
@@ -669,86 +674,144 @@ open_sudoers(const char *sudoers, bool doedit, bool *keepopen)
 }
 
 static bool
-userlist_matches_filter(struct member_list *userlist)
+userlist_matches_filter(struct member_list *users, bool remove_nonmatching)
 {
     struct cvtsudoers_string *s;
-    bool matches = false;
+    struct member *m, *next;
+    bool ret = false;
     debug_decl(userlist_matches_filter, SUDOERS_DEBUG_UTIL)
 
     if (filters == NULL ||
 	(STAILQ_EMPTY(&filters->users) && STAILQ_EMPTY(&filters->groups)))
 	debug_return_bool(true);
 
-    if (STAILQ_EMPTY(&filters->users)) {
-	struct passwd pw;
+    TAILQ_FOREACH_REVERSE_SAFE(m, users, member_list, entries, next) {
+	bool matched = false;
 
-	/*
-	 * Only groups in filter, make a dummy user so userlist_matches()
-	 * can do its thing.
-	 */
-	memset(&pw, 0, sizeof(pw));
-	pw.pw_name = "_nobody";
-	pw.pw_uid = (uid_t)-1;
-	pw.pw_gid = (gid_t)-1;
-	if (userlist_matches(&pw, userlist) == true)
-	    matches = true;
-    }
+	if (STAILQ_EMPTY(&filters->users)) {
+	    struct passwd pw;
 
-    STAILQ_FOREACH(s, &filters->users, entries) {
-	struct passwd *pw = NULL;
-        if (s->str[0] == '#') {
-	    const char *errstr;
-            uid_t uid = sudo_strtoid(s->str + 1, NULL, NULL, &errstr);
-            if (errstr == NULL)
-                pw = sudo_getpwuid(uid);
+	    /*
+	     * Only groups in filter, make a dummy user so userlist_matches()
+	     * can do its thing.
+	     */
+	    memset(&pw, 0, sizeof(pw));
+	    pw.pw_name = "_nobody";
+	    pw.pw_uid = (uid_t)-1;
+	    pw.pw_gid = (gid_t)-1;
+
+	    if (user_matches(&pw, m) == true) {
+		matched = true;
+		ret = true;
+	    }
+	} else {
+	    STAILQ_FOREACH(s, &filters->users, entries) {
+		struct passwd *pw = NULL;
+
+		if (s->str[0] == '#') {
+		    const char *errstr;
+		    uid_t uid = sudo_strtoid(s->str + 1, NULL, NULL, &errstr);
+		    if (errstr == NULL)
+			pw = sudo_getpwuid(uid);
+		}
+		if (pw == NULL)
+		    pw = sudo_getpwnam(s->str);
+		if (pw == NULL)
+		    continue;
+
+		if (user_matches(pw, m) == true)
+		    matched = true;
+		sudo_pw_delref(pw);
+
+		/* Only need one user in the filter to match. */
+		if (matched) {
+		    ret = true;
+		    break;
+		}
+	    }
+	}
+
+	if (!matched && remove_nonmatching) {
+	    TAILQ_REMOVE(users, m, entries);
+	    free_member(m);
 	}
-	if (pw == NULL)
-	    pw = sudo_getpwnam(s->str);
-	if (pw == NULL)
-	    continue;
-	if (userlist_matches(pw, userlist) == true)
-	    matches = true;
-	sudo_pw_delref(pw);
-	if (matches)
-	    break;
     }
 
-    debug_return_bool(matches);
+    debug_return_bool(ret);
 }
 
 static bool
-hostlist_matches_filter(struct member_list *hostlist)
+hostlist_matches_filter(struct member_list *hostlist, bool remove_nonmatching)
 {
     struct cvtsudoers_string *s;
-    bool matches = false;
+    struct member *m, *next;
+    char *lhost, *shost;
+    bool ret = false;
+    char **shosts;
+    int n = 0;
     debug_decl(hostlist_matches_filter, SUDOERS_DEBUG_UTIL)
 
     if (filters == NULL || STAILQ_EMPTY(&filters->hosts))
 	debug_return_bool(true);
 
+    /* Create an array of short host names. */
     STAILQ_FOREACH(s, &filters->hosts, entries) {
-	user_runhost = s->str;
-	if ((user_srunhost = strchr(user_runhost, '.')) != NULL) {
-	    user_srunhost = strndup(user_runhost,
-		(size_t)(user_srunhost - user_runhost));
-	    if (user_srunhost == NULL) {
+	n++;
+    }
+    shosts = reallocarray(NULL, n, sizeof(char *));
+    if (shosts == NULL)
+	sudo_fatalx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
+    n = 0;
+    STAILQ_FOREACH(s, &filters->hosts, entries) {
+	lhost = s->str;
+	if ((shost = strchr(lhost, '.')) != NULL) {
+	    shost = strndup(lhost, (size_t)(shost - lhost));
+	    if (shost == NULL) {
 		sudo_fatalx(U_("%s: %s"), __func__,
 		    U_("unable to allocate memory"));
 	    }
 	} else {
-	    user_srunhost = user_runhost;
+	    shost = lhost;
 	}
-	/* XXX - can't use netgroup_tuple with NULL pw */
-	if (hostlist_matches(NULL, hostlist) == true)
-	    matches = true;
-	if (user_srunhost != user_runhost)
-	    free(user_srunhost);
-	user_runhost = user_host;
-	user_srunhost = user_shost;
-	if (matches)
-	    break;
+	shosts[n++] = shost;
     }
-    debug_return_bool(matches);
+
+    TAILQ_FOREACH_REVERSE_SAFE(m, hostlist, member_list, entries, next) {
+	bool matched = false;
+	n = 0;
+	STAILQ_FOREACH(s, &filters->hosts, entries) {
+	    lhost = s->str;
+	    shost = shosts[n++];
+
+	    /* XXX - can't use netgroup_tuple with NULL pw */
+	    if (host_matches(NULL, lhost, shost, m) == true)
+		matched = true;
+
+	    /* Only need one host in the filter to match. */
+	    if (matched) {
+		ret = true;
+		break;
+	    }
+	}
+
+	/* Short-circuit unless removing non-matches. */
+	if (!matched && remove_nonmatching) {
+	    TAILQ_REMOVE(hostlist, m, entries);
+	    free_member(m);
+	}
+    }
+
+    /* Free shosts array and its contents. */
+    n = 0;
+    STAILQ_FOREACH(s, &filters->hosts, entries) {
+	lhost = s->str;
+	shost = shosts[n++];
+	if (shost != lhost)
+	    free(shost);
+    }
+    free(shosts);
+
+    debug_return_bool(ret == true);
 }
 
 /*
@@ -811,7 +874,7 @@ convert_sudoers_output(const char *buf)
  * Apply filters to userspecs, removing non-matching entries.
  */
 static void
-filter_userspecs(void)
+filter_userspecs(struct cvtsudoers_config *conf)
 {
     struct userspec *us, *next_us;
     struct privilege *priv, *next_priv;
@@ -826,13 +889,13 @@ filter_userspecs(void)
      * In the future, we may want to add a prune option.
      */
     TAILQ_FOREACH_SAFE(us, &userspecs, entries, next_us) {
-	if (!userlist_matches_filter(&us->users)) {
+	if (!userlist_matches_filter(&us->users, conf->prune_matches)) {
 	    TAILQ_REMOVE(&userspecs, us, entries);
 	    free_userspec(us);
 	    continue;
 	}
 	TAILQ_FOREACH_SAFE(priv, &us->privileges, entries, next_priv) {
-	    if (!hostlist_matches_filter(&priv->hostlist)) {
+	    if (!hostlist_matches_filter(&priv->hostlist, conf->prune_matches)) {
 		TAILQ_REMOVE(&us->privileges, priv, entries);
 		free_privilege(priv);
 	    }
@@ -869,7 +932,7 @@ filter_defaults(struct cvtsudoers_config *conf)
 	    break;
 	case DEFAULTS_USER:
 	    if (!ISSET(conf->defaults, CVT_DEFAULTS_USER) ||
-		!userlist_matches_filter(def->binding))
+		!userlist_matches_filter(def->binding, conf->prune_matches))
 		keep = false;
 	    break;
 	case DEFAULTS_RUNAS:
@@ -878,7 +941,7 @@ filter_defaults(struct cvtsudoers_config *conf)
 	    break;
 	case DEFAULTS_HOST:
 	    if (!ISSET(conf->defaults, CVT_DEFAULTS_HOST) ||
-		!hostlist_matches_filter(def->binding))
+		!hostlist_matches_filter(def->binding, conf->prune_matches))
 		keep = false;
 	    break;
 	case DEFAULTS_CMND:
@@ -998,7 +1061,7 @@ done:
 static void
 usage(int fatal)
 {
-    (void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehMV] [-b dn] "
+    (void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehMpV] [-b dn] "
 	"[-c conf_file ] [-d deftypes] [-f output_format] [-i input_format] "
 	"[-I increment] [-m filter] [-o output_file] [-O start_point] "
 	"[-s sections] [input_file]\n", getprogname());
@@ -1023,6 +1086,7 @@ help(void)
 	"  -M, --match-local          match filter uses passwd and group databases\n"
 	"  -o, --output=output_file   write converted sudoers to output_file\n"
 	"  -O, --order-start=num      starting point for first sudoOrder\n"
+	"  -p, --prune-matches        prune non-matching users, groups and hosts\n"
 	"  -s, --suppress=sections    suppress output of certain sections\n"
 	"  -V, --version              display version information and exit"));
     exit(0);
diff --git a/plugins/sudoers/cvtsudoers.h b/plugins/sudoers/cvtsudoers.h
index 499382127..cc3b4462b 100644
--- a/plugins/sudoers/cvtsudoers.h
+++ b/plugins/sudoers/cvtsudoers.h
@@ -58,6 +58,7 @@ struct cvtsudoers_config {
     short suppress;
     bool expand_aliases;
     bool store_options;
+    bool prune_matches;
     char *sudoers_base;
     char *input_format;
     char *output_format;
@@ -67,7 +68,7 @@ struct cvtsudoers_config {
 };
 
 /* Initial config settings for above. */
-#define INITIAL_CONFIG { 1, 1, CVT_DEFAULTS_ALL, 0, false, true }
+#define INITIAL_CONFIG { 1, 1, CVT_DEFAULTS_ALL, 0, false, true, false }
 
 #define CONF_BOOL	0
 #define CONF_UINT	1
diff --git a/plugins/sudoers/match.c b/plugins/sudoers/match.c
index e67809a53..1ccbc1f0a 100644
--- a/plugins/sudoers/match.c
+++ b/plugins/sudoers/match.c
@@ -89,6 +89,48 @@ static bool digest_matches(int fd, const char *file, const struct sudo_digest *s
  */
 #define has_meta(s)	(strpbrk(s, "\\?*[]") != NULL)
 
+/*
+ * Check whether user described by pw matches member.
+ * Returns ALLOW, DENY or UNSPEC.
+ */
+int
+user_matches(const struct passwd *pw, const struct member *m)
+{
+    struct alias *a;
+    int matched = UNSPEC;
+    debug_decl(user_matches, SUDOERS_DEBUG_MATCH)
+
+    switch (m->type) {
+	case ALL:
+	    matched = !m->negated;
+	    break;
+	case NETGROUP:
+	    if (netgr_matches(m->name,
+		def_netgroup_tuple ? user_runhost : NULL,
+		def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name))
+		matched = !m->negated;
+	    break;
+	case USERGROUP:
+	    if (usergr_matches(m->name, pw->pw_name, pw))
+		matched = !m->negated;
+	    break;
+	case ALIAS:
+	    if ((a = alias_get(m->name, USERALIAS)) != NULL) {
+		int rc = userlist_matches(pw, &a->members);
+		if (rc != UNSPEC)
+		    matched = m->negated ? !rc : rc;
+		alias_put(a);
+		break;
+	    }
+	    /* FALLTHROUGH */
+	case WORD:
+	    if (userpw_matches(m->name, pw->pw_name, pw))
+		matched = !m->negated;
+	    break;
+    }
+    debug_return_int(matched);
+}
+
 /*
  * Check for user described by pw in a list of members.
  * Returns ALLOW, DENY or UNSPEC.
@@ -97,40 +139,11 @@ int
 userlist_matches(const struct passwd *pw, const struct member_list *list)
 {
     struct member *m;
-    struct alias *a;
-    int rc, matched = UNSPEC;
+    int matched = UNSPEC;
     debug_decl(userlist_matches, SUDOERS_DEBUG_MATCH)
 
     TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
-	switch (m->type) {
-	    case ALL:
-		matched = !m->negated;
-		break;
-	    case NETGROUP:
-		if (netgr_matches(m->name,
-		    def_netgroup_tuple ? user_runhost : NULL,
-		    def_netgroup_tuple ? user_srunhost : NULL, pw->pw_name))
-		    matched = !m->negated;
-		break;
-	    case USERGROUP:
-		if (usergr_matches(m->name, pw->pw_name, pw))
-		    matched = !m->negated;
-		break;
-	    case ALIAS:
-		if ((a = alias_get(m->name, USERALIAS)) != NULL) {
-		    rc = userlist_matches(pw, &a->members);
-		    if (rc != UNSPEC)
-			matched = m->negated ? !rc : rc;
-		    alias_put(a);
-		    break;
-		}
-		/* FALLTHROUGH */
-	    case WORD:
-		if (userpw_matches(m->name, pw->pw_name, pw))
-		    matched = !m->negated;
-		break;
-	}
-	if (matched != UNSPEC)
+	if ((matched = user_matches(pw, m)) != UNSPEC)
 	    break;
     }
     debug_return_int(matched);
@@ -256,46 +269,72 @@ runaslist_matches(const struct member_list *user_list,
 }
 
 /*
- * Check for host and shost in a list of members.
+ * Check for lhost and shost in a list of members.
+ * Returns ALLOW, DENY or UNSPEC.
+ */
+static int
+hostlist_matches_int(const struct passwd *pw, const char *lhost,
+    const char *shost, const struct member_list *list)
+{
+    struct member *m;
+    int matched = UNSPEC;
+    debug_decl(hostlist_matches, SUDOERS_DEBUG_MATCH)
+
+    TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
+	matched = host_matches(pw, lhost, shost, m);
+	if (matched != UNSPEC)
+	    break;
+    }
+    debug_return_int(matched);
+}
+
+/*
+ * Check for user_runhost and user_srunhost in a list of members.
  * Returns ALLOW, DENY or UNSPEC.
  */
 int
 hostlist_matches(const struct passwd *pw, const struct member_list *list)
 {
-    struct member *m;
-    struct alias *a;
-    int rc, matched = UNSPEC;
-    debug_decl(hostlist_matches, SUDOERS_DEBUG_MATCH)
+    return hostlist_matches_int(pw, user_runhost, user_srunhost, list);
+}
 
-    TAILQ_FOREACH_REVERSE(m, list, member_list, entries) {
-	switch (m->type) {
-	    case ALL:
+/*
+ * Check whether host or shost matches member.
+ * Returns ALLOW, DENY or UNSPEC.
+ */
+int
+host_matches(const struct passwd *pw, const char *lhost, const char *shost,
+    const struct member *m)
+{
+    struct alias *a;
+    int matched = UNSPEC;
+    debug_decl(host_matches, SUDOERS_DEBUG_MATCH)
+
+    switch (m->type) {
+	case ALL:
+	    matched = !m->negated;
+	    break;
+	case NETGROUP:
+	    if (netgr_matches(m->name, lhost, shost,
+		def_netgroup_tuple ? pw->pw_name : NULL))
 		matched = !m->negated;
+	    break;
+	case NTWKADDR:
+	    if (addr_matches(m->name))
+		matched = !m->negated;
+	    break;
+	case ALIAS:
+	    if ((a = alias_get(m->name, HOSTALIAS)) != NULL) {
+		int rc = hostlist_matches_int(pw, lhost, shost, &a->members);
+		if (rc != UNSPEC)
+		    matched = m->negated ? !rc : rc;
+		alias_put(a);
 		break;
-	    case NETGROUP:
-		if (netgr_matches(m->name, user_runhost, user_srunhost,
-		    def_netgroup_tuple ? pw->pw_name : NULL))
-		    matched = !m->negated;
-		break;
-	    case NTWKADDR:
-		if (addr_matches(m->name))
-		    matched = !m->negated;
-		break;
-	    case ALIAS:
-		if ((a = alias_get(m->name, HOSTALIAS)) != NULL) {
-		    rc = hostlist_matches(pw, &a->members);
-		    if (rc != UNSPEC)
-			matched = m->negated ? !rc : rc;
-		    alias_put(a);
-		    break;
-		}
-		/* FALLTHROUGH */
-	    case WORD:
-		if (hostname_matches(user_srunhost, user_runhost, m->name))
-		    matched = !m->negated;
-		break;
-	}
-	if (matched != UNSPEC)
+	    }
+	    /* FALLTHROUGH */
+	case WORD:
+	    if (hostname_matches(shost, lhost, m->name))
+		matched = !m->negated;
 	    break;
     }
     debug_return_int(matched);
diff --git a/plugins/sudoers/parse.h b/plugins/sudoers/parse.h
index 91555af91..f3dfaef6c 100644
--- a/plugins/sudoers/parse.h
+++ b/plugins/sudoers/parse.h
@@ -286,8 +286,10 @@ bool usergr_matches(const char *group, const char *user, const struct passwd *pw
 bool userpw_matches(const char *sudoers_user, const char *user, const struct passwd *pw);
 int cmnd_matches(const struct member *m);
 int cmndlist_matches(const struct member_list *list);
+int host_matches(const struct passwd *pw, const char *host, const char *shost, const struct member *m);
 int hostlist_matches(const struct passwd *pw, const struct member_list *list);
 int runaslist_matches(const struct member_list *user_list, const struct member_list *group_list, struct member **matching_user, struct member **matching_group);
+int user_matches(const struct passwd *pw, const struct member *m);
 int userlist_matches(const struct passwd *pw, const struct member_list *list);
 const char *sudo_getdomainname(void);
 
