Skip to content
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

PAM: Multiple certificates on a Smartcard #433

Closed
wants to merge 9 commits into from

Conversation

sumit-bose
Copy link
Contributor

With this patch set SSSD will ask the user to select a certificate if multiple
certificates suitable for authentication are found on a Smartcard.

The first three patches are basically refactoring to use a list of structs
instead of individual variables. The third also adds the PAM conversations to
select the certificate.

The fourth patch makes sure that p11_child can address a specific certificate
as well.

The fifth adds the string shown to the user to identify a certificate
in the selection dialog.

The sixth makes sure the user name is available when the authentication request
is send to the backend.

Finally the seventh patch adds error messages explaining the NSS error codes in
p11_child to make debugging easier.

Resolves https://pagure.io/SSSD/sssd/issue/3560

@fidencio
Copy link
Contributor

fidencio commented Nov 3, 2017

@sumit-bose, as we discussed in our internal meeting, someone from our QA team would do the functional testing.

So far I've taken a look at the code and came up with the following changes: https://github.com/fidencio/sssd/tree/review/multiple_certs

The most part of the fixup patches are minors and could be ignored but "fixup! pam_sss: refactoring, use struct cert_auth_info" would be worth to have squashed.

I'll run coverity in those patches and wait for QA ack as well. Okay?

@fidencio
Copy link
Contributor

fidencio commented Nov 3, 2017

