diff --git a/SOURCES/0112-TESTS-Extend-the-schema-with-sshPublicKey-attribute.patch b/SOURCES/0112-TESTS-Extend-the-schema-with-sshPublicKey-attribute.patch
new file mode 100644
index 0000000..5d2d383
--- /dev/null
+++ b/SOURCES/0112-TESTS-Extend-the-schema-with-sshPublicKey-attribute.patch
@@ -0,0 +1,62 @@
+From c981d4f4a40ac6cb3650ae1934b0931b0ea5b6f6 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Sun, 17 Jun 2018 21:48:36 +0200
+Subject: [PATCH] TESTS: Extend the schema with sshPublicKey attribute
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This will allow to store the users with a sshPublicKey attribute
+provided that they have the right objectclass as well.
+
+Related to:
+https://pagure.io/SSSD/sssd/issue/3747
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 1575ec97e080656f69b3f93e641c76e74ffb8182)
+
+DOWNSTREAM:
+Resolves: rhbz#1594178 - Login with sshkeys stored in ipa not working after update to RHEL-7.5 [rhel-7.5.z]
+---
+ src/tests/intg/data/ssh_schema.ldif | 11 +++++++++++
+ src/tests/intg/ds_openldap.py       |  6 ++++++
+ 2 files changed, 17 insertions(+)
+ create mode 100644 src/tests/intg/data/ssh_schema.ldif
+
+diff --git a/src/tests/intg/data/ssh_schema.ldif b/src/tests/intg/data/ssh_schema.ldif
+new file mode 100644
+index 0000000000000000000000000000000000000000..efe05706b9ded5614a7f3f5e0bab28a7eb869daa
+--- /dev/null
++++ b/src/tests/intg/data/ssh_schema.ldif
+@@ -0,0 +1,11 @@
++dn: cn=openssh-lpk,cn=schema,cn=config
++objectClass: olcSchemaConfig
++cn: openssh-lpk
++olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey'
++  DESC 'MANDATORY: OpenSSH Public key'
++  EQUALITY octetStringMatch
++  SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 )
++olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY
++  DESC 'MANDATORY: OpenSSH LPK objectclass'
++  MAY ( sshPublicKey $ uid )
++  )
+diff --git a/src/tests/intg/ds_openldap.py b/src/tests/intg/ds_openldap.py
+index 842ff910803658834841c8f9181f3c4af29b955a..c9a4b6de8c53c6644b3de9047d657ee35ce06512 100644
+--- a/src/tests/intg/ds_openldap.py
++++ b/src/tests/intg/ds_openldap.py
+@@ -186,6 +186,12 @@ class DSOpenLDAP(DS):
+         db_config_file.write(db_config)
+         db_config_file.close()
+ 
++        # Import ad schema
++        subprocess.check_call(
++            ["slapadd", "-F", self.conf_slapd_d_dir, "-b", "cn=config",
++             "-l", "data/ssh_schema.ldif"],
++        )
++
+     def _start_daemon(self):
+         """Start the instance."""
+         if subprocess.call(["slapd", "-F", self.conf_slapd_d_dir,
+-- 
+2.14.4
+
diff --git a/SOURCES/0113-TESTS-Allow-adding-sshPublicKey-for-users.patch b/SOURCES/0113-TESTS-Allow-adding-sshPublicKey-for-users.patch
new file mode 100644
index 0000000..630e1b0
--- /dev/null
+++ b/SOURCES/0113-TESTS-Allow-adding-sshPublicKey-for-users.patch
@@ -0,0 +1,78 @@
+From db4a80c1e798872d4b1196ef9a768b35e7962d28 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Sun, 17 Jun 2018 22:06:22 +0200
+Subject: [PATCH] TESTS: Allow adding sshPublicKey for users
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adds the objectclass and allows storing a list of sshPublicKey
+attributes for users. Since there is no harm in adding the extra
+objectclass, we can do it unconditionally.
+
+Related to:
+https://pagure.io/SSSD/sssd/issue/3747
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 56cda832e9f61c52e9cfde1f0864507de718ffbb)
+---
+ src/tests/intg/ldap_ent.py | 15 +++++++++++----
+ 1 file changed, 11 insertions(+), 4 deletions(-)
+
+diff --git a/src/tests/intg/ldap_ent.py b/src/tests/intg/ldap_ent.py
+index 6b6d8f903cbcc277d892c3212ca382f4aaadc671..a4c987969d3dcefba2af69e095b220180e0fa54c 100644
+--- a/src/tests/intg/ldap_ent.py
++++ b/src/tests/intg/ldap_ent.py
+@@ -24,7 +24,8 @@ def user(base_dn, uid, uidNumber, gidNumber,
+          homeDirectory=None,
+          loginShell=None,
+          cn=None,
+-         sn=None):
++         sn=None,
++         sshPubKey=()):
+     """
+     Generate an RFC2307(bis) user add-modlist for passing to ldap.add*
+     """
+@@ -33,7 +34,8 @@ def user(base_dn, uid, uidNumber, gidNumber,
+     user = (
+         "uid=" + uid + ",ou=Users," + base_dn,
+         [
+-            ('objectClass', [b'top', b'inetOrgPerson', b'posixAccount']),
++            ('objectClass', [b'top', b'inetOrgPerson',
++                             b'posixAccount', b'ldapPublicKey']),
+             ('cn', [uidNumber if cn is None else cn.encode('utf-8')]),
+             ('sn', [b'User' if sn is None else sn.encode('utf-8')]),
+             ('uidNumber', [uidNumber]),
+@@ -51,6 +53,9 @@ def user(base_dn, uid, uidNumber, gidNumber,
+     )
+     if gecos is not None:
+         user[1].append(('gecos', [gecos.encode('utf-8')]))
++    if len(sshPubKey) > 0:
++        pubkeys = [key.encode('utf-8') for key in sshPubKey]
++        user[1].append(('sshPublicKey', pubkeys))
+     return user
+ 
+ 
+@@ -118,7 +123,8 @@ class List(list):
+                  homeDirectory=None,
+                  loginShell=None,
+                  cn=None,
+-                 sn=None):
++                 sn=None,
++                 sshPubKey=()):
+         """Add an RFC2307(bis) user add-modlist."""
+         self.append(user(base_dn or self.base_dn,
+                          uid, uidNumber, gidNumber,
+@@ -127,7 +133,8 @@ class List(list):
+                          homeDirectory=homeDirectory,
+                          loginShell=loginShell,
+                          cn=cn,
+-                         sn=sn))
++                         sn=sn,
++                         sshPubKey=sshPubKey))
+ 
+     def add_group(self, cn, gidNumber, member_uids=[],
+                   base_dn=None):
+-- 
+2.14.4
+
diff --git a/SOURCES/0114-TESTS-Add-a-basic-SSH-responder-test.patch b/SOURCES/0114-TESTS-Add-a-basic-SSH-responder-test.patch
new file mode 100644
index 0000000..8dbcf01
--- /dev/null
+++ b/SOURCES/0114-TESTS-Add-a-basic-SSH-responder-test.patch
@@ -0,0 +1,276 @@
+From 612dda4bbd706be9e7c3674e4d0420f9ebd1ea83 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 18 Jun 2018 09:12:13 +0200
+Subject: [PATCH] TESTS: Add a basic SSH responder test
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adds a basic test that makes sure that a list of SSH public keys can be
+retrieved. This is to make sure we don't break the SSH integration later
+on.
+
+Related:
+https://pagure.io/SSSD/sssd/issue/3747
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 804c5b538ad89a1a3897b93f39d716fa50530842)
+---
+ src/tests/intg/Makefile.am        |   1 +
+ src/tests/intg/test_ssh_pubkey.py | 232 ++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 233 insertions(+)
+ create mode 100644 src/tests/intg/test_ssh_pubkey.py
+
+diff --git a/src/tests/intg/Makefile.am b/src/tests/intg/Makefile.am
+index 209e5a0c73db99b11aa6967f30db23933d21acb0..285ce21f04dddb6388c595470eac8f31bc224a60 100644
+--- a/src/tests/intg/Makefile.am
++++ b/src/tests/intg/Makefile.am
+@@ -35,6 +35,7 @@ dist_noinst_DATA = \
+     data/ad_data.ldif \
+     data/ad_schema.ldif \
+     test_pysss_nss_idmap.py \
++    test_ssh_pubkey.py \
+     $(NULL)
+ 
+ config.py: config.py.m4
+diff --git a/src/tests/intg/test_ssh_pubkey.py b/src/tests/intg/test_ssh_pubkey.py
+new file mode 100644
+index 0000000000000000000000000000000000000000..fbf55566e341373873057ec4e3af1d7f83202aa7
+--- /dev/null
++++ b/src/tests/intg/test_ssh_pubkey.py
+@@ -0,0 +1,232 @@
++#
++# ssh public key integration test
++#
++# Copyright (c) 2018 Red Hat, Inc.
++#
++# This is free software; you can redistribute it and/or modify it
++# under the terms of the GNU General Public License as published by
++# the Free Software Foundation; version 2 only
++#
++# This program 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
++# General Public License for more details.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program.  If not, see <http://www.gnu.org/licenses/>.
++#
++
++import os
++import stat
++import signal
++import subprocess
++import time
++import ldap
++import ldap.modlist
++import pytest
++
++import config
++import ds_openldap
++import ent
++import ldap_ent
++from util import unindent, get_call_output
++
++LDAP_BASE_DN = "dc=example,dc=com"
++
++USER1_PUBKEY1 = "ssh-dss AAAAB3NzaC1kc3MAAACBAPMkvcU53RVhBtjwiC3IqeRIWR9Qwdv8\
++DmZzEsDD3Csd6jYxMsPZoXcPrHqwYcEj1s5MVqhdSFS0Cjz13e7gO6OMLInO3xMBSSFHjfp9RE1H\
++pgc4WisazzyJaW9EMkQo/DqvkFkKh31oqAmxcSbLAFJRg4TTIqm18qu8IRKS6m/RAAAAFQC97TA5\
++JSsMsaX1bRszC7y4PhMBvQAAAIEAt9Yo9v/h9W4nDbzUdkGwNRszlPEK+T12bJv0O9Fk6subD3Do\
++6A4Qru/Nr6voXoq8b018Wb7iFWvKOoz5uT/plWBKLXL2NN7ovTR+dUJIzvwurQZroukmU1EghNey\
++lkSHmDlxSoMK6Nh21uGu6l+b6x5pXNaZHMpsywG4kY8SoC0AAACAAWLHneEGvqkYA8La4Eob+Hjj\
++mAKilx8byxm3Kfb1XO+ZrR6XxadofZOaUYRMpPKgFjKAKPxJftPLiDjWM7lSe6h8df0dUMLVXt6m\
++eA83kE0uK5JOOGJfJDqmRed2YnfxUDNNFQGT4xFWGrNtYNbGyw9BWKbkooAsLqaO04zP3Rs= \
++user1@LDAP"
++
++USER1_PUBKEY2 = "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAwHUUF3HPH+DkU6j8k7Q1wHG\
++RJY9NeLqSav3h95mTSCQYPSC7I9RTJ4OORgqCbEzrP/DYrrn4TtQ9dhRJar3ZY+F36SH5yFIXORb\
++lAIbFU+/anahBuFS9vHi1MqFPckGmwJ4QCpjQhdYxo1ro0e1RuGSaQNp/w9N6S/fDz4Cj4I99xDz\
++SeQeGHxYv0e60plQ8dUajmnaGmYRJHF9a6Ban7IWySActCja7eQP2zIRXEZMpuhl1E0U4y+gHTFI\
++gD3zQai3QrXm8RUrQURIJ0u6BlGS910OPbHqLpLTFWG08L8sNUcYzC+DY6yoCSO0n/Df3pVRS4C9\
++5Krf3FqppMTjdfQ== user1@LDAP"
++
++
++@pytest.fixture(scope="module")
++def ds_inst(request):
++    """LDAP server instance fixture"""
++    ds_inst = ds_openldap.DSOpenLDAP(
++        config.PREFIX, 10389, LDAP_BASE_DN,
++        "cn=admin", "Secret123"
++    )
++
++    try:
++        ds_inst.setup()
++    except:
++        ds_inst.teardown()
++        raise
++    request.addfinalizer(ds_inst.teardown)
++    return ds_inst
++
++
++@pytest.fixture(scope="module")
++def ldap_conn(request, ds_inst):
++    """LDAP server connection fixture"""
++    ldap_conn = ds_inst.bind()
++    ldap_conn.ds_inst = ds_inst
++    request.addfinalizer(ldap_conn.unbind_s)
++    return ldap_conn
++
++
++def create_ldap_entries(ldap_conn, ent_list=None):
++    """Add LDAP entries from ent_list"""
++    if ent_list is not None:
++        for entry in ent_list:
++            ldap_conn.add_s(entry[0], entry[1])
++
++
++def cleanup_ldap_entries(ldap_conn, ent_list=None):
++    """Remove LDAP entries added by create_ldap_entries"""
++    if ent_list is None:
++        for ou in ("Users", "Groups", "Netgroups", "Services", "Policies"):
++            for entry in ldap_conn.search_s("ou=" + ou + "," +
++                                            ldap_conn.ds_inst.base_dn,
++                                            ldap.SCOPE_ONELEVEL,
++                                            attrlist=[]):
++                ldap_conn.delete_s(entry[0])
++    else:
++        for entry in ent_list:
++            ldap_conn.delete_s(entry[0])
++
++
++def create_ldap_cleanup(request, ldap_conn, ent_list=None):
++    """Add teardown for removing all user/group LDAP entries"""
++    request.addfinalizer(lambda: cleanup_ldap_entries(ldap_conn, ent_list))
++
++
++def create_ldap_fixture(request, ldap_conn, ent_list=None):
++    """Add LDAP entries and add teardown for removing them"""
++    create_ldap_entries(ldap_conn, ent_list)
++    create_ldap_cleanup(request, ldap_conn, ent_list)
++
++
++SCHEMA_RFC2307_BIS = "rfc2307bis"
++
++
++def format_basic_conf(ldap_conn, schema):
++    """Format a basic SSSD configuration"""
++    schema_conf = "ldap_schema         = " + schema + "\n"
++    schema_conf += "ldap_group_object_class = groupOfNames\n"
++    return unindent("""\
++        [sssd]
++        domains             = LDAP
++        services            = nss, ssh
++
++        [nss]
++
++        [ssh]
++        debug_level=10
++
++        [domain/LDAP]
++        {schema_conf}
++        id_provider         = ldap
++        auth_provider       = ldap
++        ldap_uri            = {ldap_conn.ds_inst.ldap_url}
++        ldap_search_base    = {ldap_conn.ds_inst.base_dn}
++        ldap_sudo_use_host_filter = false
++        debug_level=10
++    """).format(**locals())
++
++
++def create_conf_file(contents):
++    """Create sssd.conf with specified contents"""
++    conf = open(config.CONF_PATH, "w")
++    conf.write(contents)
++    conf.close()
++    os.chmod(config.CONF_PATH, stat.S_IRUSR | stat.S_IWUSR)
++
++
++def cleanup_conf_file():
++    """Remove sssd.conf, if it exists"""
++    if os.path.lexists(config.CONF_PATH):
++        os.unlink(config.CONF_PATH)
++
++
++def create_conf_cleanup(request):
++    """Add teardown for removing sssd.conf"""
++    request.addfinalizer(cleanup_conf_file)
++
++
++def create_conf_fixture(request, contents):
++    """
++    Create sssd.conf with specified contents and add teardown for removing it
++    """
++    create_conf_file(contents)
++    create_conf_cleanup(request)
++
++
++def create_sssd_process():
++    """Start the SSSD process"""
++    if subprocess.call(["sssd", "-D", "-f"]) != 0:
++        raise Exception("sssd start failed")
++
++
++def get_sssd_pid():
++    pid_file = open(config.PIDFILE_PATH, "r")
++    pid = int(pid_file.read())
++    return pid
++
++
++def cleanup_sssd_process():
++    """Stop the SSSD process and remove its state"""
++    try:
++        pid = get_sssd_pid()
++        os.kill(pid, signal.SIGTERM)
++        while True:
++            try:
++                os.kill(pid, signal.SIGCONT)
++            except:
++                break
++            time.sleep(1)
++    except:
++        pass
++    for path in os.listdir(config.DB_PATH):
++        os.unlink(config.DB_PATH + "/" + path)
++    for path in os.listdir(config.MCACHE_PATH):
++        os.unlink(config.MCACHE_PATH + "/" + path)
++
++
++def create_sssd_fixture(request):
++    """Start SSSD and add teardown for stopping it and removing its state"""
++    create_sssd_process()
++    create_sssd_cleanup(request)
++
++
++def create_sssd_cleanup(request):
++    """Add teardown for stopping SSSD and removing its state"""
++    request.addfinalizer(cleanup_sssd_process)
++
++
++@pytest.fixture
++def add_user_with_ssh_key(request, ldap_conn):
++    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
++    ent_list.add_user("user1", 1001, 2001,
++                      sshPubKey=(USER1_PUBKEY1, USER1_PUBKEY2))
++    ent_list.add_user("user2", 1002, 2001)
++    create_ldap_fixture(request, ldap_conn, ent_list)
++
++    conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS)
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
++def test_ssh_pubkey_retrieve(add_user_with_ssh_key):
++    """
++    Test that we can retrieve an SSH public key for a user who has one
++    and can't retrieve a key for a user who does not have one.
++    """
++    sshpubkey = get_call_output(["sss_ssh_authorizedkeys", "user1"])
++    assert sshpubkey == USER1_PUBKEY1 + '\n' + USER1_PUBKEY2 + '\n'
++
++    sshpubkey = get_call_output(["sss_ssh_authorizedkeys", "user2"])
++    assert len(sshpubkey) == 0
+-- 
+2.14.4
+
diff --git a/SOURCES/0115-SSH-Do-not-exit-abruptly-if-SSHD-closes-its-end-of-t.patch b/SOURCES/0115-SSH-Do-not-exit-abruptly-if-SSHD-closes-its-end-of-t.patch
new file mode 100644
index 0000000..711e447
--- /dev/null
+++ b/SOURCES/0115-SSH-Do-not-exit-abruptly-if-SSHD-closes-its-end-of-t.patch
@@ -0,0 +1,88 @@
+From d1b01f0a04e54c55183fd5cee4b713e28e4e2cd7 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 28 May 2018 21:41:49 +0200
+Subject: [PATCH] SSH: Do not exit abruptly if SSHD closes its end of the pipe
+ before reading all the SSH keys
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3747
+
+Before writing the keys to sshd, ignore SIGPIPE so that if the pipe
+towards the authorizedkeys helper is closed, the sss_ssh_authorizedkeys
+helper is not terminated with SIGPIPE, but instead proceeds and then the
+write(2) calls would non-terminally fail with EPIPE.
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit cb138d7d060611e891d341db08477e41f9a3d17d)
+---
+ src/sss_client/ssh/sss_ssh_authorizedkeys.c | 35 ++++++++++++++++++++++++++++-
+ 1 file changed, 34 insertions(+), 1 deletion(-)
+
+diff --git a/src/sss_client/ssh/sss_ssh_authorizedkeys.c b/src/sss_client/ssh/sss_ssh_authorizedkeys.c
+index 782a9f44379bff5346c896b3e03570720632c0be..b0280fbf8b0ed0501d792973241b826fc4a7a04d 100644
+--- a/src/sss_client/ssh/sss_ssh_authorizedkeys.c
++++ b/src/sss_client/ssh/sss_ssh_authorizedkeys.c
+@@ -21,6 +21,7 @@
+ #include <stdio.h>
+ #include <talloc.h>
+ #include <popt.h>
++#include <signal.h>
+ 
+ #include "util/util.h"
+ #include "util/crypto/sss_crypto.h"
+@@ -99,8 +100,16 @@ int main(int argc, const char **argv)
+         goto fini;
+     }
+ 
++    /* if sshd closes its end of the pipe, we don't want sss_ssh_authorizedkeys
++     * to exit abruptly, but to finish gracefully instead because the valid
++     * key can be present in the data already written
++     */
++    signal(SIGPIPE, SIG_IGN);
++
+     /* print results */
+     for (i = 0; i < ent->num_pubkeys; i++) {
++        char *repr_break = NULL;
++
+         ret = sss_ssh_format_pubkey(mem_ctx, &ent->pubkeys[i], &repr);
+         if (ret != EOK) {
+             DEBUG(SSSDBG_OP_FAILURE,
+@@ -109,7 +118,31 @@ int main(int argc, const char **argv)
+             continue;
+         }
+ 
+-        printf("%s\n", repr);
++        /* OpenSSH expects a linebreak after each key */
++        repr_break = talloc_asprintf(mem_ctx, "%s\n", repr);
++        talloc_zfree(repr);
++        if (repr_break == NULL) {
++            ret = ENOMEM;
++            goto fini;
++        }
++
++        ret = sss_atomic_write_s(STDOUT_FILENO, repr_break, strlen(repr_break));
++        /* Avoid spiking memory with too many large keys */
++        talloc_zfree(repr_break);
++        if (ret < 0) {
++            ret = errno;
++            if (ret == EPIPE) {
++                DEBUG(SSSDBG_MINOR_FAILURE,
++                      "SSHD closed the pipe before all keys could be written\n");
++                /* Return 0 so that openssh doesn't abort pubkey auth */
++                ret = 0;
++                goto fini;
++            }
++            DEBUG(SSSDBG_CRIT_FAILURE,
++                  "sss_atomic_write_s() failed (%d): %s\n",
++                  ret, strerror(ret));
++            goto fini;
++        }
+     }
+ 
+     ret = EXIT_SUCCESS;
+-- 
+2.14.4
+
diff --git a/SOURCES/0116-TESTS-Add-a-helper-binary-that-can-trigger-the-SIGPI.patch b/SOURCES/0116-TESTS-Add-a-helper-binary-that-can-trigger-the-SIGPI.patch
new file mode 100644
index 0000000..494b327
--- /dev/null
+++ b/SOURCES/0116-TESTS-Add-a-helper-binary-that-can-trigger-the-SIGPI.patch
@@ -0,0 +1,213 @@
+From 187b9f28f1ea2e3fa4b5e3385050701fdc1d0f69 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Mon, 28 May 2018 21:49:41 +0200
+Subject: [PATCH] TESTS: Add a helper binary that can trigger the SIGPIPE to
+ authorizedkeys
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Adds a test tool that simulates the behaviour of OpenSSH in the sense
+that it starts to read the output from the sss_ssh_authorizedkeys tool,
+but then closes the pipe before reading the whole output.
+
+Related:
+https://pagure.io/SSSD/sssd/issue/3747
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 909c16edb26a3c48b10a49e7919a35d13d31c52e)
+---
+ Makefile.am                 |  15 ++++-
+ src/tests/test_ssh_client.c | 133 ++++++++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 147 insertions(+), 1 deletion(-)
+ create mode 100644 src/tests/test_ssh_client.c
+
+diff --git a/Makefile.am b/Makefile.am
+index 5917bd904054055a259eb69217282e4fb914c700..01fa4e43e48dcb722056d614e19f02687d32014b 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -331,6 +331,7 @@ endif   # HAVE_CMOCKA
+ check_PROGRAMS = \
+     stress-tests \
+     krb5-child-test \
++    test_ssh_client \
+     $(non_interactive_cmocka_based_tests) \
+     $(non_interactive_check_based_tests)
+ 
+@@ -2294,6 +2295,18 @@ krb5_child_test_LDADD = \
+     $(SSSD_INTERNAL_LTLIBS) \
+     libsss_test_common.la
+ 
++test_ssh_client_SOURCES = \
++    src/tests/test_ssh_client.c \
++    $(NULL)
++test_ssh_client_CFLAGS = \
++    $(AM_CFLAGS) \
++    -DSSH_CLIENT_DIR=\"$(abs_top_builddir)\" \
++    $(NULL)
++test_ssh_client_LDADD = \
++    $(SSSD_INTERNAL_LTLIBS) \
++    $(SSSD_LIBS) \
++    $(NULL)
++
+ if BUILD_DBUS_TESTS
+ 
+ sbus_tests_SOURCES = \
+@@ -3418,7 +3431,6 @@ test_iobuf_LDADD = \
+     $(SSSD_LIBS) \
+     $(NULL)
+ 
+-
+ EXTRA_simple_access_tests_DEPENDENCIES = \
+     $(ldblib_LTLIBRARIES)
+ simple_access_tests_SOURCES = \
+@@ -3607,6 +3619,7 @@ intgcheck-prepare:
+ 	    $(INTGCHECK_CONFIGURE_FLAGS) \
+ 	    CFLAGS="$$CFLAGS -DKCM_PEER_UID=$$(id -u)"; \
+ 	$(MAKE) $(AM_MAKEFLAGS) ; \
++	$(MAKE) $(AM_MAKEFLAGS) test_ssh_client; \
+ 	: Force single-thread install to workaround concurrency issues; \
+ 	$(MAKE) $(AM_MAKEFLAGS) -j1 install; \
+ 	: Remove .la files from LDB module directory to avoid loader warnings; \
+diff --git a/src/tests/test_ssh_client.c b/src/tests/test_ssh_client.c
+new file mode 100644
+index 0000000000000000000000000000000000000000..8f963941f3249561178436d6f6dfc376780a4cda
+--- /dev/null
++++ b/src/tests/test_ssh_client.c
+@@ -0,0 +1,133 @@
++/*
++    Copyright (C) 2018 Red Hat
++
++    This program is free software; you can redistribute it and/or modify
++    it under the terms of the GNU General Public License as published by
++    the Free Software Foundation; either version 3 of the License, or
++    (at your option) any later version.
++
++    This program 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 General Public License for more details.
++
++    You should have received a copy of the GNU General Public License
++    along with this program.  If not, see <http://www.gnu.org/licenses/>.
++*/
++
++#include <popt.h>
++#include <sys/wait.h>
++#include "util/util.h"
++
++#ifdef SSH_CLIENT_DIR
++#define SSH_AK_CLIENT_PATH SSH_CLIENT_DIR"/sss_ssh_authorizedkeys"
++#else
++#error "The path to the ssh authorizedkeys helper is not defined"
++#endif /* SSH_CLIENT_DIR */
++
++int main(int argc, const char *argv[])
++{
++    poptContext pc;
++    int opt;
++    struct poptOption long_options[] = {
++        POPT_AUTOHELP
++        SSSD_DEBUG_OPTS
++        POPT_TABLEEND
++    };
++    struct stat sb;
++    int ret;
++    int status;
++    int p[2];
++    pid_t pid;
++    const char *pc_user = NULL;
++    char *av[3];
++    char buf[5]; /* Ridiculously small buffer by design */
++
++    /* Set debug level to invalid value so we can decide if -d 0 was used. */
++    debug_level = SSSDBG_INVALID;
++
++    pc = poptGetContext(argv[0], argc, argv, long_options, 0);
++    poptSetOtherOptionHelp(pc, "USER");
++    while((opt = poptGetNextOpt(pc)) != -1) {
++        switch(opt) {
++        default:
++            fprintf(stderr, "\nInvalid option %s: %s\n\n",
++                    poptBadOption(pc, 0), poptStrerror(opt));
++            poptPrintUsage(pc, stderr, 0);
++            return 3;
++        }
++    }
++
++    pc_user = poptGetArg(pc);
++    if (pc_user == NULL) {
++        fprintf(stderr, "No user specified\n");
++        return 3;
++    }
++
++    poptFreeContext(pc);
++
++    DEBUG_CLI_INIT(debug_level);
++
++    ret = stat(SSH_AK_CLIENT_PATH, &sb);
++    if (ret != 0) {
++        ret = errno;
++        DEBUG(SSSDBG_CRIT_FAILURE,
++              "Could not stat %s [%d]: %s\n",
++              SSH_AK_CLIENT_PATH, ret, strerror(ret));
++        return 3;
++    }
++
++    ret = pipe(p);
++    if (ret != 0) {
++        perror("pipe");
++        return 3;
++    }
++
++    switch (pid = fork()) {
++    case -1:
++        ret = errno;
++        close(p[0]);
++        close(p[1]);
++        DEBUG(SSSDBG_CRIT_FAILURE, "fork failed: %d\n", ret);
++        return 3;
++    case 0:
++        /* child */
++        av[0] = discard_const(SSH_AK_CLIENT_PATH);
++        av[1] = discard_const(pc_user);
++        av[2] = NULL;
++
++        close(p[0]);
++        ret = dup2(p[1], STDOUT_FILENO);
++        if (ret == -1) {
++            perror("dup2");
++            return 3;
++        }
++
++        execv(av[0], av);
++        return 3;
++    default:
++        /* parent */
++        break;
++    }
++
++    close(p[1]);
++    read(p[0], buf, sizeof(buf));
++    close(p[0]);
++
++    pid = waitpid(pid, &status, 0);
++    if (pid == -1) {
++        perror("waitpid");
++        return 3;
++    }
++
++    if (WIFEXITED(status)) {
++        printf("sss_ssh_authorizedkeys exited with return code %d\n", WEXITSTATUS(status));
++        return 0;
++    } else if (WIFSIGNALED(status)) {
++        printf("sss_ssh_authorizedkeys exited with signal %d\n", WTERMSIG(status));
++        return 1;
++    }
++
++    printf("sss_ssh_authorizedkeys exited for another reason\n");
++    return 2;
++}
+-- 
+2.14.4
+
diff --git a/SOURCES/0117-TESTS-Add-a-regression-test-for-SIGHUP-handling-in-s.patch b/SOURCES/0117-TESTS-Add-a-regression-test-for-SIGHUP-handling-in-s.patch
new file mode 100644
index 0000000..c791974
--- /dev/null
+++ b/SOURCES/0117-TESTS-Add-a-regression-test-for-SIGHUP-handling-in-s.patch
@@ -0,0 +1,94 @@
+From 103a22b85df5c371aefb08e476a3ab950e6882a3 Mon Sep 17 00:00:00 2001
+From: Jakub Hrozek <jhrozek@redhat.com>
+Date: Tue, 19 Jun 2018 11:39:02 +0200
+Subject: [PATCH] TESTS: Add a regression test for SIGHUP handling in
+ sss_ssh_authorizedkeys
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+A regression test for:
+https://pagure.io/SSSD/sssd/issue/3747
+
+Reviewed-by: Fabiano Fidêncio <fidencio@redhat.com>
+(cherry picked from commit 4cc3c1a1b1070c12bcc4351880d8207e47b37496)
+---
+ src/tests/intg/test_ssh_pubkey.py | 58 +++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 58 insertions(+)
+
+diff --git a/src/tests/intg/test_ssh_pubkey.py b/src/tests/intg/test_ssh_pubkey.py
+index fbf55566e341373873057ec4e3af1d7f83202aa7..8fb41c62d87ec210c9aad8582023fe1cb00f2b4e 100644
+--- a/src/tests/intg/test_ssh_pubkey.py
++++ b/src/tests/intg/test_ssh_pubkey.py
+@@ -24,6 +24,8 @@ import time
+ import ldap
+ import ldap.modlist
+ import pytest
++import string
++import random
+ 
+ import config
+ import ds_openldap
+@@ -230,3 +232,59 @@ def test_ssh_pubkey_retrieve(add_user_with_ssh_key):
+ 
+     sshpubkey = get_call_output(["sss_ssh_authorizedkeys", "user2"])
+     assert len(sshpubkey) == 0
++
++
++@pytest.fixture()
++def sighup_client(request):
++    test_ssh_cli_path = os.path.join(config.ABS_BUILDDIR,
++                                     "..", "..", "..", "test_ssh_client")
++    assert os.access(test_ssh_cli_path, os.X_OK)
++    return test_ssh_cli_path
++
++
++@pytest.fixture
++def add_user_with_many_keys(request, ldap_conn):
++    # Generate a large list of unique ssh pubkeys
++    pubkey_list = []
++    while len(pubkey_list) < 50:
++        new_pubkey = list(USER1_PUBKEY1)
++        new_pubkey[10] = random.choice(string.ascii_uppercase)
++        new_pubkey[11] = random.choice(string.ascii_uppercase)
++        new_pubkey[12] = random.choice(string.ascii_uppercase)
++        str_new_pubkey = ''.join(c for c in new_pubkey)
++        if str_new_pubkey in pubkey_list:
++            continue
++        pubkey_list.append(str_new_pubkey)
++
++    ent_list = ldap_ent.List(ldap_conn.ds_inst.base_dn)
++    ent_list.add_user("user1", 1001, 2001, sshPubKey=pubkey_list)
++    create_ldap_fixture(request, ldap_conn, ent_list)
++
++    conf = format_basic_conf(ldap_conn, SCHEMA_RFC2307_BIS)
++    create_conf_fixture(request, conf)
++    create_sssd_fixture(request)
++    return None
++
++
++def test_ssh_sighup(add_user_with_many_keys, sighup_client):
++    """
++    A regression test for https://pagure.io/SSSD/sssd/issue/3747
++
++    OpenSSH can close its end of the pipe towards sss_ssh_authorizedkeys
++    before all of the output is read. In that case, older versions
++    of sss_ssh_authorizedkeys were receiving a SIGPIPE
++    """
++    cli_path = sighup_client
++
++    # python actually does the sensible, but unexpected (for a C programmer)
++    # thing and handles SIGPIPE. In order to reproduce the bug, we need
++    # to unset the SIGPIPE handler
++    signal.signal(signal.SIGPIPE, signal.SIG_DFL)
++
++    process = subprocess.Popen([cli_path, "user1"],
++                               stdout=subprocess.PIPE,
++                               stderr=subprocess.PIPE)
++    _, _ = process.communicate()
++    # If the test tool detects that sss_ssh_authorizedkeys was killed with a
++    # signal, it would have returned 1
++    assert process.returncode == 0
+-- 
+2.14.4
+
diff --git a/SOURCES/0118-TESTS-Order-list-of-entries-in-some-lists.patch b/SOURCES/0118-TESTS-Order-list-of-entries-in-some-lists.patch
new file mode 100644
index 0000000..8e7ca54
--- /dev/null
+++ b/SOURCES/0118-TESTS-Order-list-of-entries-in-some-lists.patch
@@ -0,0 +1,175 @@
+From 1a73dbe9747aec2818fabd179e0fb46695d66433 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Michal=20=C5=BDidek?= <mzidek@redhat.com>
+Date: Mon, 13 Nov 2017 16:15:21 +0100
+Subject: [PATCH] TESTS: Order list of entries in some lists
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Some tests started to fail because we depended on specific
+order of users in groups or messages in ldb results to be
+returned and that order changed.
+
+This patch adds a simple helper functions into these tests
+that order the entries before comparison with expected results.
+more deterministic.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3563
+
+Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
+(cherry picked from commit caae0e53e6091806634943699f4398b6a20273b4)
+
+DOWNSTREAM:
+Resolves: rhbz#1596292 - home dir disappear in sssd cache on the IPA master for AD users [rhel-7.5.z]
+---
+ src/tests/cmocka/test_nss_srv.c     | 22 +++++++++++++++++++
+ src/tests/cmocka/test_sysdb_views.c | 42 ++++++++++++++++++++++++++++++++-----
+ 2 files changed, 59 insertions(+), 5 deletions(-)
+
+diff --git a/src/tests/cmocka/test_nss_srv.c b/src/tests/cmocka/test_nss_srv.c
+index 6aa726153183b5a871a75d398727ea7132358ca6..21bd80fb7f6562f6a31452bac6a26c109fef4cb1 100644
+--- a/src/tests/cmocka/test_nss_srv.c
++++ b/src/tests/cmocka/test_nss_srv.c
+@@ -585,6 +585,25 @@ static errno_t delete_group(struct nss_test_ctx *ctx,
+     return ret;
+ }
+ 
++static int cmp_func(const void *a, const void *b)
++{
++    char *str1 = *(char **)discard_const(a);
++    char *str2 = *(char **)discard_const(b);
++
++    return strcmp(str1, str2);
++}
++
++static void order_string_array(char **_list, int size)
++{
++    if (size < 2 || _list == NULL || *_list == NULL) {
++        /* Nothing to do */
++        return;
++    }
++
++    qsort(_list, size, sizeof(char *), cmp_func);
++    return;
++}
++
+ static void assert_groups_equal(struct group *expected,
+                                 struct group *gr, const int nmem)
+ {
+@@ -594,6 +613,9 @@ static void assert_groups_equal(struct group *expected,
+     assert_string_equal(gr->gr_name, expected->gr_name);
+     assert_string_equal(gr->gr_passwd, expected->gr_passwd);
+ 
++    order_string_array(gr->gr_mem, nmem);
++    order_string_array(expected->gr_mem, nmem);
++
+     for (i = 0; i < nmem; i++) {
+         assert_string_equal(gr->gr_mem[i], expected->gr_mem[i]);
+     }
+diff --git a/src/tests/cmocka/test_sysdb_views.c b/src/tests/cmocka/test_sysdb_views.c
+index 0378254b4440b29c3182faf2adde8c3db8a4ce97..dd3eb50f9310ff925734dcf51a669d08a638aefd 100644
+--- a/src/tests/cmocka/test_sysdb_views.c
++++ b/src/tests/cmocka/test_sysdb_views.c
+@@ -22,6 +22,7 @@
+     along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
++#include <stdlib.h>
+ #include <stdarg.h>
+ #include <stddef.h>
+ #include <setjmp.h>
+@@ -612,6 +613,31 @@ static int test_enum_users_setup(void **state)
+     return 0;
+ }
+ 
++static int cmp_func(const void *a, const void *b)
++{
++    const char *str1;
++    const char *str2;
++    struct ldb_message *msg1 = *(struct ldb_message **)discard_const(a);
++    struct ldb_message *msg2 = *(struct ldb_message **)discard_const(b);
++
++    str1 = ldb_msg_find_attr_as_string(msg1, SYSDB_NAME, NULL);
++    str2 = ldb_msg_find_attr_as_string(msg2, SYSDB_NAME, NULL);
++
++    return strcmp(str1, str2);
++}
++
++/* Make the order of ldb results deterministic */
++static void order_ldb_res_msgs(struct ldb_result *res)
++{
++    if (res == NULL || res->count < 2) {
++        /* Nothing to do */
++        return;
++    }
++
++    qsort(res->msgs, res->count, sizeof(struct ldb_message *), cmp_func);
++    return;
++}
++
+ static void assert_user_attrs(struct ldb_message *msg,
+                               struct sss_domain_info *dom,
+                               const char *shortname,
+@@ -660,8 +686,9 @@ static void check_enumpwent(int ret, struct sss_domain_info *dom,
+     assert_int_equal(ret, EOK);
+     assert_int_equal(res->count, N_ELEMENTS(users)-1);
+ 
+-    assert_user_attrs(res->msgs[0], dom, "barney", views);
+-    assert_user_attrs(res->msgs[1], dom, "alice", views);
++    order_ldb_res_msgs(res);
++    assert_user_attrs(res->msgs[0], dom, "alice", views);
++    assert_user_attrs(res->msgs[1], dom, "barney", views);
+     assert_user_attrs(res->msgs[2], dom, "bob", views);
+ }
+ 
+@@ -703,6 +730,7 @@ static void test_sysdb_enumpwent_filter(void **state)
+     ret = sysdb_enumpwent_filter(test_ctx, test_ctx->domain, "b*", 0, &res);
+     assert_int_equal(ret, EOK);
+     assert_int_equal(res->count, 2);
++    order_ldb_res_msgs(res);
+     assert_user_attrs(res->msgs[0], test_ctx->domain, "barney", false);
+     assert_user_attrs(res->msgs[1], test_ctx->domain, "bob", false);
+ 
+@@ -749,6 +777,7 @@ static void test_sysdb_enumpwent_filter_views(void **state)
+                                             "b*", NULL, &res);
+     assert_int_equal(ret, EOK);
+     assert_int_equal(res->count, 2);
++    order_ldb_res_msgs(res);
+     assert_user_attrs(res->msgs[0], test_ctx->domain, "barney", true);
+     assert_user_attrs(res->msgs[1], test_ctx->domain, "bob", true);
+ 
+@@ -896,10 +925,11 @@ static void check_enumgrent(int ret, struct sss_domain_info *dom,
+ {
+     assert_int_equal(ret, EOK);
+     assert_int_equal(res->count, N_ELEMENTS(groups)-1);
+-    assert_group_attrs(res->msgs[0], dom, "three",
+-                       views ? TEST_GID_OVERRIDE_BASE + 2 : 0);
+-    assert_group_attrs(res->msgs[1], dom, "one",
++    order_ldb_res_msgs(res);
++    assert_group_attrs(res->msgs[0], dom, "one",
+                        views ? TEST_GID_OVERRIDE_BASE : 0);
++    assert_group_attrs(res->msgs[1], dom, "three",
++                       views ? TEST_GID_OVERRIDE_BASE + 2 : 0);
+     assert_group_attrs(res->msgs[2], dom, "two",
+                        views ? TEST_GID_OVERRIDE_BASE + 1 : 0);
+ }
+@@ -942,6 +972,7 @@ static void test_sysdb_enumgrent_filter(void **state)
+     ret = sysdb_enumgrent_filter(test_ctx, test_ctx->domain, "t*", 0, &res);
+     assert_int_equal(ret, EOK);
+     assert_int_equal(res->count, 2);
++    order_ldb_res_msgs(res);
+     assert_group_attrs(res->msgs[0], test_ctx->domain, "three", 0);
+     assert_group_attrs(res->msgs[1], test_ctx->domain, "two", 0);
+ 
+@@ -988,6 +1019,7 @@ static void test_sysdb_enumgrent_filter_views(void **state)
+                                             "t*", NULL, &res);
+     assert_int_equal(ret, EOK);
+     assert_int_equal(res->count, 2);
++    order_ldb_res_msgs(res);
+     assert_group_attrs(res->msgs[0], test_ctx->domain,
+                        "three", TEST_GID_OVERRIDE_BASE + 2);
+     assert_group_attrs(res->msgs[1], test_ctx->domain, "two",
+-- 
+2.14.4
+
diff --git a/SOURCES/0119-sysdb-add-sysdb_getgrgid_attrs.patch b/SOURCES/0119-sysdb-add-sysdb_getgrgid_attrs.patch
new file mode 100644
index 0000000..747a123
--- /dev/null
+++ b/SOURCES/0119-sysdb-add-sysdb_getgrgid_attrs.patch
@@ -0,0 +1,171 @@
+From f46dc8010a7d5fbb398e282d680703e1bd5963f4 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 29 May 2018 15:33:34 +0200
+Subject: [PATCH] sysdb: add sysdb_getgrgid_attrs()
+
+sysdb_getgrgid() is the only MPG aware by GID request but only supports
+a fixes set of attributes. The new call allows to add additional
+arguments.
+
+Related to https://pagure.io/SSSD/sssd/issue/3748
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 8aa56a9e8744a7611fa26a254c4f9228e919c8ed)
+---
+ src/db/sysdb.h          |  6 ++++++
+ src/db/sysdb_ops.c      |  3 +++
+ src/db/sysdb_search.c   | 31 ++++++++++++++++++++++++++-----
+ src/tests/sysdb-tests.c | 37 +++++++++++++++++++++++++++++++++++++
+ 4 files changed, 72 insertions(+), 5 deletions(-)
+
+diff --git a/src/db/sysdb.h b/src/db/sysdb.h
+index 2660314a75a574d7f5625c8672e5261587056d1a..affd1c9053e43ff24c98cc8fb23eec2c4b69f955 100644
+--- a/src/db/sysdb.h
++++ b/src/db/sysdb.h
+@@ -779,6 +779,12 @@ int sysdb_getgrgid(TALLOC_CTX *mem_ctx,
+                    gid_t gid,
+                    struct ldb_result **res);
+ 
++int sysdb_getgrgid_attrs(TALLOC_CTX *mem_ctx,
++                         struct sss_domain_info *domain,
++                         gid_t gid,
++                         const char **attrs,
++                         struct ldb_result **res);
++
+ int sysdb_enumgrent(TALLOC_CTX *mem_ctx,
+                     struct sss_domain_info *domain,
+                     struct ldb_result **res);
+diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
+index 024683317cab99743681db804f7026c8dbb33a38..c0d343bdabd324cbe8b9745c65c2a6e5a56321e1 100644
+--- a/src/db/sysdb_ops.c
++++ b/src/db/sysdb_ops.c
+@@ -709,6 +709,9 @@ int sysdb_search_group_by_name(TALLOC_CTX *mem_ctx,
+     return sysdb_search_by_name(mem_ctx, domain, name, SYSDB_GROUP, attrs, msg);
+ }
+ 
++/* Please note that sysdb_search_group_by_gid() is not aware of MPGs. If MPG
++ * support is needed either the caller must handle it or sysdb_getgrgid() or
++ * sysdb_getgrgid_attrs() should be used. */
+ int sysdb_search_group_by_gid(TALLOC_CTX *mem_ctx,
+                               struct sss_domain_info *domain,
+                               gid_t gid,
+diff --git a/src/db/sysdb_search.c b/src/db/sysdb_search.c
+index 9f37cbcd50a778145518c15b6146ad812a5b4fa3..3d78a38b36e65febd50e41fc1d1be29eceeb7649 100644
+--- a/src/db/sysdb_search.c
++++ b/src/db/sysdb_search.c
+@@ -1036,24 +1036,37 @@ done:
+     return ret;
+ }
+ 
+-int sysdb_getgrgid(TALLOC_CTX *mem_ctx,
+-                   struct sss_domain_info *domain,
+-                   gid_t gid,
+-                   struct ldb_result **_res)
++int sysdb_getgrgid_attrs(TALLOC_CTX *mem_ctx,
++                         struct sss_domain_info *domain,
++                         gid_t gid,
++                         const char **additional_attrs,
++                         struct ldb_result **_res)
+ {
+     TALLOC_CTX *tmp_ctx;
+     unsigned long int ul_gid = gid;
+-    static const char *attrs[] = SYSDB_GRSRC_ATTRS;
+     const char *fmt_filter;
+     struct ldb_dn *base_dn;
+     struct ldb_result *res;
+     int ret;
++    static const char *default_attrs[] = SYSDB_GRSRC_ATTRS;
++    const char **attrs = NULL;
+ 
+     tmp_ctx = talloc_new(NULL);
+     if (!tmp_ctx) {
+         return ENOMEM;
+     }
+ 
++    if (additional_attrs == NULL) {
++        attrs = default_attrs;
++    } else {
++        ret = add_strings_lists(tmp_ctx, additional_attrs, default_attrs,
++                                false, discard_const(&attrs));
++        if (ret != EOK) {
++            DEBUG(SSSDBG_OP_FAILURE, "add_strings_lists failed.\n");
++            goto done;
++        }
++    }
++
+     if (domain->mpg) {
+         fmt_filter = SYSDB_GRGID_MPG_FILTER;
+         base_dn = ldb_dn_new_fmt(tmp_ctx, domain->sysdb->ldb,
+@@ -1092,6 +1105,14 @@ done:
+     return ret;
+ }
+ 
++int sysdb_getgrgid(TALLOC_CTX *mem_ctx,
++                   struct sss_domain_info *domain,
++                   gid_t gid,
++                   struct ldb_result **_res)
++{
++    return sysdb_getgrgid_attrs(mem_ctx, domain, gid, NULL, _res);
++}
++
+ int sysdb_enumgrent_filter(TALLOC_CTX *mem_ctx,
+                            struct sss_domain_info *domain,
+                            const char *name_filter,
+diff --git a/src/tests/sysdb-tests.c b/src/tests/sysdb-tests.c
+index fc9936968bcde8370c7054ba303de4463b35e15a..30574679f51362d03d7b9e3f8a1d55889817e4c1 100644
+--- a/src/tests/sysdb-tests.c
++++ b/src/tests/sysdb-tests.c
+@@ -1114,6 +1114,42 @@ done:
+ }
+ END_TEST
+ 
++START_TEST (test_sysdb_getgrgid_attrs)
++{
++    struct sysdb_test_ctx *test_ctx;
++    struct test_data *data;
++    struct ldb_result *res;
++    int ret;
++    const char *attrs[] = { SYSDB_CREATE_TIME, NULL };
++    uint64_t ctime;
++
++    /* Setup */
++    ret = setup_sysdb_tests(&test_ctx);
++    if (ret != EOK) {
++        fail("Could not set up the test");
++        return;
++    }
++
++    data = test_data_new_group(test_ctx, _i);
++    fail_if(data == NULL, "OOM");
++
++    ret = sysdb_getgrgid_attrs(test_ctx,
++                               test_ctx->domain,
++                               data->gid, attrs, &res);
++    if (ret) {
++        fail("sysdb_getgrgid_attrs failed for gid %d (%d: %s)",
++             data->gid, ret, strerror(ret));
++        goto done;
++    }
++
++    ctime = ldb_msg_find_attr_as_uint64(res->msgs[0], SYSDB_CREATE_TIME, 0);
++    fail_unless(ctime != 0, "Missing create time");
++
++done:
++    talloc_free(test_ctx);
++}
++END_TEST
++
+ START_TEST (test_sysdb_search_groups)
+ {
+     struct sysdb_test_ctx *test_ctx;
+@@ -7072,6 +7108,7 @@ Suite *create_sysdb_suite(void)
+ 
+     /* Verify the groups can be queried by GID */
+     tcase_add_loop_test(tc_sysdb, test_sysdb_getgrgid, 28010, 28020);
++    tcase_add_loop_test(tc_sysdb, test_sysdb_getgrgid_attrs, 28010, 28020);
+ 
+     /* Find the users by GID using a filter */
+     tcase_add_loop_test(tc_sysdb, test_sysdb_search_groups, 28010, 28020);
+-- 
+2.14.4
+
diff --git a/SOURCES/0120-ipa-use-mpg-aware-group-lookup-in-get_object_from_ca.patch b/SOURCES/0120-ipa-use-mpg-aware-group-lookup-in-get_object_from_ca.patch
new file mode 100644
index 0000000..b5ea5f9
--- /dev/null
+++ b/SOURCES/0120-ipa-use-mpg-aware-group-lookup-in-get_object_from_ca.patch
@@ -0,0 +1,61 @@
+From a6de362d3cfe16550eb16d01900f44c9aeb8cc50 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 29 May 2018 15:42:55 +0200
+Subject: [PATCH] ipa: use mpg aware group lookup in get_object_from_cache()
+
+Since with algorithmic id-mapping SSSD automatically creates user
+private groups for AD user with the help of magic private groups (mpg)
+get_object_from_cache() should use mpg aware calls to make sure the
+right user object is found when handling a request to look up a user
+private group.
+
+Only the lookup by gid had to be modified because
+sysdb_search_group_by_name() used for lookups by name is aware of MPGs.
+
+Related to https://pagure.io/SSSD/sssd/issue/3748
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit 032221568fe4287686d0ebb11b5c1fe51cc4735f)
+---
+ src/providers/ipa/ipa_subdomains_id.c | 18 ++++++++++++++++--
+ 1 file changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index d40671086854f9c1a3f8bc7fc711009298dc31c8..3943579b07c7b2d32dde192b97b86eb036b91885 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -1030,7 +1030,14 @@ errno_t get_object_from_cache(TALLOC_CTX *mem_ctx,
+ 
+         switch (ar->entry_type & BE_REQ_TYPE_MASK) {
+         case BE_REQ_GROUP:
+-            ret = sysdb_search_group_by_gid(mem_ctx, dom, id, attrs, &msg);
++            ret = sysdb_getgrgid_attrs(mem_ctx, dom, id, attrs, &res);
++            if (ret == EOK) {
++                if (res->count == 0) {
++                    ret = ENOENT;
++                } else {
++                    msg = res->msgs[0];
++                }
++            }
+             break;
+         case BE_REQ_INITGROUPS:
+         case BE_REQ_USER:
+@@ -1038,7 +1045,14 @@ errno_t get_object_from_cache(TALLOC_CTX *mem_ctx,
+             ret = sysdb_search_user_by_uid(mem_ctx, dom, id, attrs, &msg);
+             if (ret == ENOENT && (ar->entry_type & BE_REQ_TYPE_MASK)
+                                                      == BE_REQ_USER_AND_GROUP) {
+-                ret = sysdb_search_group_by_gid(mem_ctx, dom, id, attrs, &msg);
++                ret = sysdb_getgrgid_attrs(mem_ctx, dom, id, attrs, &res);
++                if (ret == EOK) {
++                    if (res->count == 0) {
++                        ret = ENOENT;
++                    } else {
++                        msg = res->msgs[0];
++                    }
++                }
+             }
+             break;
+         default:
+-- 
+2.14.4
+
diff --git a/SOURCES/0121-ipa-allow-mpg-group-objects-in-apply_subdomain_homed.patch b/SOURCES/0121-ipa-allow-mpg-group-objects-in-apply_subdomain_homed.patch
new file mode 100644
index 0000000..b785997
--- /dev/null
+++ b/SOURCES/0121-ipa-allow-mpg-group-objects-in-apply_subdomain_homed.patch
@@ -0,0 +1,47 @@
+From 8bbee851484f7fa51af542ed2757e2eea36bf535 Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 29 May 2018 15:44:28 +0200
+Subject: [PATCH] ipa: allow mpg group objects in apply_subdomain_homedir()
+
+Since with algorithmic id-mapping SSSD automatically creates user
+private groups for AD user with the help of magic private groups (mpg)
+apply_subdomain_homedir() should be aware the in mpg domains a group
+lookup might actually return a user object. Since the related sysdb
+calls are clever and replace the objectcategory so that it matches the
+original request type we have to check for the group category in the mpg
+case as well. apply_subdomain_homedir() checks the uidNumber later as
+well to make sure the object has the needed attributes for a user.
+
+Related to https://pagure.io/SSSD/sssd/issue/3748
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit e66517dcf63f1d4aaf866c22371dac7740ce0a48)
+---
+ src/providers/ipa/ipa_subdomains_id.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_subdomains_id.c b/src/providers/ipa/ipa_subdomains_id.c
+index 3943579b07c7b2d32dde192b97b86eb036b91885..c2064d33029a27a2c5d4b5344034ce90f8e746b8 100644
+--- a/src/providers/ipa/ipa_subdomains_id.c
++++ b/src/providers/ipa/ipa_subdomains_id.c
+@@ -895,9 +895,16 @@ apply_subdomain_homedir(TALLOC_CTX *mem_ctx, struct sss_domain_info *dom,
+         goto done;
+     }
+ 
++    /* The object is a user if SYSDB_OBJECTCATEGORY is SYSDB_USER_CLASS or in
++     * case of a MPG group lookup if SYSDB_OBJECTCATEGORY is SYSDB_GROUP_CLASS.
++     */
+     for (c = 0; c < msg_el->num_values; c++) {
+         if (strncmp(SYSDB_USER_CLASS, (const char *)msg_el->values[c].data,
+-                    msg_el->values[c].length) == 0) {
++                    msg_el->values[c].length) == 0
++                || (dom->mpg
++                    && strncmp(SYSDB_GROUP_CLASS,
++                               (const char *)msg_el->values[c].data,
++                               msg_el->values[c].length) == 0)) {
+             break;
+         }
+     }
+-- 
+2.14.4
+
diff --git a/SOURCES/0122-AD-LDAP-do-not-fall-back-to-mpg-user-lookup-on-GC-co.patch b/SOURCES/0122-AD-LDAP-do-not-fall-back-to-mpg-user-lookup-on-GC-co.patch
new file mode 100644
index 0000000..68dba38
--- /dev/null
+++ b/SOURCES/0122-AD-LDAP-do-not-fall-back-to-mpg-user-lookup-on-GC-co.patch
@@ -0,0 +1,68 @@
+From 4dbfa49f50fd785f374209c2e59205e79533788e Mon Sep 17 00:00:00 2001
+From: Sumit Bose <sbose@redhat.com>
+Date: Tue, 29 May 2018 15:46:33 +0200
+Subject: [PATCH] AD/LDAP: do not fall back to mpg user lookup on GC connection
+
+For MPG domains a group lookup might fall back to a user lookup to check
+if the request is for a user private group. Since we cannot be sure that
+all needed attributes for a user are replicated to the Global Catalog we
+do not want to lookup the user during the fall back from the Global
+Catalog.
+
+Since we cannot skip Global Catalog lookups for groups completely due to
+membership to groups with universal scope this patch adds a flag to tell
+the lower level lookup calls to not fall back on connections to a Global
+Catalog.
+
+Related to https://pagure.io/SSSD/sssd/issue/3748
+
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit ad6ab352879264fdade8861aff53aa035a2e2240)
+---
+ src/providers/ad/ad_common.c     | 1 +
+ src/providers/ldap/ldap_common.h | 2 ++
+ src/providers/ldap/ldap_id.c     | 3 ++-
+ 3 files changed, 5 insertions(+), 1 deletion(-)
+
+diff --git a/src/providers/ad/ad_common.c b/src/providers/ad/ad_common.c
+index 2a1647173b76b410371315eb364e9a3785714a93..1dca2fe279cb7d6d647aed42e3b3fabfb34b7dac 100644
+--- a/src/providers/ad/ad_common.c
++++ b/src/providers/ad/ad_common.c
+@@ -1375,6 +1375,7 @@ ad_gc_conn_list(TALLOC_CTX *mem_ctx, struct ad_id_ctx *ad_ctx,
+     if (dp_opt_get_bool(ad_ctx->ad_options->basic, AD_ENABLE_GC)) {
+         clist[cindex] = ad_ctx->gc_ctx;
+         clist[cindex]->ignore_mark_offline = true;
++        clist[cindex]->no_mpg_user_fallback = true;
+         cindex++;
+     }
+ 
+diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h
+index 44dbc3fb0678412f46366321e0be836313380949..21cb57b0e7b265972db74ac78a3c1fb4ba2a9529 100644
+--- a/src/providers/ldap/ldap_common.h
++++ b/src/providers/ldap/ldap_common.h
+@@ -57,6 +57,8 @@ struct sdap_id_conn_ctx {
+     struct sdap_id_conn_ctx *prev, *next;
+     /* do not go offline, try another connection */
+     bool ignore_mark_offline;
++    /* do not fall back to user lookups for mpg domains on this connection */
++    bool no_mpg_user_fallback;
+ };
+ 
+ struct sdap_id_ctx {
+diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c
+index 3824f8f9aa8d2892664f1182376bedf6fb8627f6..365d90fd1cdfba86c719b3669d057444a7449d66 100644
+--- a/src/providers/ldap/ldap_id.c
++++ b/src/providers/ldap/ldap_id.c
+@@ -1076,7 +1076,8 @@ static void groups_get_done(struct tevent_req *subreq)
+     }
+ 
+     if (ret == ENOENT
+-            && state->domain->mpg == true) {
++            && state->domain->mpg == true
++            && !state->conn->no_mpg_user_fallback) {
+         /* The requested filter did not find a group. Before giving up, we must
+          * also check if the GID can be resolved through a primary group of a
+          * user
+-- 
+2.14.4
+
diff --git a/SOURCES/0123-deskprofile-don-t-bail-if-we-fail-to-save-one-profil.patch b/SOURCES/0123-deskprofile-don-t-bail-if-we-fail-to-save-one-profil.patch
new file mode 100644
index 0000000..85fe196
--- /dev/null
+++ b/SOURCES/0123-deskprofile-don-t-bail-if-we-fail-to-save-one-profil.patch
@@ -0,0 +1,45 @@
+From 7f698a050cea2baad34c84f8ae9e611dbf03ac7f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
+Date: Thu, 12 Jul 2018 23:55:03 +0200
+Subject: [PATCH] deskprofile: don't bail if we fail to save one profile
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Due to different reasons (a bug on fleet-commander, for instance?) we
+may face the situation where one profile ends up stored in freeipa on a
+half-broken state (with no data, for instance).
+
+In case it happens, we should try our best to save the not broken
+profiles and just skip the broken ones instead of bailing the whole
+operation.
+
+Resolves:
+https://pagure.io/SSSD/sssd/issue/3773
+
+Signed-off-by: Fabiano Fidêncio <fidencio@redhat.com>
+Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
+(cherry picked from commit efd6702e5f70bb3df0f840dd3ce9f8f9264661ba)
+
+DOWNSTREAM:
+Resolves: rhbz#1601360 - SSSD bails out saving desktop profiles in case an invalid profile is found [rhel-7.5.z]
+---
+ src/providers/ipa/ipa_session.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/src/providers/ipa/ipa_session.c b/src/providers/ipa/ipa_session.c
+index 3c7dd33c30ac6331319fc62cac466c4fbf04c0a5..a93c21224ee4d539d412e61ea06cbde3b928416a 100644
+--- a/src/providers/ipa/ipa_session.c
++++ b/src/providers/ipa/ipa_session.c
+@@ -766,7 +766,7 @@ ipa_pam_session_handler_save_deskprofile_rules(
+             DEBUG(SSSDBG_OP_FAILURE,
+                   "Failed to save a Desktop Profile Rule to disk [%d]: %s\n",
+                   ret, sss_strerror(ret));
+-            goto done;
++            continue;
+         }
+     }
+ 
+-- 
+2.14.4
+
diff --git a/SPECS/sssd.spec b/SPECS/sssd.spec
index 197046c..475c27c 100644
--- a/SPECS/sssd.spec
+++ b/SPECS/sssd.spec
@@ -48,7 +48,7 @@
 
 Name: sssd
 Version: 1.16.0
-Release: 19%{?dist}.5
+Release: 19%{?dist}.8
 Group: Applications/System
 Summary: System Security Services Daemon
 License: GPLv3+
@@ -170,6 +170,18 @@ Patch0108: 0108-NSS-nss_clear_netgroup_hash_table-do-not-free-data.patch
 Patch0109: 0109-winbind-idmap-plugin-support-inferface-version-6.patch
 Patch0110: 0110-winbind-idmap-plugin-fix-detection.patch
 Patch0111: 0111-Do-not-keep-allocating-external-groups-on-a-long-liv.patch
+Patch0112: 0112-TESTS-Extend-the-schema-with-sshPublicKey-attribute.patch
+Patch0113: 0113-TESTS-Allow-adding-sshPublicKey-for-users.patch
+Patch0114: 0114-TESTS-Add-a-basic-SSH-responder-test.patch
+Patch0115: 0115-SSH-Do-not-exit-abruptly-if-SSHD-closes-its-end-of-t.patch
+Patch0116: 0116-TESTS-Add-a-helper-binary-that-can-trigger-the-SIGPI.patch
+Patch0117: 0117-TESTS-Add-a-regression-test-for-SIGHUP-handling-in-s.patch
+Patch0118: 0118-TESTS-Order-list-of-entries-in-some-lists.patch
+Patch0119: 0119-sysdb-add-sysdb_getgrgid_attrs.patch
+Patch0120: 0120-ipa-use-mpg-aware-group-lookup-in-get_object_from_ca.patch
+Patch0121: 0121-ipa-allow-mpg-group-objects-in-apply_subdomain_homed.patch
+Patch0122: 0122-AD-LDAP-do-not-fall-back-to-mpg-user-lookup-on-GC-co.patch
+Patch0123: 0123-deskprofile-don-t-bail-if-we-fail-to-save-one-profil.patch
 
 #This patch should not be removed in RHEL-7
 Patch999: 0999-NOUPSTREAM-Default-to-root-if-sssd-user-is-not-spec
@@ -1317,6 +1329,15 @@ systemctl try-restart sssd >/dev/null 2>&1 || :
 }
 
 %changelog
+* Thu Jul 26 2018 Jakub Hrozek <jhrozek@redhat.com> - 1.16.0-19.8
+- Resolves: rhbz#1601360 - SSSD bails out saving desktop profiles in case an invalid profile is found [rhel-7.5.z]
+
+* Tue Jul 24 2018 Jakub Hrozek <jhrozek@redhat.com> - 1.16.0-19.7
+- Resolves: rhbz#1596292 - home dir disappear in sssd cache on the IPA master for AD users [rhel-7.5.z]
+
+* Fri Jul 20 2018 Jakub Hrozek <jhrozek@redhat.com> - 1.16.0-19.6
+- Resolves: rhbz#1594178 - Login with sshkeys stored in ipa not working after update to RHEL-7.5 [rhel-7.5.z]
+
 * Thu May 31 2018 Fabiano Fidêncio <fidencio@redhat.com> - 1.16.0-19.5
 - Resolves: rhbz#1583746 - The SSSD IPA provider allocates information about external groups on a long lived memory context, causing memory growth of the sssd_be process [rhel-7.5.z]