Tests: Smart card authentication tests for SSSD's CKF_PROTECTED_AUTH…#8744
Tests: Smart card authentication tests for SSSD's CKF_PROTECTED_AUTH…#8744krishnavema wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive suite of system tests for SSSD smart card pinpad authentication, covering scenarios such as successful login via su, correct external keypad prompting, local user authentication, and keyboard-less PIN entry. The review feedback correctly identifies a security vulnerability where sensitive private key files are written to /tmp with default permissions and are not cleaned up after execution. It is recommended to restrict these files to 0600 permissions and ensure they are deleted in a finally block to prevent credential leakage.
| client.fs.write(pem_cert, cert_content) | ||
| client.fs.write(pem_key, key_content) | ||
|
|
||
| x509_args: CLIBuilderArgs = { | ||
| "in": (client.host.cli.option.VALUE, pem_cert), | ||
| "outform": (client.host.cli.option.VALUE, "DER"), | ||
| "out": (client.host.cli.option.VALUE, der_cert), | ||
| } | ||
| client.host.conn.run(client.host.cli.command("openssl x509", x509_args)) | ||
|
|
||
| rsa_args: CLIBuilderArgs = { | ||
| "in": (client.host.cli.option.VALUE, pem_key), | ||
| "outform": (client.host.cli.option.VALUE, "DER"), | ||
| "out": (client.host.cli.option.VALUE, der_key), | ||
| } | ||
| client.host.conn.run(client.host.cli.command("openssl rsa", rsa_args)) | ||
|
|
||
| for obj_path, obj_type in [(der_key, "privkey"), (der_cert, "cert")]: | ||
| args: CLIBuilderArgs = { | ||
| "module": (client.host.cli.option.VALUE, OPENSC_MODULE), | ||
| "login": (client.host.cli.option.SWITCH, True), | ||
| "pin": (client.host.cli.option.VALUE, pin), | ||
| "write-object": (client.host.cli.option.VALUE, obj_path), | ||
| "type": (client.host.cli.option.VALUE, obj_type), | ||
| "id": (client.host.cli.option.VALUE, cert_id), | ||
| "label": (client.host.cli.option.VALUE, username), | ||
| } | ||
| client.host.conn.run(client.host.cli.command("pkcs11-tool", args)) |
There was a problem hiding this comment.
The private key files are written to /tmp with default permissions, which can make them readable by other local users on the system. Additionally, these temporary files are never cleaned up from the filesystem after the test completes, leading to sensitive credential leakage.
To resolve this, restrict the permissions of the private key files to 0600 immediately after creation, and ensure all temporary files are cleaned up in a finally block.
try:
client.fs.write(pem_cert, cert_content)
client.fs.write(pem_key, key_content)
client.host.conn.run(f"chmod 600 {pem_key}", raise_on_error=False)
x509_args: CLIBuilderArgs = {
"in": (client.host.cli.option.VALUE, pem_cert),
"outform": (client.host.cli.option.VALUE, "DER"),
"out": (client.host.cli.option.VALUE, der_cert),
}
client.host.conn.run(client.host.cli.command("openssl x509", x509_args))
rsa_args: CLIBuilderArgs = {
"in": (client.host.cli.option.VALUE, pem_key),
"outform": (client.host.cli.option.VALUE, "DER"),
"out": (client.host.cli.option.VALUE, der_key),
}
client.host.conn.run(client.host.cli.command("openssl rsa", rsa_args))
client.host.conn.run(f"chmod 600 {der_key}", raise_on_error=False)
for obj_path, obj_type in [(der_key, "privkey"), (der_cert, "cert")]:
args: CLIBuilderArgs = {
"module": (client.host.cli.option.VALUE, OPENSC_MODULE),
"login": (client.host.cli.option.SWITCH, True),
"pin": (client.host.cli.option.VALUE, pin),
"write-object": (client.host.cli.option.VALUE, obj_path),
"type": (client.host.cli.option.VALUE, obj_type),
"id": (client.host.cli.option.VALUE, cert_id),
"label": (client.host.cli.option.VALUE, username),
}
client.host.conn.run(client.host.cli.command("pkcs11-tool", args))
finally:
client.host.conn.run(f"rm -f {pem_cert} {pem_key} {der_cert} {der_key}", raise_on_error=False)| try: | ||
| req_args: CLIBuilderArgs = { | ||
| "x509": (client.host.cli.option.SWITCH, True), | ||
| "nodes": (client.host.cli.option.SWITCH, True), | ||
| "sha256": (client.host.cli.option.SWITCH, True), | ||
| "days": (client.host.cli.option.VALUE, "365"), | ||
| "newkey": (client.host.cli.option.VALUE, "rsa:2048"), | ||
| "keyout": (client.host.cli.option.VALUE, key_pem), | ||
| "out": (client.host.cli.option.VALUE, cert_pem), | ||
| "subj": (client.host.cli.option.VALUE, "/CN=Pinpad Test Cert"), | ||
| } | ||
| client.host.conn.run(client.host.cli.command("openssl req", req_args)) | ||
|
|
||
| x509_args: CLIBuilderArgs = { | ||
| "in": (client.host.cli.option.VALUE, cert_pem), | ||
| "outform": (client.host.cli.option.VALUE, "DER"), | ||
| "out": (client.host.cli.option.VALUE, cert_der), | ||
| } | ||
| client.host.conn.run(client.host.cli.command("openssl x509", x509_args)) | ||
|
|
||
| rsa_args: CLIBuilderArgs = { | ||
| "in": (client.host.cli.option.VALUE, key_pem), | ||
| "outform": (client.host.cli.option.VALUE, "DER"), | ||
| "out": (client.host.cli.option.VALUE, key_der), | ||
| } | ||
| client.host.conn.run(client.host.cli.command("openssl rsa", rsa_args)) | ||
|
|
||
| for obj_path, obj_type in [(key_der, "privkey"), (cert_der, "cert")]: | ||
| args: CLIBuilderArgs = { | ||
| "module": (client.host.cli.option.VALUE, OPENSC_MODULE), | ||
| "login": (client.host.cli.option.SWITCH, True), | ||
| "pin": (client.host.cli.option.VALUE, TOKEN_PIN), | ||
| "write-object": (client.host.cli.option.VALUE, obj_path), | ||
| "type": (client.host.cli.option.VALUE, obj_type), | ||
| "id": (client.host.cli.option.VALUE, cert_id), | ||
| "label": (client.host.cli.option.VALUE, username), | ||
| } | ||
| client.host.conn.run(client.host.cli.command("pkcs11-tool", args)) | ||
|
|
||
| ensure_pcscd_accessible(client) | ||
|
|
||
| client.host.fs.rm("/etc/sssd/pki/sssd_auth_ca_db.pem") | ||
| cert_data = client.host.fs.read(cert_pem) | ||
| client.host.fs.append("/etc/sssd/pki/sssd_auth_ca_db.pem", cert_data) | ||
|
|
||
| client.authselect.select("sssd", ["with-smartcard"]) | ||
| client.sssd.common.local() | ||
| client.sssd.dom("local")["local_auth_policy"] = "only" | ||
| client.sssd.section(f"certmap/local/{username}")["matchrule"] = ( | ||
| "<SUBJECT>.*CN=Pinpad Test Cert.*" | ||
| ) | ||
| client.sssd.pam["pam_cert_auth"] = "True" | ||
| client.sssd.pam["p11_child_timeout"] = "60" | ||
| client.sssd.start(debug_level="0xFFF0", check_config=False) | ||
|
|
||
| result = client.host.conn.run( | ||
| f"su - {username} -c 'su - {username} -c whoami'", | ||
| raise_on_error=False, | ||
| ) | ||
|
|
||
| output = result.stdout + result.stderr | ||
| assert "Use external keypad" in output, ( | ||
| f"Expected 'Use external keypad' prompt but got: " | ||
| f"stdout={result.stdout!r}, stderr={result.stderr!r}" | ||
| ) | ||
| assert result.rc == 0, ( | ||
| f"Authentication failed with rc={result.rc}, " | ||
| f"stdout={result.stdout!r}, stderr={result.stderr!r}" | ||
| ) | ||
| assert username in result.stdout, ( | ||
| f"'{username}' not found in whoami output: {result.stdout}" | ||
| ) | ||
| finally: | ||
| cleanup_hw_card(client, cert_id=cert_id) |
There was a problem hiding this comment.
The generated self-signed private key files are written to /tmp with default permissions and are never cleaned up from the filesystem after the test completes, leading to sensitive credential leakage.
To resolve this, restrict the permissions of the private key files to 0600 immediately after creation, and ensure all temporary files are cleaned up in the finally block.
try:
req_args: CLIBuilderArgs = {
"x509": (client.host.cli.option.SWITCH, True),
"nodes": (client.host.cli.option.SWITCH, True),
"sha256": (client.host.cli.option.SWITCH, True),
"days": (client.host.cli.option.VALUE, "365"),
"newkey": (client.host.cli.option.VALUE, "rsa:2048"),
"keyout": (client.host.cli.option.VALUE, key_pem),
"out": (client.host.cli.option.VALUE, cert_pem),
"subj": (client.host.cli.option.VALUE, "/CN=Pinpad Test Cert"),
}
client.host.conn.run(client.host.cli.command("openssl req", req_args))
client.host.conn.run(f"chmod 600 {key_pem}", raise_on_error=False)
x509_args: CLIBuilderArgs = {
"in": (client.host.cli.option.VALUE, cert_pem),
"outform": (client.host.cli.option.VALUE, "DER"),
"out": (client.host.cli.option.VALUE, cert_der),
}
client.host.conn.run(client.host.cli.command("openssl x509", x509_args))
rsa_args: CLIBuilderArgs = {
"in": (client.host.cli.option.VALUE, key_pem),
"outform": (client.host.cli.option.VALUE, "DER"),
"out": (client.host.cli.option.VALUE, key_der),
}
client.host.conn.run(client.host.cli.command("openssl rsa", rsa_args))
client.host.conn.run(f"chmod 600 {key_der}", raise_on_error=False)
for obj_path, obj_type in [(key_der, "privkey"), (cert_der, "cert")]:
args: CLIBuilderArgs = {
"module": (client.host.cli.option.VALUE, OPENSC_MODULE),
"login": (client.host.cli.option.SWITCH, True),
"pin": (client.host.cli.option.VALUE, TOKEN_PIN),
"write-object": (client.host.cli.option.VALUE, obj_path),
"type": (client.host.cli.option.VALUE, obj_type),
"id": (client.host.cli.option.VALUE, cert_id),
"label": (client.host.cli.option.VALUE, username),
}
client.host.conn.run(client.host.cli.command("pkcs11-tool", args))
ensure_pcscd_accessible(client)
client.host.fs.rm("/etc/sssd/pki/sssd_auth_ca_db.pem")
cert_data = client.host.fs.read(cert_pem)
client.host.fs.append("/etc/sssd/pki/sssd_auth_ca_db.pem", cert_data)
client.authselect.select("sssd", ["with-smartcard"])
client.sssd.common.local()
client.sssd.dom("local")["local_auth_policy"] = "only"
client.sssd.section(f"certmap/local/{username}")["matchrule"] = (
"<SUBJECT>.*CN=Pinpad Test Cert.*"
)
client.sssd.pam["pam_cert_auth"] = "True"
client.sssd.pam["p11_child_timeout"] = "60"
client.sssd.start(debug_level="0xFFF0", check_config=False)
result = client.host.conn.run(
f"su - {username} -c 'su - {username} -c whoami'",
raise_on_error=False,
)
output = result.stdout + result.stderr
assert "Use external keypad" in output, (
f"Expected 'Use external keypad' prompt but got: "
f"stdout={result.stdout!r}, stderr={result.stderr!r}"
)
assert result.rc == 0, (
f"Authentication failed with rc={result.rc}, "
f3d8ac0b to
d0a8fd2
Compare
|
Removed the Test result: |
…ENTICATION_PATH (hardware pinpad) support
|
|
||
| OPENSC_MODULE = "/usr/lib64/pkcs11/opensc-pkcs11.so" | ||
| P11_CHILD_LOG = "/var/log/sssd/p11_child.log" | ||
| TOKEN_LABEL = "MyEID" |
There was a problem hiding this comment.
Hi,
I think a more generic label like e.g. "SSSD Test Token" or similar would be better.
bye,
Sumit
| if slot_result.rc != 0 or "token" not in slot_result.stdout.lower(): | ||
| pytest.skip("Smart card reader found but no token present — insert a card") | ||
|
|
||
| if TOKEN_LABEL.lower() not in slot_result.stdout.lower(): |
There was a problem hiding this comment.
Hi,
maybe you can use a more specific match. Since the line you are interested in the the line with the PKCS#11 URI you can at least use something like `f"token={TOKEN_LABEL.lower()}"?
bye,
Sumit
| ) | ||
|
|
||
|
|
||
| def ensure_pcscd_accessible(client: Client) -> None: |
There was a problem hiding this comment.
Hi,
if you run all the check in detect_pinpad_reader() as sssd user you can drop this method.
bye,
Sumit
| 3. No ``C_Login failed`` in the p11_child log | ||
| :customerscenario: True | ||
| """ | ||
| detect_pinpad_reader(client) |
There was a problem hiding this comment.
Hi,
I think you removed some useful steps. I think it is ok to write and remove certificate and key to the card as long as the fixed TOKEN_LABEL is used to not accidentally overwrite some other certificate from a different token.
Assuming the card already has an expected certificate stored would lower the benefit of the test. If the test write the data to the card you only have to initialize a test card with the expected token label and then you can run the test. Otherwise you have to create the key and certificate (maybe even the CA), write them to the card manually and make the client aware of the CA certificate so that the validation does not fail.
Additionally, you removed the method which sets enable_pinpad = true in opensc.conf. This should be re-added as well.
bye,
Sumit
Smart card authentication tests for SSSD's CKF_PROTECTED_AUTHENTICATION_PATH (hardware pinpad) support, verifying that p11_child detects the flag, passes NULL PIN to C_Login, and PAM prompts for external keypad
entry instead of keyboard PIN input.