diff --git a/doc/cvtsudoers.cat b/doc/cvtsudoers.cat
index eec3a9174..75d0cac50 100644
--- a/doc/cvtsudoers.cat
+++ b/doc/cvtsudoers.cat
@@ -4,7 +4,7 @@ NNAAMMEE
      ccvvttssuuddooeerrss - convert between sudoers file formats
 
 SSYYNNOOPPSSIISS
-     ccvvttssuuddooeerrss [--eehhVV] [--bb _d_n] [--cc _c_o_n_f___f_i_l_e] [--ff _o_u_t_p_u_t___f_o_r_m_a_t]
+     ccvvttssuuddooeerrss [--eehhMMVV] [--bb _d_n] [--cc _c_o_n_f___f_i_l_e] [--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]
 
@@ -99,6 +99,16 @@ DDEESSCCRRIIPPTTIIOONN
                  are referenced by the filtered policy rules will be
                  displayed.
 
+     --MM, ----mmaattcchh--llooccaall
+                 When the --mm option is also specified, use password and group
+                 database information when matching users and groups in the
+                 filter.  Only users and groups in the filter that exist on
+                 the local system will match, and a user's groups will
+                 automatically be added to the filter.  If the --MM is _n_o_t
+                 specified, users and groups in the filter do not need to
+                 exist on the local system, but all groups used for matching
+                 must be explicitly listed in the filter.
+
      --oo _o_u_t_p_u_t___f_i_l_e, ----oouuttppuutt=_o_u_t_p_u_t___f_i_l_e
                  Write the converted output to _o_u_t_p_u_t___f_i_l_e.  If no _o_u_t_p_u_t___f_i_l_e
                  is specified, or if it is `-', the converted _s_u_d_o_e_r_s policy
diff --git a/doc/cvtsudoers.man.in b/doc/cvtsudoers.man.in
index f08e7e328..1cff19d31 100644
--- a/doc/cvtsudoers.man.in
+++ b/doc/cvtsudoers.man.in
@@ -25,7 +25,7 @@
 .SH "SYNOPSIS"
 .HP 11n
 \fBcvtsudoers\fR
-[\fB\-ehV\fR]
+[\fB\-ehMV\fR]
 [\fB\-b\fR\ \fIdn\fR]
 [\fB\-c\fR\ \fIconf_file\fR]
 [\fB\-f\fR\ \fIoutput_format\fR]
@@ -191,6 +191,21 @@ on the local system.
 Only aliases that are referenced by the filtered policy rules will
 be displayed.
 .TP 12n
+\fB\-M\fR, \fB\--match-local\fR
+When the
+\fB\-m\fR
+option is also specified, use password and group database information
+when matching users and groups in the filter.
+Only users and groups in the filter that exist on the local system will match,
+and a user's groups will automatically be added to the filter.
+If the
+\fB\-M\fR
+is
+\fInot\fR
+specified, users and groups in the filter do not need to exist on the
+local system, but all groups used for matching must be explicitly listed
+in the filter.
+.TP 12n
 \fB\-o\fR \fIoutput_file\fR, \fB\--output\fR=\fIoutput_file\fR
 Write the converted output to
 \fIoutput_file\fR.
diff --git a/doc/cvtsudoers.mdoc.in b/doc/cvtsudoers.mdoc.in
index ce1e5630e..2792e1c72 100644
--- a/doc/cvtsudoers.mdoc.in
+++ b/doc/cvtsudoers.mdoc.in
@@ -22,7 +22,7 @@
 .Nd convert between sudoers file formats
 .Sh SYNOPSIS
 .Nm cvtsudoers
-.Op Fl ehV
+.Op Fl ehMV
 .Op Fl b Ar dn
 .Op Fl c Ar conf_file
 .Op Fl f Ar output_format
@@ -155,6 +155,20 @@ against the filter so the users and groups do not need to be present
 on the local system.
 Only aliases that are referenced by the filtered policy rules will
 be displayed.
+.It Fl M , Fl -match-local
+When the
+.Fl m
+option is also specified, use password and group database information
+when matching users and groups in the filter.
+Only users and groups in the filter that exist on the local system will match,
+and a user's groups will automatically be added to the filter.
+If the
+.Fl M
+is
+.Em not
+specified, users and groups in the filter do not need to exist on the
+local system, but all groups used for matching must be explicitly listed
+in the filter.
 .It Fl o Ar output_file , Fl -output Ns = Ns Ar output_file
 Write the converted output to
 .Ar output_file .
diff --git a/plugins/sudoers/cvtsudoers.c b/plugins/sudoers/cvtsudoers.c
index 4cd556252..7ad26bbe3 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:ef:hi:I:m:o:O:s:V";
+static const char short_opts[] =  "b:c:ef:hi:I:m:Mo:O:s:V";
 static struct option long_opts[] = {
     { "base",		required_argument,	NULL,	'b' },
     { "config",		required_argument,	NULL,	'c' },
@@ -66,6 +66,7 @@ static struct option long_opts[] = {
     { "input-format",	required_argument,	NULL,	'i' },
     { "increment",	required_argument,	NULL,	'I' },
     { "match",		required_argument,	NULL,	'm' },
+    { "match-local",	no_argument,		NULL,	'M' },
     { "order-start",	required_argument,	NULL,	'O' },
     { "output",		required_argument,	NULL,	'o' },
     { "suppress",	required_argument,	NULL,	's' },
@@ -93,6 +94,7 @@ main(int argc, char *argv[])
     enum sudoers_formats output_format = format_ldif;
     enum sudoers_formats input_format = format_sudoers;
     struct cvtsudoers_config *conf = NULL;
+    bool match_local = false;
     const char *input_file = "-";
     const char *output_file = "-";
     const char *conf_file = _PATH_CVTSUDOERS_CONF;
@@ -187,6 +189,9 @@ main(int argc, char *argv[])
 	case 'm':
 	    conf->filter = optarg;
 	    break;
+	case 'M':
+	    match_local = true;
+	    break;
 	case 'o':
 	    output_file = optarg;
 	    break;
@@ -272,6 +277,12 @@ main(int argc, char *argv[])
 	}
     }
 
+    /* Set pwutil backend to use the filter data. */
+    if (conf->filter != NULL && !match_local) {
+	sudo_pwutil_set_backend(cvtsudoers_make_pwitem, cvtsudoers_make_gritem,
+	    cvtsudoers_make_gidlist_item, cvtsudoers_make_grlist_item);
+    }
+
     /* We may need the hostname to resolve %h escapes in include files. */
     get_hostname();
 
@@ -610,7 +621,7 @@ open_sudoers(const char *sudoers, bool doedit, bool *keepopen)
     return fopen(sudoers, "r");
 }
 
-bool
+static bool
 userlist_matches_filter(struct member_list *userlist)
 {
     struct cvtsudoers_string *s;
@@ -658,7 +669,7 @@ userlist_matches_filter(struct member_list *userlist)
     debug_return_bool(matches);
 }
 
-bool
+static bool
 hostlist_matches_filter(struct member_list *hostlist)
 {
     struct cvtsudoers_string *s;
@@ -1011,7 +1022,7 @@ done:
 static void
 usage(int fatal)
 {
-    (void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehV] [-b dn] "
+    (void) fprintf(fatal ? stderr : stdout, "usage: %s [-ehMV] [-b dn] "
 	"[-c conf_file ] [-f output_format] [-i input_format] [-I increment] "
 	"[-m filter] [-o output_file] [-O start_point] [-s sections] "
 	"[input_file]\n", getprogname());
@@ -1031,7 +1042,8 @@ help(void)
 	"  -i, --input-format=format  set input format: LDIF or sudoers\n"
 	"  -I, --increment=num        amount to increase each sudoOrder by\n"
 	"  -h, --help                 display help message and exit\n"
-	"  -m, --match=filter         only convert entries that match the filter expression\n"
+	"  -m, --match=filter         only convert entries that match the filter\n"
+	"  -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"
 	"  -s, --suppress=sections    suppress output of certain sections\n"
diff --git a/plugins/sudoers/cvtsudoers.h b/plugins/sudoers/cvtsudoers.h
index c3ff522ee..4ec4f3bf1 100644
--- a/plugins/sudoers/cvtsudoers.h
+++ b/plugins/sudoers/cvtsudoers.h
@@ -74,21 +74,25 @@ struct cvtsudoers_filter {
     struct cvtsudoers_str_list hosts;
 };
 
-bool convert_sudoers_json(const char *output_file, struct cvtsudoers_config *conf);
-bool convert_sudoers_ldif(const char *output_file, struct cvtsudoers_config *conf);
-bool parse_ldif(const char *input_file, struct cvtsudoers_config *conf);
-void get_hostname(void);
-
-struct member_list;
-struct userspec_list;
-bool userlist_matches_filter(struct member_list *userlist);
-bool hostlist_matches_filter(struct member_list *hostlist);
-
+/* cvtsudoers.c */
+extern struct cvtsudoers_filter *filters;
 struct cvtsudoers_str_list *str_list_alloc(void);
 void str_list_free(void *v);
 struct cvtsudoers_string *cvtsudoers_string_alloc(const char *s);
 void cvtsudoers_string_free(struct cvtsudoers_string *ls);
 
-extern struct cvtsudoers_filter *filters;
+/* cvtsudoers_json.c */
+bool convert_sudoers_json(const char *output_file, struct cvtsudoers_config *conf);
+
+/* cvtsudoers_ldif.c */
+bool convert_sudoers_ldif(const char *output_file, struct cvtsudoers_config *conf);
+bool parse_ldif(const char *input_file, struct cvtsudoers_config *conf);
+void get_hostname(void);
+
+/* cvtsudoers_pwutil.c */
+struct cache_item *cvtsudoers_make_pwitem(uid_t uid, const char *name);
+struct cache_item *cvtsudoers_make_gritem(gid_t gid, const char *name);
+struct cache_item *cvtsudoers_make_gidlist_item(const struct passwd *pw, char * const *unused1, unsigned int type);
+struct cache_item *cvtsudoers_make_grlist_item(const struct passwd *pw, char * const *unused1);
 
 #endif /* SUDOERS_CVTSUDOERS_H */
diff --git a/plugins/sudoers/cvtsudoers_pwutil.c b/plugins/sudoers/cvtsudoers_pwutil.c
index 4f8de07b5..9314b22c4 100644
--- a/plugins/sudoers/cvtsudoers_pwutil.c
+++ b/plugins/sudoers/cvtsudoers_pwutil.c
@@ -73,7 +73,7 @@ do {							\
  * to ENOMEM or ENOENT respectively.
  */
 struct cache_item *
-sudo_make_pwitem(uid_t uid, const char *name)
+cvtsudoers_make_pwitem(uid_t uid, const char *name)
 {
     char *cp, uidstr[MAX_UID_T_LEN + 2];
     size_t nsize, psize, csize, gsize, dsize, ssize, total;
@@ -179,7 +179,7 @@ sudo_make_pwitem(uid_t uid, const char *name)
  * to ENOMEM or ENOENT respectively.
  */
 struct cache_item *
-sudo_make_gritem(gid_t gid, const char *name)
+cvtsudoers_make_gritem(gid_t gid, const char *name)
 {
     char *cp, gidstr[MAX_UID_T_LEN + 2];
     size_t nsize, psize, nmem, total, len;
@@ -292,7 +292,7 @@ static struct cache_item_gidlist *gidlist_item;
  * elements.  Fills in datum from user_gids or from getgrouplist(3).
  */
 struct cache_item *
-sudo_make_gidlist_item(const struct passwd *pw, char * const *unused1,
+cvtsudoers_make_gidlist_item(const struct passwd *pw, char * const *unused1,
     unsigned int type)
 {
     char *cp;
@@ -391,7 +391,7 @@ static struct cache_item_gidlist *grlist_item;
  * elements.  Fills in group names from a call to sudo_get_gidlist().
  */
 struct cache_item *
-sudo_make_grlist_item(const struct passwd *pw, char * const *unused1)
+cvtsudoers_make_grlist_item(const struct passwd *pw, char * const *unused1)
 {
     char *cp;
     size_t nsize, ngroups, total, len;
diff --git a/plugins/sudoers/pwutil.c b/plugins/sudoers/pwutil.c
index 26235812a..e170c31e5 100644
--- a/plugins/sudoers/pwutil.c
+++ b/plugins/sudoers/pwutil.c
@@ -54,6 +54,14 @@ static int  cmp_pwuid(const void *, const void *);
 static int  cmp_pwnam(const void *, const void *);
 static int  cmp_grgid(const void *, const void *);
 
+/*
+ * Default functions for building cache items.
+ */
+static sudo_make_pwitem_t make_pwitem = sudo_make_pwitem;
+static sudo_make_gritem_t make_gritem = sudo_make_gritem;
+static sudo_make_gidlist_item_t make_gidlist_item = sudo_make_gidlist_item;
+static sudo_make_grlist_item_t make_grlist_item = sudo_make_grlist_item;
+
 #define cmp_grnam	cmp_pwnam
 
 /*
@@ -68,6 +76,24 @@ static int  cmp_grgid(const void *, const void *);
 # define getauthregistry(u, r)	((r)[0] = '\0')
 #endif
 
+/*
+ * Change the default pwutil backend functions.
+ * The default functions query the password and group databases.
+ */
+void
+sudo_pwutil_set_backend(sudo_make_pwitem_t pwitem, sudo_make_gritem_t gritem,
+    sudo_make_gidlist_item_t gidlist_item, sudo_make_grlist_item_t grlist_item)
+{
+    debug_decl(sudo_pwutil_set_backend, SUDOERS_DEBUG_NSS)
+
+    make_pwitem = pwitem;
+    make_gritem = gritem;
+    make_gidlist_item = gidlist_item;
+    make_grlist_item = grlist_item;
+
+    debug_return;
+}
+
 /*
  * Compare by user ID.
  * v1 is the key to find or data to insert, v2 is in-tree data.
@@ -179,7 +205,7 @@ sudo_getpwuid(uid_t uid)
 #ifdef HAVE_SETAUTHDB
     aix_setauthdb(IDtouser(uid), key.registry);
 #endif
-    item = sudo_make_pwitem(uid, NULL);
+    item = make_pwitem(uid, NULL);
 #ifdef HAVE_SETAUTHDB
     aix_restoreauthdb();
 #endif
@@ -250,7 +276,7 @@ sudo_getpwnam(const char *name)
 #ifdef HAVE_SETAUTHDB
     aix_setauthdb((char *) name, key.registry);
 #endif
-    item = sudo_make_pwitem((uid_t)-1, name);
+    item = make_pwitem((uid_t)-1, name);
 #ifdef HAVE_SETAUTHDB
     aix_restoreauthdb();
 #endif
@@ -497,7 +523,7 @@ sudo_getgrgid(gid_t gid)
     /*
      * Cache group db entry if it exists or a negative response if not.
      */
-    item = sudo_make_gritem(gid, NULL);
+    item = make_gritem(gid, NULL);
     if (item == NULL) {
 	if (errno != ENOENT || (item = calloc(1, sizeof(*item))) == NULL) {
 	    sudo_warnx(U_("unable to cache gid %u, out of memory"),
@@ -562,7 +588,7 @@ sudo_getgrnam(const char *name)
     /*
      * Cache group db entry if it exists or a negative response if not.
      */
-    item = sudo_make_gritem((gid_t)-1, name);
+    item = make_gritem((gid_t)-1, name);
     if (item == NULL) {
 	const size_t len = strlen(name) + 1;
 	if (errno != ENOENT || (item = calloc(1, sizeof(*item) + len)) == NULL) {
@@ -789,7 +815,7 @@ sudo_get_grlist(const struct passwd *pw)
     /*
      * Cache group db entry if it exists or a negative response if not.
      */
-    item = sudo_make_grlist_item(pw, NULL);
+    item = make_grlist_item(pw, NULL);
     if (item == NULL) {
 	/* Out of memory? */
 	debug_return_ptr(NULL);
@@ -843,7 +869,7 @@ sudo_set_grlist(struct passwd *pw, char * const *groups)
     key.k.name = pw->pw_name;
     getauthregistry(NULL, key.registry);
     if ((node = rbfind(grlist_cache, &key)) == NULL) {
-	if ((item = sudo_make_grlist_item(pw, groups)) == NULL) {
+	if ((item = make_grlist_item(pw, groups)) == NULL) {
 	    sudo_warnx(U_("unable to parse groups for %s"), pw->pw_name);
 	    debug_return_int(-1);
 	}
@@ -892,7 +918,7 @@ sudo_get_gidlist(const struct passwd *pw, unsigned int type)
     /*
      * Cache group db entry if it exists or a negative response if not.
      */
-    item = sudo_make_gidlist_item(pw, NULL, type);
+    item = make_gidlist_item(pw, NULL, type);
     if (item == NULL) {
 	/* Out of memory? */
 	debug_return_ptr(NULL);
@@ -947,7 +973,7 @@ sudo_set_gidlist(struct passwd *pw, char * const *gids, unsigned int type)
     key.type = type;
     getauthregistry(NULL, key.registry);
     if ((node = rbfind(gidlist_cache, &key)) == NULL) {
-	if ((item = sudo_make_gidlist_item(pw, gids, type)) == NULL) {
+	if ((item = make_gidlist_item(pw, gids, type)) == NULL) {
 	    sudo_warnx(U_("unable to parse gids for %s"), pw->pw_name);
 	    debug_return_int(-1);
 	}
diff --git a/plugins/sudoers/sudoers.h b/plugins/sudoers/sudoers.h
index 0825181f8..a8139f9e0 100644
--- a/plugins/sudoers/sudoers.h
+++ b/plugins/sudoers/sudoers.h
@@ -304,6 +304,10 @@ int display_privs(struct sudo_nss_list *, struct passwd *);
 int display_cmnd(struct sudo_nss_list *, struct passwd *);
 
 /* pwutil.c */
+typedef struct cache_item * (*sudo_make_pwitem_t)(uid_t uid, const char *user);
+typedef struct cache_item * (*sudo_make_gritem_t)(gid_t gid, const char *group);
+typedef struct cache_item * (*sudo_make_gidlist_item_t)(const struct passwd *pw, char * const *gids, unsigned int type);
+typedef struct cache_item * (*sudo_make_grlist_item_t)(const struct passwd *pw, char * const *groups);
 __dso_public struct group *sudo_getgrgid(gid_t);
 __dso_public struct group *sudo_getgrnam(const char *);
 __dso_public void sudo_gr_addref(struct group *);
@@ -327,6 +331,7 @@ void sudo_pw_addref(struct passwd *);
 void sudo_pw_delref(struct passwd *);
 int  sudo_set_gidlist(struct passwd *pw, char * const *gids, unsigned int type);
 int  sudo_set_grlist(struct passwd *pw, char * const *groups);
+void sudo_pwutil_set_backend(sudo_make_pwitem_t, sudo_make_gritem_t, sudo_make_gidlist_item_t, sudo_make_grlist_item_t);
 void sudo_setspent(void);
 
 /* timestr.c */