So, coverity found some issues:

  • Error: USE_AFTER_FREE (CWE-825): [#def1]
sssd-1.16.1/src/sss_client/pam_sss.c:154: alias: Assigning: "cai" = "cai->next". Now both point to the same storage.
sssd-1.16.1/src/sss_client/pam_sss.c:155: freed_arg: "free_cai" frees "cai".
sssd-1.16.1/src/sss_client/pam_sss.c:145:9: freed_arg: "free" frees parameter "cai".
sssd-1.16.1/src/sss_client/pam_sss.c:154: deref_after_free: Dereferencing freed pointer "cai".
#  152|   
#  153|       if (list != NULL) {
#  154|->         DLIST_FOR_EACH(cai, list) {
#  155|               free_cai(cai);
#  156|           }

Here it seems that a DLIST_FOR_EACH_SAFE() should be used.
Something like:

diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index b2968634e..e6d559b44 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -148,10 +148,11 @@ static void free_cai(struct cert_auth_info *cai)

 static void free_cert_list(struct cert_auth_info *list)
 {
-    struct cert_auth_info *cai;
+    struct cert_auth_info *cai, *cai_next;

     if (list != NULL) {
-        DLIST_FOR_EACH(cai, list) {
+        DLIST_FOR_EACH_SAFE(cai, cai_next, list) {
+            DLIST_REMOVE(list, cai);
             free_cai(cai);
         }
     }
  • Error: FORWARD_NULL (CWE-476): [#def2]
sssd-1.16.1/src/responder/pam/pamsrv_p11.c:433: var_compare_op: Comparing "pd->authtok" to null implies that "pd->authtok" might be null.
sssd-1.16.1/src/responder/pam/pamsrv_p11.c:461: var_deref_model: Passing null pointer "pd->authtok" to "sss_authtok_get_type", which dereferences it.
sssd-1.16.1/src/util/authtok.c:30:5: deref_parm: Directly dereferencing parameter "tok".
#   28|   enum sss_authtok_type sss_authtok_get_type(struct sss_auth_token *tok)
#   29|   {
#   30|->     return tok->type;
#   31|   }
#   32|   

This one could either be solved by a simple check on the called to ensure that tok is not NULL or by the suggested patch below:

diff --git a/src/util/authtok.c b/src/util/authtok.c
index c2f78be32..2c5a26ce3 100644
--- a/src/util/authtok.c
+++ b/src/util/authtok.c
@@ -27,6 +27,10 @@ struct sss_auth_token {

 enum sss_authtok_type sss_authtok_get_type(struct sss_auth_token *tok)
 {
+    if (tok == NULL) {
+        return SSS_AUTHTOK_TYPE_EMPTY;
+    }
+
     return tok->type;
 }

The patch above would also avoid a check done as part of the one patches to ensure that pd->authtok != NULL

@fidencio
Copy link
Contributor

fidencio commented Nov 3, 2017

I have updated the comments in order to reflect on what we discussed on IRC.

@sumit-bose
Copy link
Contributor Author

Thank you for the review, I include all your comments in the latest version.

@fidencio
Copy link
Contributor

fidencio commented Nov 7, 2017

After a few tries to run coverity in the new code I've finally able to and seems that no new issues have been introduced.

You have an ACK from me. @jhrozek would like to run some downstream tests (please, @jhrozek, let us know about the result as soon as you have those) and after that and Scott ACK this PR will be ready to go.

@sumit-bose, please, let us know about Scott's tests results.

@sumit-bose
Copy link
Contributor Author

Hi Fabiano,

thank you for the review. Scott's test are not done yet. He found that just showing the subject-DN to select a certificate is not sufficient for some types of Smartcards where all certificates will have the same subject-DN. I prepared a new build for him which he hopefully can test today.

@fidencio
Copy link
Contributor

fidencio commented Nov 7, 2017

Okay, let me know when you update the patches and I'll do a third round of review then.
@jhrozek, I'd suggest you to wait with the downstream tests till we have the ACK from Scott.

@sumit-bose
Copy link
Contributor Author

I just updates the patches to the latest version which got a positive feedback from Scott. The changes are mostly in the two new patches.

@fidencio
Copy link
Contributor

fidencio commented Nov 8, 2017

Okay. I'm firing our internal CI and I'll try to go through these patches before lunch.

@jhrozek
Copy link
Contributor

jhrozek commented Nov 8, 2017

Please let me know if I should re-run some of the downstream tests I ran the other day. Although you should be able to do that as well if you have the job IDs around -- just cloning those after you build the new test repo should do the trick.

@fidencio
Copy link
Contributor

fidencio commented Nov 8, 2017

@jhrozek, don't worry, I'll run the downstream tests based on the job IDs you pointed me to.

@fidencio
Copy link
Contributor

fidencio commented Nov 8, 2017

So, code-wise I have just a few nitpicks related to the new patches. Feel free to pick up the changes or ignore them if you don't think they're valid:

[ffidenci@pessoa sssd]$ cat 0001-fixup-pam-filter-certificates-in-the-responder-not-i.patch 
From b881194ae06e4e835f84d5bfbdf2924e5b2b7b15 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 8 Nov 2017 12:26:23 +0100
Subject: [PATCH] fixup! pam: filter certificates in the responder not in the
 child

---
 src/responder/pam/pamsrv_p11.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 6386954ae..c2eccb1f3 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -157,7 +157,7 @@ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
         /* Try to add default matching rule */
         ret = sss_certmap_add_rule(sss_certmap_ctx, SSS_CERTMAP_MIN_PRIO,
                                    CERT_AUTH_DEFAULT_MATCHING_RULE, NULL, NULL);
-        if (ret != 0) {
+        if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "Failed to add default matching rule.\n");
         }
@@ -176,7 +176,7 @@ errno_t p11_refresh_certmap_ctx(struct pam_ctx *pctx,
                                    certmap_list[c]->match_rule,
                                    certmap_list[c]->map_rule,
                                    certmap_list[c]->domains);
-        if (ret != 0) {
+        if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE,
                   "sss_certmap_add_rule failed for rule [%s] "
                   "with error [%d][%s], skipping. "
@@ -463,7 +463,7 @@ static errno_t parse_p11_child_response(TALLOC_CTX *mem_ctx, uint8_t *buf,
         }
 
         ret = sss_certmap_match_cert(sss_certmap_ctx, der, der_size);
-        if (ret == 0) {
+        if (ret == EOK) {
             DLIST_ADD(cert_list, cert_auth_info);
         } else {
             DEBUG(SSSDBG_TRACE_LIBS,
-- 
2.13.6

Covscan came up without any issues and I'm just waiting for the results of the downstream tests to ACK the patches.

@sumit-bose
Copy link
Contributor Author

@fidencio, thank you for the review. I used '0' instead of EOK here on purpose. Although in the SSSD source tree the sss_certmap_* calls are coming from an external library and the return code for success is '0' as documented in src/lib/certmap/sss_certmap.h.

EOK is used by SSSD's internal calls and is currently defined as '0'. But in theory it should be possible to set this to any int value and SSSD should keep working. I pretty sure this would currently break heavily but that why I would prefer to not use EOK for calls from external libraries.

@fidencio
Copy link
Contributor

fidencio commented Nov 8, 2017

@sumit-bose, makes sense :-) So, no more comments from me and I'm just waiting for the results of downstream tests.

@fidencio
Copy link
Contributor

fidencio commented Nov 8, 2017

@sumit-bose, last thing ... running our internal CI has been a little problematic due to the amount of dependencies pulled by gdm-devel.

I'd like to suggest to check for the OS version and add gdm-devel as a requirement only for el > 7.4 and fedora >= 27.

Does that make sense?

@fidencio
Copy link
Contributor

fidencio commented Nov 8, 2017

Hmm. It should be enough:

[ffidenci@pessoa x86_64]$ cat 0001-fixup-pam_sss-refactoring-use-struct-cert_auth_info.patch
From 18286c3ffd14053909eca1114419c3a63009e9dd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 8 Nov 2017 14:30:25 +0100
Subject: [PATCH] fixup! pam_sss: refactoring, use struct cert_auth_info

---
 contrib/sssd.spec.in | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in
index b3ebd32cd..4a2745baa 100644
--- a/contrib/sssd.spec.in
+++ b/contrib/sssd.spec.in
@@ -121,6 +121,12 @@
     %global with_kcm_option --without-kcm
 %endif
 
+%if (0%{?fedora} >= 27 || (0%{?rhel} >= 7 && 0%{?rhel7_minor} > 4))
+    %global depend_on_gdm_devel 1
+%else
+    %global depend_on_gdm_devel 0
+%endif
+
 Name: @PACKAGE_NAME@
 Version: @PACKAGE_VERSION@
 Release: 0@PRERELEASE_VERSION@%{?dist}
@@ -233,7 +239,9 @@ BuildRequires: libuuid-devel
 BuildRequires: jansson-devel
 BuildRequires: libcurl-devel
 %endif
+%if (0%{?depend_on_gdm_devel} == 1)
 BuildRequires: gdm-devel
+%endif
 
 %description
 Provides a set of daemons to manage access to remote directories and
-- 
2.13.6

Just fired an internal CI build in order to have it tested.

@fidencio
Copy link
Contributor

fidencio commented Nov 8, 2017

Okay, Debian testing has been failing on our CI and the following patch solves the issue:

[ffidenci@pessoa sssd]$ cat 0001-fixup-PAM-handled-multiple-certs-in-the-responder.patch 
From 0ce79d843545cb939b891cdf66b1dbc8133972a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Wed, 8 Nov 2017 15:47:01 +0100
Subject: [PATCH] fixup! PAM: handled multiple certs in the responder

---
 Makefile.am | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Makefile.am b/Makefile.am
index 5296c4ca0..57e03abfe 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1401,6 +1401,7 @@ sssd_pam_LDADD = \
     $(PAM_LIBS) \
     $(SYSTEMD_DAEMON_LIBS) \
     $(SSSD_INTERNAL_LTLIBS) \
+    libsss_certmap.la \
     $(NULL)
 
 if BUILD_SUDO
@@ -2423,6 +2424,7 @@ pam_srv_tests_LDADD = \
     $(SYSTEMD_DAEMON_LIBS) \
     libsss_test_common.la \
     libsss_idmap.la \
+    libsss_certmap.la \
     $(NULL)
 
 EXTRA_responder_get_domains_tests_DEPENDENCIES = \
-- 
2.13.6

You can find the two patches that I mentioned at: https://github.com/fidencio/sssd/tree/review/multiple_certs

@sumit-bose
Copy link
Contributor Author

The latest version include both fixes, I just renamed the spec file global.

@fidencio
Copy link
Contributor

fidencio commented Nov 9, 2017

Okay, all the downstream tests passed (one of the them only in the second run, though).

ACK!

@fidencio
Copy link
Contributor

fidencio commented Nov 9, 2017

Please, whoever pushes this patch set, add Scott Poore as reviewer as well.

@lslebodn
Copy link
Contributor

lslebodn commented Nov 9, 2017

Just a note. I would like to avoid following change:

diff --git a/src/util/authtok.c b/src/util/authtok.c
index c2f78be32..2c5a26ce3 100644
--- a/src/util/authtok.c
+++ b/src/util/authtok.c
@@ -27,6 +27,10 @@ struct sss_auth_token {

 enum sss_authtok_type sss_authtok_get_type(struct sss_auth_token *tok)
 {
+    if (tok == NULL) {
+        return SSS_AUTHTOK_TYPE_EMPTY;
+    }
+
     return tok->type;
 }

We need to properly initialise authtok in all cases. It must not be NULL
BTW following report from static analysers part is outdated:

    Error: FORWARD_NULL (CWE-476): [#def2]

sssd-1.16.1/src/responder/pam/pamsrv_p11.c:433: var_compare_op: Comparing "pd->authtok" to null implies that "pd->authtok" might be null.
sssd-1.16.1/src/responder/pam/pamsrv_p11.c:461: var_deref_model: Passing null pointer "pd->authtok" to "sss_authtok_get_type", which dereferences it.
sssd-1.16.1/src/util/authtok.c:30:5: deref_parm: Directly dereferencing parameter "tok".
#   28|   enum sss_authtok_type sss_authtok_get_type(struct sss_auth_token *tok)
#   29|   {
#   30|->     return tok->type;
#   31|   }
#   32|   

@lslebodn lslebodn removed the Accepted label Nov 9, 2017
@fidencio
Copy link
Contributor

So, I've just done some tests here and seems that we can just drop "authtok: check for NULL in sss_authtok_get_type()" patch, as long as we add the following fixup to the "p11_child: use options to select certificate for authentication" patch.

[ffidenci@pessoa x86_64]$ cat 0001-fixup-p11_child-use-options-to-select-certificate-fo.patch 
From 0d2ef9ac950d3539fa1ef0044790799d900f6a21 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Fabiano=20Fid=C3=AAncio?= <fidencio@redhat.com>
Date: Fri, 10 Nov 2017 14:04:15 +0100
Subject: [PATCH] fixup! p11_child: use options to select certificate for
 authentication

---
 src/responder/pam/pamsrv_p11.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 7f7b02b0d..27aeb9833 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -428,10 +428,9 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
         extra_args[arg_c++] = "--verify";
     }
 
-    if (pd->authtok != NULL
-            && (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
-                || sss_authtok_get_type(pd->authtok) ==
-                                                  SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
+    if (sss_authtok_get_type(pd->authtok) == SSS_AUTHTOK_TYPE_SC_PIN
+            || sss_authtok_get_type(pd->authtok) ==
+                                                  SSS_AUTHTOK_TYPE_SC_KEYPAD) {
         ret = sss_authtok_get_sc(pd->authtok, NULL, NULL, &token_name, NULL,
                                                           &module_name, NULL,
                                                           &key_id, NULL);
-- 
2.13.6

@fidencio
Copy link
Contributor

@lslebodn, @sumit-bose, both of you agree on that? (so we can have it merged sooner than later :-))

@lslebodn
Copy link
Contributor

@lslebodn, @sumit-bose, both of you agree on that? (so we can have it merged sooner than later :-))

Sure I suggested to drop the check.

This patch refactors the handling of certificates in p11_child. Not only
the first but all certificates suitable for authentication are returned.
The PAM responder component calling p11_child is refactored to handle
multiple certificate returned by p11_child but so far only returns the
first one to its callers.

Related to https://pagure.io/SSSD/sssd/issue/3560
@sumit-bose
Copy link
Contributor Author

@fidencio, @lslebodn, the new version has both, the if-check and the sss_authtok patch, remove.

I'll enhance the sss_authtok patch and send it with a different PR.

@fidencio
Copy link
Contributor

Covscan passed. ACK, and I'm adding the "Accepted" label.

@fidencio fidencio self-assigned this Nov 10, 2017
@lslebodn
Copy link
Contributor

whitespace unit test failed for me on my laptop

  • I would like to re-run downstream tests with current master which contains fixes for few regressions. (therefore temporary removing ACK label)

@lslebodn lslebodn removed the Accepted label Nov 10, 2017
@lslebodn
Copy link
Contributor

return ret;
}

request = calloc(1, GDM_PAM_EXTENSION_CHOICE_LIST_REQUEST_SIZE(cert_count));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should check return value here and fail in case of NULL.
Because it is checked inly in done section which is too late

return EINVAL;
}

DLIST_FOR_EACH(cai, pi->cert_list) cert_count++;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it happen that cert_count will be 0 ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is checked before if pi->cert_list is NULL, so cert_count is at least one.


login_token_name = getenv("PKCS11_LOGIN_TOKEN_NAME");
if (login_token_name == NULL) {
return PAM_SUCCESS;
}

while (pi->token_name == NULL
|| strcmp(login_token_name, pi->token_name) != 0) {
while (cai->token_name == NULL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clang says that cai can be NULL here

  • call pam_sss
  • line 2245 -> call get_pam_items
  • line 1253 -> "pi->cert_list = NULL"
  • return back from call get_pam_items
  • line 2263 -> use case SSS_APM_AUTHENTICAte in switch
  • line 2287 -> call check_login_token_name
  • line 2170 -> "struct cert_auth_info *cai = pi->cert_list;"
  • line 2177 -> note: Access to field 'token_name' results in a dereference of a null pointer (loaded from variable 'cai')

But I think it is false positive because we call send_and_receive-> eval_response -> parse_cert_info
What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a check because we cannot be sure that the PAM responder did add the needed data.

@lslebodn
Copy link
Contributor

BTW fix for whitespace on my laptop

echo "" >> src/tests/cmocka/p11_nssdb_2certs/key4.db
echo "" >> src/tests/cmocka/p11_nssdb_2certs/cert9.db

it was simpler than adding it to blacklist and unit test passed

This patch refactors the handling of the certificate and the attributes
to address the certificate on the Smartcard (module name, token name and
key id). Instead of using individual variables the values are put into a
new struct cert_auth_info. Since the new struct can be used as a list
the PAM responder can now handle multiple certificates on the Smartcard
and can send the needed data to pam_sss with multiple SSS_PAM_CERT_INFO
messages.

Unit tests are added to confirm the expected behavior.

Related to https://pagure.io/SSSD/sssd/issue/3560
Similar as in the PAM responder this patch replaces the individual
certificate authentication related attributes by a struct which can be
used as a list. With the pam_sss can handle multiple SSS_PAM_CERT_INFO
message and place the data in individual list items.

If multiple certificates are returned before prompting for the PIN a
dialog to select a certificate is shown to the users. If available a GDM
PAM extension is used to let the user choose from a list. All coded
needed at runtime to check if the extension is available and handle the
data is provided by GDM as macros. This means that there are no
additional run-time requirements.

Related to https://pagure.io/SSSD/sssd/issue/3560
New options are added to p11_child to select a specific certificate
during authentication.

The related unit tests are updated by adding the needed attributes to
the requests. The was not necessary before because although the
attribute were already send by pam_sss they were not used in the PAM
responder but only forwarded to the back where they were used by the
PKINIT code to select the expected certificate.

Related to https://pagure.io/SSSD/sssd/issue/3560
A new certificate attribute is added which contains a string which is
used in the certificate selection list displayed to the user. The
Subject-DN of the certificate is used here because it is present in all
certificate and in general differs for certificate with different usage.
libsss_certmap is used to extract the subject-DN from the certificate
and convert it into a string.

Related to https://pagure.io/SSSD/sssd/issue/3560
If only one certificate is available and the logon_name is the user is
not given the PAM responder already tried to find the name during the
pre-auth step. With multiple certificates this might cause useless extra
effort and the name should be determined after the certificate is
selected in the authentication step. This might currently only happen
with GDM because all other PAM clients will prompt for the user name
unconditionally.

New unit tests are added to cover this new case.

Related to https://pagure.io/SSSD/sssd/issue/3560
Additionally to the NSS erro code a text message describing the error is
added. This will help to see why p11_child ignores specific
certificates. For example it would be more obvious why the certificate
is not valid (expired, missing CA cert, failed OCSP etc).

Related to https://pagure.io/SSSD/sssd/issue/3560
With the new selection option and the handling of multiple certificates
in the PAM responder it is not needed anymore to filter the certificates
in p11_child but the matching rules can be applied by the PAM responder
directly.

Related to https://pagure.io/SSSD/sssd/issue/3560
Some types of Smartcards contain multiple certificate with the same
subject-DN for different usages. To make it easier to choose between
them in case the matching rules allow more than one of them for
authentication the label assigned to the certificate on the Smartcard is
shown in the selection prompt as well.

Related to https://pagure.io/SSSD/sssd/issue/3560
@sumit-bose
Copy link
Contributor Author

The latest version fixes @lslebodn's comment and modifies a filter in the whitespace test so that the added NSS database files are skipped as well.

@lslebodn
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants