-
Notifications
You must be signed in to change notification settings - Fork 20
Add support for multi-token management in SmartCardUtils #239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -76,12 +76,43 @@ def initialize_card(self, label: str = "sc_test", so_pin: str = "12345678", user | |||||
| self.cli.command("softhsm2-util --init-token", args), env={"SOFTHSM2_CONF": self.SOFTHSM2_CONF_PATH} | ||||||
| ) | ||||||
|
|
||||||
| def initialize_additional_token(self, label: str, so_pin: str = "12345678", user_pin: str = "123456") -> None: | ||||||
| """ | ||||||
| Initializes an additional SoftHSM token without wiping existing tokens. | ||||||
|
|
||||||
| Unlike :meth:`initialize_card`, this method does **not** remove the | ||||||
| existing token storage directory. It simply calls | ||||||
| ``softhsm2-util --init-token --free`` with the given *label* so that a | ||||||
| new slot is allocated alongside any tokens that are already present. | ||||||
|
|
||||||
| Use this after an initial :meth:`initialize_card` call to create a | ||||||
| multi-token environment (simulating multiple physical smart cards). | ||||||
|
|
||||||
| :param label: Token label (must be unique among existing tokens). | ||||||
| :type label: str | ||||||
| :param so_pin: Security Officer PIN, defaults to "12345678" | ||||||
| :type so_pin: str, optional | ||||||
| :param user_pin: User PIN, defaults to "123456" | ||||||
| :type user_pin: str, optional | ||||||
| """ | ||||||
| args: CLIBuilderArgs = { | ||||||
| "label": (self.cli.option.VALUE, label), | ||||||
| "free": (self.cli.option.SWITCH, True), | ||||||
| "so-pin": (self.cli.option.VALUE, so_pin), | ||||||
| "pin": (self.cli.option.VALUE, user_pin), | ||||||
| } | ||||||
| self.host.conn.run( | ||||||
| self.cli.command("softhsm2-util --init-token", args), env={"SOFTHSM2_CONF": self.SOFTHSM2_CONF_PATH} | ||||||
| ) | ||||||
|
Comment on lines
+98
to
+106
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for building arguments and calling |
||||||
|
|
||||||
| def add_cert( | ||||||
| self, | ||||||
| cert_path: str, | ||||||
| cert_id: str = "01", | ||||||
| pin: str = "123456", | ||||||
| private: bool | None = False, | ||||||
| token_label: str | None = None, | ||||||
| label: str | None = None, | ||||||
| ) -> None: | ||||||
| """ | ||||||
| Adds a certificate or private key to the smart card. | ||||||
|
|
@@ -94,6 +125,15 @@ def add_cert( | |||||
| :type pin: str, optional | ||||||
| :param private: Whether the object is a private key. Defaults to False. | ||||||
| :type private: bool, optional | ||||||
| :param token_label: Label of the target token. When ``None`` (the | ||||||
| default) ``pkcs11-tool`` writes to the first available token. | ||||||
| Set this when multiple tokens exist to target a specific one. | ||||||
| :type token_label: str | None, optional | ||||||
| :param label: Label for the PKCS#11 object being written. Required | ||||||
| when ``p11_child`` accesses the token directly (i.e. without | ||||||
| ``virt_cacard``), because the response parser expects a | ||||||
| non-empty label. | ||||||
| :type label: str | None, optional | ||||||
| """ | ||||||
| obj_type = "privkey" if private else "cert" | ||||||
| args: CLIBuilderArgs = { | ||||||
|
|
@@ -104,9 +144,20 @@ def add_cert( | |||||
| "type": (self.cli.option.VALUE, obj_type), | ||||||
| "id": (self.cli.option.VALUE, cert_id), | ||||||
| } | ||||||
| if token_label is not None: | ||||||
| args["token-label"] = (self.cli.option.VALUE, token_label) | ||||||
| if label is not None: | ||||||
| args["label"] = (self.cli.option.VALUE, label) | ||||||
| self.host.conn.run(self.cli.command("pkcs11-tool", args), env={"SOFTHSM2_CONF": self.SOFTHSM2_CONF_PATH}) | ||||||
|
|
||||||
| def add_key(self, key_path: str, key_id: str = "01", pin: str = "123456") -> None: | ||||||
| def add_key( | ||||||
| self, | ||||||
| key_path: str, | ||||||
| key_id: str = "01", | ||||||
| pin: str = "123456", | ||||||
| token_label: str | None = None, | ||||||
| label: str | None = None, | ||||||
| ) -> None: | ||||||
| """ | ||||||
| Adds a private key to the smart card. | ||||||
|
|
||||||
|
|
@@ -116,8 +167,12 @@ def add_key(self, key_path: str, key_id: str = "01", pin: str = "123456") -> Non | |||||
| :type key_id: str, optional | ||||||
| :param pin: User PIN, defaults to "123456" | ||||||
| :type pin: str, optional | ||||||
| :param token_label: Label of the target token (see :meth:`add_cert`). | ||||||
| :type token_label: str | None, optional | ||||||
| :param label: Label for the PKCS#11 object (see :meth:`add_cert`). | ||||||
| :type label: str | None, optional | ||||||
| """ | ||||||
| self.add_cert(cert_path=key_path, cert_id=key_id, pin=pin, private=True) | ||||||
| self.add_cert(cert_path=key_path, cert_id=key_id, pin=pin, private=True, token_label=token_label, label=label) | ||||||
|
|
||||||
| def generate_cert( | ||||||
| self, | ||||||
|
|
@@ -150,6 +205,40 @@ def generate_cert( | |||||
| self.host.conn.run(self.cli.command("openssl req", args)) | ||||||
| return key_path, cert_path | ||||||
|
|
||||||
| def register_for_p11_child(self) -> None: | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm thinking this should move to the SSSDCommonConfiguration class along with the settings from SSSD/sssd#8519 for configure_sssd_for_smartcard(). You could create a smartcard_with_softhsm() method that wraps all of that together into a single method you could call from the test with something like: |
||||||
| """ | ||||||
| Register SoftHSM as a system-wide p11-kit module visible to ``p11_child``. | ||||||
|
|
||||||
| This is required for multi-token scenarios where ``p11_child`` must | ||||||
| iterate all PKCS#11 slots. The method: | ||||||
|
|
||||||
| 1. Ensures ``slots.removable = true`` is set in the SoftHSM config. | ||||||
| 2. Copies the SoftHSM config to ``/etc/softhsm2.conf`` so that | ||||||
| ``p11_child`` (running as the *sssd* user) can find it without | ||||||
| ``SOFTHSM2_CONF`` in its environment. | ||||||
| 3. Registers the SoftHSM module with p11-kit. | ||||||
| 4. Creates a systemd drop-in for ``sssd.service`` that exports | ||||||
| ``SOFTHSM2_CONF``. | ||||||
| 5. Adjusts file permissions so the *sssd* user can access the token | ||||||
| storage directory. | ||||||
| """ | ||||||
| conf = self.SOFTHSM2_CONF_PATH | ||||||
| module = "/usr/lib64/pkcs11/libsofthsm2.so" | ||||||
|
|
||||||
| self.host.conn.run(f"grep -q 'slots.removable' {conf} || echo 'slots.removable = true' >> {conf}") | ||||||
| self.host.conn.run(f"cp {conf} /etc/softhsm2.conf") | ||||||
| self.host.conn.run(f'echo "module: {module}" > /etc/pkcs11/modules/softhsm2.module') | ||||||
| self.fs.mkdir_p("/etc/systemd/system/sssd.service.d") | ||||||
| self.host.conn.run( | ||||||
| f'printf "[Service]\\nEnvironment=SOFTHSM2_CONF={conf}\\n" ' | ||||||
| f"> /etc/systemd/system/sssd.service.d/softhsm.conf" | ||||||
| ) | ||||||
| self.host.conn.run("systemctl daemon-reload") | ||||||
| self.host.conn.run("chmod -R o+rX /opt/test_ca/") | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The command
Suggested change
|
||||||
| self.host.conn.run( | ||||||
| f"chown -R sssd:sssd {self.TOKEN_STORAGE_PATH}/ " f"&& chmod -R 770 {self.TOKEN_STORAGE_PATH}/" | ||||||
| ) | ||||||
|
|
||||||
| def insert_card(self) -> None: | ||||||
| """ | ||||||
| Simulates card insertion by starting the smart card service. | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the initialize_card() method be modified to take an option to skip the cleanup if set instead of having an additional method?