diff --git a/configure b/configure
index 9138af37f8..db35d52e12 100755
--- a/configure
+++ b/configure
@@ -463,6 +463,7 @@ nettle_kdf="no"
 gcrypt=""
 gcrypt_hmac="no"
 gcrypt_kdf="no"
+auth_pam=""
 vte=""
 virglrenderer=""
 tpm="yes"
@@ -1361,6 +1362,10 @@ for opt do
   ;;
   --enable-gcrypt) gcrypt="yes"
   ;;
+  --disable-auth-pam) auth_pam="no"
+  ;;
+  --enable-auth-pam) auth_pam="yes"
+  ;;
   --enable-rdma) rdma="yes"
   ;;
   --disable-rdma) rdma="no"
@@ -1653,6 +1658,7 @@ disabled with --disable-FEATURE, default is enabled if available:
   gnutls          GNUTLS cryptography support
   nettle          nettle cryptography support
   gcrypt          libgcrypt cryptography support
+  auth-pam        PAM access control
   sdl             SDL UI
   --with-sdlabi     select preferred SDL ABI 1.2 or 2.0
   gtk             gtk UI
@@ -2876,6 +2882,33 @@ else
 fi
 
 
+##########################################
+# PAM probe
+
+if test "x$auth_pam" != "no"; then
+    cat > $TMPC <<EOF
+#include <security/pam_appl.h>
+#include <stdio.h>
+int main(void) {
+   const char *service_name = "qemu";
+   const char *user = "frank";
+   const struct pam_conv *pam_conv = NULL;
+   pam_handle_t *pamh = NULL;
+   pam_start(service_name, user, pam_conv, &pamh);
+   return 0;
+}
+EOF
+    if compile_prog "" "-lpam" ; then
+	auth_pam=yes
+    else
+	if test "$auth_pam" = "yes"; then
+	    feature_not_found "PAM" "Install PAM development package"
+	else
+	    auth_pam=no
+	fi
+    fi
+fi
+
 ##########################################
 # getifaddrs (for tests/test-io-channel-socket )
 
@@ -5967,6 +6000,7 @@ echo "libgcrypt kdf     $gcrypt_kdf"
 echo "nettle            $nettle $(echo_version $nettle $nettle_version)"
 echo "nettle kdf        $nettle_kdf"
 echo "libtasn1          $tasn1"
+echo "PAM               $auth_pam"
 echo "curses support    $curses"
 echo "virgl support     $virglrenderer $(echo_version $virglrenderer $virgl_version)"
 echo "curl support      $curl"
@@ -6423,6 +6457,9 @@ fi
 if test "$tasn1" = "yes" ; then
   echo "CONFIG_TASN1=y" >> $config_host_mak
 fi
+if test "$auth_pam" = "yes" ; then
+    echo "CONFIG_AUTH_PAM=y" >> $config_host_mak
+fi
 if test "$have_ifaddrs_h" = "yes" ; then
     echo "HAVE_IFADDRS_H=y" >> $config_host_mak
 fi
