/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')
|