This file is indexed.

/usr/lib/python2.7/dist-packages/ipaserver/advise/plugins/smart_card_auth.py is in python-ipaserver 4.7.0~pre1+git20180411-2ubuntu2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#
# Copyright (C) 2017 FreeIPA Contributors see COPYING for license
#

from ipalib.plugable import Registry
from ipaplatform import services
from ipaplatform.paths import paths
from ipaserver.advise.base import Advice
from ipaserver.install.httpinstance import OCSP_ENABLED, OCSP_DIRECTIVE

register = Registry()


class common_smart_card_auth_config(Advice):
    """
    Common steps required to properly configure both server and client for
    smart card auth
    """

    systemwide_nssdb = paths.NSS_DB_DIR
    smart_card_ca_certs_variable_name = "SC_CA_CERTS"
    single_ca_cert_variable_name = 'ca_cert'

    def check_ccache_not_empty(self):
        self.log.comment('Check whether the credential cache is not empty')
        self.log.exit_on_failed_command(
            'klist',
            [
                "Credential cache is empty",
                'Use kinit as privileged user to obtain Kerberos credentials'
            ])

    def check_and_set_ca_cert_paths(self):
        ca_paths_variable = self.smart_card_ca_certs_variable_name
        single_ca_path_variable = self.single_ca_cert_variable_name

        self.log.command("{}=$@".format(ca_paths_variable))
        self.log.exit_on_predicate(
            '[ -z "${}" ]'.format(ca_paths_variable),
            ['You need to provide one or more paths to the PEM files '
             'containing CAs signing the Smart Cards']
        )
        with self.log.for_loop(single_ca_path_variable,
                               '${}'.format(ca_paths_variable)):
            self.log.exit_on_predicate(
                '[ ! -f "${}" ]'.format(single_ca_path_variable),
                ['Invalid CA certificate filename: ${}'.format(
                    single_ca_path_variable),
                 'Please check that the path exists and is a valid file']
            )

    def upload_smartcard_ca_certificates_to_systemwide_db(self):
        with self.log.for_loop(
                self.single_ca_cert_variable_name,
                '${}'.format(self.smart_card_ca_certs_variable_name)):
            self.log.command(
                'certutil -d {} -A -i ${} -n "Smart Card CA $(uuidgen)" '
                '-t CT,C,C'.format(
                    self.systemwide_nssdb, self.single_ca_cert_variable_name
                )
            )

    def install_smart_card_signing_ca_certs(self):
        with self.log.for_loop(
                self.single_ca_cert_variable_name,
                '${}'.format(self.smart_card_ca_certs_variable_name)):
            self.log.exit_on_failed_command(
                'ipa-cacert-manage install ${} -t CT,C,C'.format(
                    self.single_ca_cert_variable_name
                ),
                ['Failed to install external CA certificate to IPA']
            )

    def update_ipa_ca_certificate_store(self):
        self.log.exit_on_failed_command(
            'ipa-certupdate',
            ['Failed to update IPA CA certificate database']
        )


@register()
class config_server_for_smart_card_auth(common_smart_card_auth_config):
    """
    Configures smart card authentication via Kerberos (PKINIT) and for WebUI
    """

    description = ("Instructions for enabling Smart Card authentication on "
                   " a single FreeIPA server. Includes Apache configuration, "
                   "enabling PKINIT on KDC and configuring WebUI to accept "
                   "Smart Card auth requests. To enable the feature in the "
                   "whole topology you have to run the script on each master")

    nss_conf = paths.HTTPD_NSS_CONF
    nss_ocsp_directive = OCSP_DIRECTIVE
    kdc_service_name = services.knownservices.krb5kdc.systemd_name

    def get_info(self):
        self.log.exit_on_nonroot_euid()
        self.check_and_set_ca_cert_paths()
        self.check_ccache_not_empty()
        self.check_hostname_is_in_masters()
        self.resolve_ipaca_records()
        self.enable_nss_ocsp()
        self.restart_httpd()
        self.record_httpd_ocsp_status()
        self.check_and_enable_pkinit()
        self.enable_ok_to_auth_as_delegate_on_http_principal()
        self.upload_smartcard_ca_certificates_to_systemwide_db()
        self.install_smart_card_signing_ca_certs()
        self.update_ipa_ca_certificate_store()
        self.restart_kdc()

    def check_hostname_is_in_masters(self):
        self.log.comment('Check whether the host is IPA master')
        self.log.exit_on_failed_command(
            'ipa server-find $(hostname -f)',
            ["This script can be run on IPA master only"])

    def resolve_ipaca_records(self):
        ipa_domain_name = self.api.env.domain

        self.log.comment('make sure bind-utils are installed so that we can '
                         'dig for ipa-ca records')
        self.log.exit_on_failed_command(
            'yum install -y bind-utils',
            ['Failed to install bind-utils'])

        self.log.comment('make sure ipa-ca records are resolvable, '
                         'otherwise error out and instruct')
        self.log.comment('the user to update the DNS infrastructure')
        self.log.command('ipaca_records=$(dig +short '
                         'ipa-ca.{})'.format(ipa_domain_name))

        self.log.exit_on_predicate(
            '[ -z "$ipaca_records" ]',
            [
                'Can not resolve ipa-ca records for ${domain_name}',
                'Please make sure to update your DNS infrastructure with ',
                'ipa-ca record pointing to IP addresses of IPA CA masters'
            ])

    def enable_nss_ocsp(self):
        self.log.comment('look for the OCSP directive in nss.conf')
        self.log.comment(' if it is present, switch it on')
        self.log.comment(
            'if it is absent, append it to the end of VirtualHost section')
        predicate = self._interpolate_ocsp_directive_file_into_command(
            "grep -q '{directive} ' {filename}")

        self.log.commands_on_predicate(
            predicate,
            [
                self._interpolate_ocsp_directive_file_into_command(
                    "sed -i.ipabkp -r "
                    "'s/^#*[[:space:]]*{directive}[[:space:]]+(on|off)$"
                    "/{directive} on/' {filename}")
            ],
            commands_to_run_when_false=[
                self._interpolate_ocsp_directive_file_into_command(
                    "sed -i.ipabkp '/<\/VirtualHost>/i {directive} on' "
                    "{filename}")
            ]
        )

    def _interpolate_ocsp_directive_file_into_command(self, fmt_line):
        return self._format_command(
            fmt_line, self.nss_ocsp_directive, self.nss_conf)

    def _format_command(self, fmt_line, directive, filename):
        return fmt_line.format(directive=directive, filename=filename)

    def restart_httpd(self):
        self.log.comment('finally restart apache')
        self.log.command('systemctl restart httpd')

    def record_httpd_ocsp_status(self):
        self.log.comment('store the OCSP upgrade state')
        self.log.command(
            "python -c 'from ipaserver.install import sysupgrade; "
            "sysupgrade.set_upgrade_state(\"httpd\", "
            "\"{}\", True)'".format(OCSP_ENABLED))

    def check_and_enable_pkinit(self):
        self.log.comment('check whether PKINIT is configured on the master')
        with self.log.if_branch(
                "ipa-pkinit-manage status | grep -q 'enabled'"):
            self.log.command('echo "PKINIT already enabled"')
        with self.log.else_branch():
            self.log.exit_on_failed_command(
                'ipa-pkinit-manage enable',
                ['Failed to issue PKINIT certificates to local KDC'])

    def enable_ok_to_auth_as_delegate_on_http_principal(self):
        self.log.comment('Enable OK-AS-DELEGATE flag on the HTTP principal')
        self.log.comment('This enables smart card login to WebUI')
        self.log.command(
            'output=$(ipa service-mod HTTP/$(hostname -f) '
            '--ok-to-auth-as-delegate=True 2>&1)')
        self.log.exit_on_predicate(
            '[ "$?" -ne "0" -a '
            '-z "$(echo $output | grep \'no modifications\')" ]',
            ["Failed to set OK_AS_AUTH_AS_DELEGATE flag on HTTP principal"]
        )

    def restart_kdc(self):
        self.log.exit_on_failed_command(
            'systemctl restart {}'.format(self.kdc_service_name),
            ['Failed to restart KDC. Please restart the service manually.']
        )