diff --git a/include/authz/pamacct.h b/include/authz/pamacct.h
new file mode 100644
index 0000000000..d2c0149153
--- /dev/null
+++ b/include/authz/pamacct.h
@@ -0,0 +1,100 @@
+/*
+ * QEMU PAM authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef QAUTHZ_PAM_H__
+#define QAUTHZ_PAM_H__
+
+#include "authz/base.h"
+
+
+#define TYPE_QAUTHZ_PAM "authz-pam"
+
+#define QAUTHZ_PAM_CLASS(klass) \
+     OBJECT_CLASS_CHECK(QAuthZPamClass, (klass), \
+                        TYPE_QAUTHZ_PAM)
+#define QAUTHZ_PAM_GET_CLASS(obj) \
+     OBJECT_GET_CLASS(QAuthZPamClass, (obj), \
+                      TYPE_QAUTHZ_PAM)
+#define QAUTHZ_PAM(obj) \
+     INTERFACE_CHECK(QAuthZPam, (obj), \
+                     TYPE_QAUTHZ_PAM)
+
+typedef struct QAuthZPam QAuthZPam;
+typedef struct QAuthZPamClass QAuthZPamClass;
+
+
+/**
+ * QAuthZPam:
+ *
+ * This authorization driver provides a PAM mechanism
+ * for granting access by matching user names against a
+ * list of globs. Each match rule has an associated policy
+ * and a catch all policy applies if no rule matches
+ *
+ * To create an instance of this class via QMP:
+ *
+ *  {
+ *    "execute": "object-add",
+ *    "arguments": {
+ *      "qom-type": "authz-pam",
+ *      "id": "authz0",
+ *      "parameters": {
+ *        "service": "qemu-vnc-tls"
+ *      }
+ *    }
+ *  }
+ *
+ * The driver only uses the PAM "account" verification
+ * subsystem. The above config would require a config
+ * file /etc/pam.d/qemu-vnc-tls. For a simple file
+ * lookup it would contain
+ *
+ *   account requisite  pam_listfile.so item=user sense=allow \
+ *           file=/etc/qemu/vnc.allow
+ *
+ * The external file would then contain a list of usernames.
+ * If x509 cert was being used as the username, a suitable
+ * entry would match the distinguish name:
+ *
+ *  CN=laptop.berrange.com,O=Berrange Home,L=London,ST=London,C=GB
+ *
+ * On the command line it can be created using
+ *
+ *   -object authz-pam,id=authz0,service=qemu-vnc-tls
+ *
+ */
+struct QAuthZPam {
+    QAuthZ parent_obj;
+
+    char *service;
+};
+
+
+struct QAuthZPamClass {
+    QAuthZClass parent_class;
+};
+
+
+QAuthZPam *qauthz_pam_new(const char *id,
+                          const char *service,
+                          Error **errp);
+
+
+#endif /* QAUTHZ_PAM_H__ */
diff --git a/authz/pamacct.c b/authz/pamacct.c
new file mode 100644
index 0000000000..a070dda217
--- /dev/null
+++ b/authz/pamacct.c
@@ -0,0 +1,149 @@
+/*
+ * QEMU PAM authorization driver
+ *
+ * Copyright (c) 2018 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "authz/pamacct.h"
+#include "authz/trace.h"
+#include "qom/object_interfaces.h"
+
+#include <security/pam_appl.h>
+
+
+static bool qauthz_pam_is_allowed(QAuthZ *authz,
+                                  const char *identity,
+                                  Error **errp)
+{
+    QAuthZPam *pauthz = QAUTHZ_PAM(authz);
+    const struct pam_conv pam_conversation = { 0 };
+    pam_handle_t *pamh = NULL;
+    int ret;
+
+    trace_qauthz_pam_check(authz, identity, pauthz->service);
+    ret = pam_start(pauthz->service,
+                    identity,
+                    &pam_conversation,
+                    &pamh);
+    if (ret != PAM_SUCCESS) {
+        error_setg(errp, "Unable to start PAM transaction: %s",
+                   pam_strerror(NULL, ret));
+        return false;
+    }
+
+    ret = pam_acct_mgmt(pamh, PAM_SILENT);
+    if (ret != PAM_SUCCESS) {
+        error_setg(errp, "Unable to authorize user '%s': %s",
+                   identity, pam_strerror(pamh, ret));
+        goto cleanup;
+    }
+
+ cleanup:
+    pam_end(pamh, ret);
+    return ret == PAM_SUCCESS;
+}
+
+
+static void
+qauthz_pam_prop_set_service(Object *obj,
+                            const char *service,
+                            Error **errp G_GNUC_UNUSED)
+{
+    QAuthZPam *pauthz = QAUTHZ_PAM(obj);
+
+    g_free(pauthz->service);
+    pauthz->service = g_strdup(service);
+}
+
+
+static char *
+qauthz_pam_prop_get_service(Object *obj,
+                            Error **errp G_GNUC_UNUSED)
+{
+    QAuthZPam *pauthz = QAUTHZ_PAM(obj);
+
+    return g_strdup(pauthz->service);
+}
+
+
+static void
+qauthz_pam_complete(UserCreatable *uc, Error **errp)
+{
+}
+
+
+static void
+qauthz_pam_finalize(Object *obj)
+{
+    QAuthZPam *pauthz = QAUTHZ_PAM(obj);
+
+    g_free(pauthz->service);
+}
+
+
+static void
+qauthz_pam_class_init(ObjectClass *oc, void *data)
+{
+    UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
+    QAuthZClass *authz = QAUTHZ_CLASS(oc);
+
+    ucc->complete = qauthz_pam_complete;
+    authz->is_allowed = qauthz_pam_is_allowed;
+
+    object_class_property_add_str(oc, "service",
+                                  qauthz_pam_prop_get_service,
+                                  qauthz_pam_prop_set_service,
+                                  NULL);
+}
+
+
+QAuthZPam *qauthz_pam_new(const char *id,
+                          const char *service,
+                          Error **errp)
+{
+    return QAUTHZ_PAM(
+        object_new_with_props(TYPE_QAUTHZ_PAM,
+                              object_get_objects_root(),
+                              id, errp,
+                              "service", service,
+                              NULL));
+}
+
+
+static const TypeInfo qauthz_pam_info = {
+    .parent = TYPE_QAUTHZ,
+    .name = TYPE_QAUTHZ_PAM,
+    .instance_size = sizeof(QAuthZPam),
+    .instance_finalize = qauthz_pam_finalize,
+    .class_size = sizeof(QAuthZPamClass),
+    .class_init = qauthz_pam_class_init,
+    .interfaces = (InterfaceInfo[]) {
+        { TYPE_USER_CREATABLE },
+        { }
+    }
+};
+
+
+static void
+qauthz_pam_register_types(void)
+{
+    type_register_static(&qauthz_pam_info);
+}
+
+
+type_init(qauthz_pam_register_types);
diff --git a/authz/Makefile.objs b/authz/Makefile.objs
index 8351bf181d..ed7b273596 100644
--- a/authz/Makefile.objs
+++ b/authz/Makefile.objs
@@ -2,3 +2,6 @@ authz-obj-y += base.o
 authz-obj-y += simple.o
 authz-obj-y += list.o
 authz-obj-y += listfile.o
+authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o
+
+pamacct.o-libs = -lpam
diff --git a/authz/trace-events b/authz/trace-events
index fb65349a90..72c411927d 100644
--- a/authz/trace-events
+++ b/authz/trace-events
@@ -13,3 +13,6 @@ qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ
 # auth/listfile.c
 qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s"
 qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d"
+
+# auth/pam.c
+qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s"
diff --git a/qemu-options.hx b/qemu-options.hx
index a1c3e0e59c..a9654b8115 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4447,6 +4447,41 @@ would look like:
      ...
 @end example
 
+@item -object authz-pam,id=@var{id},service=@var{string}
+
+Create an authorization object that will control access to network services.
+
+The @option{service} parameter provides the name of a PAM service to use
+for authorization. It requires that a file @code{/etc/pam.d/@var{service}}
+exist to provide the configuration for the @code{account} subsystem.
+
+An example authorization object to validate a TLS x509 distinguished
+name would look like:
+
+@example
+ # $QEMU \
+     ...
+     -object authz-simple,id=auth0,service=qemu-vnc
+     ...
+@end example
+
+There would then be a corresponding config file for PAM at
+@code{/etc/pam.d/qemu-vnc} that contains:
+
+@example
+account requisite  pam_listfile.so item=user sense=allow \
+           file=/etc/qemu/vnc.allow
+@end example
+
+Finally the @code{/etc/qemu/vnc.allow} file would contain
+the list of x509 distingished names that are permitted
+access
+
+@example
+CN=laptop.example.com,O=Example Home,L=London,ST=London,C=GB
+@end example
+
+
 @end table
 
 ETEXI