@register()
class config_client_for_smart_card_auth(common_smart_card_auth_config):
    """
    Configures smart card authentication on FreeIPA client
    """

    description = ("Instructions for enabling Smart Card authentication on "
                   " a single FreeIPA client. Configures Smart Card daemon, "
                   "set the system-wide trust store and configures SSSD to "
                   "allow smart card logins to desktop")

    opensc_module_name = "OpenSC"
    pkcs11_shared_lib = '/usr/lib64/opensc-pkcs11.so'
    smart_card_service_file = 'pcscd.service'
    smart_card_socket = 'pcscd.socket'

    def get_info(self):
        self.log.exit_on_nonroot_euid()
        self.check_and_set_ca_cert_paths()
        self.check_ccache_not_empty()
        self.check_and_remove_pam_pkcs11()
        self.install_opensc_and_dconf_packages()
        self.install_krb5_client_dependencies()
        self.start_enable_smartcard_daemon()
        self.add_pkcs11_module_to_systemwide_db()
        self.upload_smartcard_ca_certificates_to_systemwide_db()
        self.update_ipa_ca_certificate_store()
        self.run_authconfig_to_configure_smart_card_auth()
        self.restart_sssd()

    def check_and_remove_pam_pkcs11(self):
        self.log.command('rpm -qi pam_pkcs11 > /dev/null')
        self.log.commands_on_predicate(
            '[ "$?" -eq "0" ]',
            [
                'yum remove -y pam_pkcs11'
            ]
        )

    def install_opensc_and_dconf_packages(self):
        self.log.comment(
            'authconfig often complains about missing dconf, '
            'install it explicitly')
        self.log.exit_on_failed_command(
            'yum install -y {} dconf'.format(self.opensc_module_name.lower()),
            ['Could not install OpenSC package']
        )

    def install_krb5_client_dependencies(self):
        self.log.exit_on_failed_command(
            'yum install -y krb5-pkinit-openssl',
            ['Failed to install Kerberos client PKINIT extensions.']
        )

    def start_enable_smartcard_daemon(self):
        self.log.command(
            'systemctl start {service} {socket} '
            '&& systemctl enable {service} {socket}'.format(
                service=self.smart_card_service_file,
                socket=self.smart_card_socket))

    def add_pkcs11_module_to_systemwide_db(self):
        module_name = self.opensc_module_name
        nssdb = self.systemwide_nssdb
        shared_lib = self.pkcs11_shared_lib

        self.log.commands_on_predicate(
            'modutil -dbdir {} -list | grep -q {}'.format(
                nssdb, module_name),
            [
                'echo "{} PKCS#11 module already configured"'.format(
                    module_name)
            ],
            commands_to_run_when_false=[
                'echo "" | modutil -dbdir {} -add "{}" -libfile {}'.format(
                    nssdb, module_name, shared_lib),
            ]
        )

    def run_authconfig_to_configure_smart_card_auth(self):
        self.log.exit_on_failed_command(
             'authconfig --enablesssd --enablesssdauth --enablesmartcard '
             '--smartcardmodule=sssd --smartcardaction=1 --updateall',
            [
                'Failed to configure Smart Card authentication in SSSD'
            ]
        )

    def restart_sssd(self):
        self.log.command('systemctl restart sssd.service')