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

PIV detection of AID using Discovery Object before doing select AID - Partial Fix #1243 #1256

Merged
merged 6 commits into from
Mar 16, 2018

Conversation

dengert
Copy link
Member

@dengert dengert commented Jan 29, 2018

Checklist
  • Documentation is added or updated
  • New files have a LGPL 2.1 license statement
  • Tested with the following cards: Yubikey 4 and PIV cards
    • tested PKCS#11
    • tested Windows Minidriver
    • tested macOS Tokend


/* Some objects will only be present if Histroy object says so */
for (i=0; i < PIV_OBJ_LAST_ENUM -1; i++)
if(piv_objects[i].flags & PIV_OBJECT_NOT_PRESENT)
priv->obj_cache[i].flags |= PIV_OBJ_CACHE_NOT_PRESENT;

sc_lock(card);
Copy link
Member

Choose a reason for hiding this comment

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

You should not assume that init() is always followed by match_card(), please remove this lock. In the worst case you need to re-do in init() what you've already sent in match_card().

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it is the other way around. A successful piv_match_card is always followed by piv_init
It also assumes card->dvr_data allocated in a successful piv_match_card is passed to piv_init

By setting card->dvr_data in piv_match allows any piv routines t have access to a valid ``card->dvr_data`

The lock in line 3048 in piv_init is unlocked in:

3204         sc_unlock(card) ; /* obtained in piv_match */

Copy link
Member

Choose a reason for hiding this comment

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

Yes, please don't do it. If you want a valid card->drv_data while executing piv_match_card, then sc_lock and sc_unlock in piv_match_card.

Copy link
Member Author

Choose a reason for hiding this comment

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

The point was to pass from match to init both the card->drv_data and the lock so any commands done during match don't have to be reissued again, and there can be no interference from other processes between match and init.

We really need to look at how match is done for applications. PIV and OpenPGP are really applications drivers and we need to have a way to support them better. See private note from Martin I forwarded to you about. https://github.com/makinako/OpenFIPS201

Copy link
Member

Choose a reason for hiding this comment

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

I know that this locking avoids issuing an additional SELECT. At some point I've done this, too. However, match_card was designed to not allocate or lock any internal resources. Other Applet-based card drivers have the same problem and they simply issue an additional SELECT. You can avoid the SELECT in match_card if you maintain a list of ATRs of cards that are known to have a PIV applet.

Also, locking is typically done in the PKCS#15 or the PKCS#11 layer, since you have more knowledge about the context that defines what is a transaction. In my opinion the locking should happen where unlocking is done, too. Otherwise we will loose track at some point and lock the token forever. So please do this only when you're forced to.

Choose a reason for hiding this comment

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

Comment on a few comments in this thread - It is Dangerous to base any PIV (Or other) "card application" identification on ATR alone (or at all), since most cards now support multiple application "Applets" and come with scores of applications in "ready to install" ROM. PIV requires only one (registered) RID/AID and supports a discovery object, other schemes support other registered/standardised methods. People need to use the method set out in the standards/doco rather than trying to second guess by the (albeit easier) ATR. In fact ATR should only be a last worst case option, and it only workled in the past because most cards were single application (which has now changed).

Copy link
Member

Choose a reason for hiding this comment

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

I can't find any statement that says you should use an ATR. But, as a matter of fact, some cards come with a specific ATR, so one may use it. As example look at SC-HSM's implementation:

static int sc_hsm_match_card(struct sc_card *card)
{
	sc_path_t path;
	int i, r, type = 0;

	i = _sc_match_atr(card, sc_hsm_atrs, &type);
	if (i >= 0 && type != SC_CARD_TYPE_SC_HSM_SOC) {
		card->type = type;
		return 1;
	}

	sc_path_set(&path, SC_PATH_TYPE_DF_NAME, sc_hsm_aid.value, sc_hsm_aid.len, 0, 0);
	r = sc_hsm_select_file(card, &path, NULL);
	LOG_TEST_RET(card->ctx, r, "Could not select SmartCard-HSM application");

	if (type == SC_CARD_TYPE_SC_HSM_SOC) {
		card->type = SC_CARD_TYPE_SC_HSM_SOC;
	} else {
		card->type = SC_CARD_TYPE_SC_HSM;
	}

	return 1;
}

It's fast in some cases (matching an ATR), and it's reliable in all of the cases (sometimes needing a SELECT).

* putting PIV driver first might help.
* TODO could be cached too
*/
i = piv_find_discovery(card);
Copy link
Member

Choose a reason for hiding this comment

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

Are you sure that this object is present on the Yubikey? NIST Special Publication 800-73-4 says it is optional an was not present in the initial release...

Copy link
Member Author

Choose a reason for hiding this comment

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

It is present on the 4 Yubico 4.2.6. Even if it is not, the the code goes ahead and tries to do a SELECT AID.

3063         if (i < 0) {
3064             /* Detect by selecting applet */
3065             i = piv_find_aid(card, &aidfile);
3066         }

By trying the piv_find_discovery first it avoids the Yubico issue of resetting the security state even
when selecting the same AID.

There are still problems in that other drivers try to select their AID before the PIV driver gets a chance to see if the PIV is the active application. So to get the full benefit of this
OPESC_DRIVER=PIV-II is needed, and a few extra fixed by Yubico.

Copy link
Member

Choose a reason for hiding this comment

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

OK, if it's present and does its job, fine.

Just wanted to remind you that the data from the Application Property template is still an option. Note that this is returned from SELECT and GET DATA, i.e. you don't need an extra command to retrieve the full AID on initialization, for example.

Copy link
Member Author

Choose a reason for hiding this comment

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

I got a new Yubikey version 4.3.7 yesterday, and it does have a Discovery object.

The point of using the discovery object first is it returns the active AID or fails without
changing the security status. Using a Select AID on Yubico and some other cards causes problems even when the active application is selected again. Yubico has this problem,also see
makinako/OpenFIPS201#1

I think we should be able to allow the user to "enter the PIN in once", to unlock a card, and use the card from other processes without having to reenter the PIN. Using disconnect=leave, and using the discovery object rather SELECT AID will allow this. This will work with single application cards.
But not with current multiple application cards that by policy (PPIV) say if a different application on the same card is selected, the security status is post. So some of the changes are trying to address both multiple and single application cards.

There are other places in OpenSC and code out of our control that interfere with "enter the PIN in once". This includes other OpenSC card drivers using Select AID to test if the card supports their application. A compliant PIV card does not have that problem as NIST 800-73-3 says it ignores SELECT AID for unknown AIDS. But the Yubico does not. Other cards may have the same issue. So OpenSC when testing for a card to not cause issues for other processes or other drivers.

The use of the Discovery object tries to do the above.

Copy link
Member

Choose a reason for hiding this comment

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

That's a misunderstanding. My suggestion was to

  1. use SELECT once in piv_init to select the applet and to get the full Application Property template.
  2. detect the PIV applet with GET DATA for the AID (4F, I believe) in every call of piv_card_reader_lock_obtained

Copy link
Member Author

Choose a reason for hiding this comment

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

Even doing a SELECT once can cause the card to lose the login state. The discovery object '7E'
has as the AID. Its enough to show the PIV application is the active application and it does not change the login state. If it fails, the PIV is not the active.

The PIV does not support the '4F' It is not required. I was trying that with the script I sent last week. and none of the cards I have support '4F'

That is why I use the '7E' discovery object that every PIV card I have has one.

I use the get data of the discovery object in every call of piv_card_reader_lock_obtained

Since the OpenPGP has the '4F` it can use that to test. Itr will fail if PIV is active.

Copy link
Member

Choose a reason for hiding this comment

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

OK, I see

piv_finish(card);
/* don't match. Does not have a PIV applet. */
sc_unlock(card);
return 0;
Copy link
Member

Choose a reason for hiding this comment

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

bad indenting

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, fixed and will be in next commit.
Note I did a commit earlier to handle the CKU_CONTEXT_SPECIFIC login.

Copy link
Member

Choose a reason for hiding this comment

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

Still I see there one space after the two tabs in the above three lines. The block above is indented with tab and 4 spaces.

data.pin_type = SC_AC_CHV;
data.pin_reference = priv->pin_preference;
/* will try our best to see if logged_in or not */
r = piv_pin_cmd(card, &data, NULL);
Copy link
Member

Choose a reason for hiding this comment

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

unused assignment.

Why do you need to check for the PIN at all? Maybe only in the case of a reset?

Copy link
Member Author

Choose a reason for hiding this comment

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

That is trying to keep the card->logged_in state correct. With the Yubico it is easy to lose the state.
For example: Assuming disconnect=leave
Current process is using PIV application and logged it.
Other process started and some driver sets the AID to something else, and then sets it to
PIV.
Our process uses discovery (7E) to see the PIV is active, but it does not know if the login state has changed unless it tests. The is the test.

Copy link
Member

Choose a reason for hiding this comment

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

I think this command is not needed. Simply set the PIV's internal tracking to SC_PIN_STATE_UNKNOWN if the applet has been deselected or reset. An application that wants to know the PIN status directly or indirectly calls sc_pin_cmd with SC_PIN_CMD_GET_INFO; this issues the VERIFY command if the card supports it.

Note that we should try to remove the driver specific tracking and put this into the PKCS#11 specific tracking. (i.e. we need to propagate a reset, for example). We should not do a double entry-bookeeping.

Copy link
Member Author

Choose a reason for hiding this comment

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

OK, will look at that closer.

Copy link
Member

Choose a reason for hiding this comment

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

Did you try piv_card_reader_lock_obtained without the PIN status query? I still think that's an superfluous APDU, but if you think that's necessary then please remove the unused assignment (which static code analysis will complain about).

@dengert
Copy link
Member Author

dengert commented Jan 30, 2018

@mouse07410
I add 1d05d89 for the context specific login.

@frankmorgner
can you look at this. I think there are other drivers that could use this too.

@mouse07410
Copy link
Contributor

@dengert is it rebased now against #1243? I.e., is it safe for me to apply both and see how the combination works?

@frankmorgner
Copy link
Member

I don't like 1d05d89, because it modifies all layers for an application specific problem (PKCS#11) that the driver should normally not be interested in.

If I had to implement this, I'd set an additional lock if the signature PIN shall be verified, because the driver knows that no other interfering commands must be sent. I think for the driver there is no need to know any detail of PKCS#11.

@dengert
Copy link
Member Author

dengert commented Jan 31, 2018

You said: " if the signature PIN shall be verified". In the PIV case there is only one PIN. So how can the card driver know it's for a signature?

As I said, in the commit: "A card driver pin_cmd code can ignore this flag or can take some action."

I could look at the pkcs15 layer with user consent. But that is on the key. Its only when PKCS11 realizes the CKA_ALWAYS_AUTHENTICATE requires a C_Login(CKU_CONTEXT_SPECIFIC) Its up to the PKCS11 calling application to to the C_Login(CKU_CONTEXT_SPECIFIC) followed by the C_Sign or other operation.

1d05d89 tries to do the above and for the PIV card, expects the C_Login(CKU_CONTEXT_SPECIFIC) followed by the C_Sign but if it does not, it will release the lock when it is it sees some other APDU complete.
Other drivers may want to do something different.

@frankmorgner
Copy link
Member

In the PIV case there is only one PIN. So how can the card driver know it's for a signature?

Ah, I understand. But how can you tell from CKU_CONTEXT_SPECIFIC that the PIN will be used for a signature? Couldn't C_Login be used without that flag and still call C_Sign later?

@dengert
Copy link
Member Author

dengert commented Jan 31, 2018

PKCS#11 says if the the key has CKA_ALWAYS_AUTHENTICATE then C_Login(CKU_CONTEXT_SPECIFIC) must by used before use of the key with any of the crypto operation.

The mod has some code in pkcs15-sec.c use_key. If it sees a card driver has done the sc_lock before the verify, that it does not need to do a lock. I believe pkcs15-sec.c use_key is called by all the crypto routines.

More later....

@frankmorgner
Copy link
Member

Thanks, for the pointer. The PKCS#11 description, indeed, looks good:

The CKA_ALWAYS_AUTHENTICATE attribute can be used to force re-authentication (i.e. force the user to provide a PIN) for each use of a private key. "Use" in this case means a cryptographic operation such as sign or decrypt. This attribute may only be set to CK_TRUE when CKA_PRIVATE is also CK_TRUE.

Re-authentication occurs by calling C_Login with userType set to CKU_CONTEXT_SPECIFIC immediately after a cryptographic operation using the key has been initiated (e.g. after C_SignInit). In this call, the actual user type is implicitly given by the usage requirements of the active key. If C_Login returns CKR_OK the user was successfully authenticated and this sets the active key in an authenticated state that lasts until the cryptographic operation has successfully or unsuccessfully been completed (e.g. by C_Sign, C_SignFinal,..). A return value CKR_PIN_INCORRECT from C_Login means that the user was denied permission to use the key and continuing the cryptographic operation will result in a behavior as if C_Login had not been called. In both of these cases the session state will remain the same, however repeated failed re-authentication attempts may cause the PIN to be locked. C_Login returns in this case CKR_PIN_LOCKED and this also logs the user out from the token. Failing or omitting to re-authenticate when CKA_ALWAYS_AUTHENTICATE is set to CK_TRUE will result in CKR_USER_NOT_LOGGED_IN to be returned from calls using the key. C_Login will return CKR_OPERATION_NOT_INITIALIZED, but the active cryptographic operation will not be affected, if an attempt is made to re-authenticate when CKA_ALWAYS_AUTHENTICATE is set to CK_FALSE.

Regarding the implementation, I think it would be better to add a new Access Control flag for a context specific verification in types.h. Check out how I've integrated the Minidriver's session PIN with SC_AC_SESSION in 74ec7b0. Essentially you add a new PIN type (SC_AC_CONTEXT) that you can check in piv_pin_cmd.

@dengert
Copy link
Member Author

dengert commented Jan 31, 2018

That could work.
framework-pkcs11.c would call sc_pkcs15_verify_pin with some extra flag?
or would you like a sc_pkcs15_verify_pin_context_specific
that would set data->cmd = SC_PIN_CMD_VERIFY_CONTEXT_SPECIFIC;
then call sc_pin_cmd?

@frankmorgner
Copy link
Member

No, you would use sc_pkcs15_verify_pin as is, but you need to change pin_obj's auth_info to auth_info->auth_method = SC_AC_CONTEXT before. This should then be passed to piv_pin_cmd as data->pin_type. Just check out how the Minidriver is initializing usage of a session PIN.

@dengert
Copy link
Member Author

dengert commented Jan 31, 2018 via email

@frankmorgner
Copy link
Member

Ok will have a look. Will other drivers need to onderstand the SC_AC_CONTEXT?

Yes, but in case of the session PIN it was enough to modify iso7816.c, where SC_AC_SESSION was mapped to SC_AC_CHV

greping through PKCS#11 2.30, I've noticed that no other mention of uses for a context specific login is mentioned than for a PIN that always needs to be authenticated. In framework-pkcs15.c it is mentioned that it was also used in combination with unblocking a PIN. Reading further, I've noticed this comment:

	/* By default, we make the reader resource manager keep other
	 * processes from accessing the card while we're logged in.
	 * Otherwise an attacker could perform some crypto operation
	 * after we've authenticated with the card */

	/* Context specific login is not real login but only a
	 * reassertion of the PIN to the card.
	 * And we don't want to do any extra operations to the card
	 * that could invalidate the assertion of the pin
	 * before the crypto operation that requires the assertion
	 */

Maybe we should actually do the opposite, i.e. create a context specific transaction by locking the card on the PKCS#11 level and don't unlock it until the next (cryptographic) operation is completed. I think this should work with the current uses (without locking the token too long and with fixing the PIV's PIN issue) and doesn't require any other change than in framework-pkcs15.c.

@Jakuje, @dengert do you know whether CKU_CONTEXT_SPECIFIC is used in some other context, where locking is undesired?

@Jakuje
Copy link
Member

Jakuje commented Feb 1, 2018

The CKU_CONTEXT_SPECIFIC is used only in this context throughout the PKCS#11 specification. This solution seems reasonable for me, but it would need to catch up all the cases when the application would call the C_Login(CKU_CONTEXT_SPECIFIC) and then for some reason would not continue with the expected functions.

@dengert
Copy link
Member Author

dengert commented Feb 1, 2018

I have reverted the previous 1d05d89 and using the suggestions from @frankmorgner written 906df5f

I believe this also addresses @Jakuje comments, as the extra lock added before the verify is released after the verify fails or the next command sent to the card completes.

@dengert
Copy link
Member Author

dengert commented Feb 1, 2018

@mouse07410 Tis has not been rebased yet. I am waiting on comments from @frankmorgner and @Jakuje

@@ -2992,19 +3036,47 @@ static int piv_match_card(sc_card_t *card)
if (card->type == -1)
card->type = type;

card->drv_data = priv;
card->drv_data = priv; /* will frre if no match, or pass on to piv_init */
Copy link
Member

Choose a reason for hiding this comment

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

typo frre -> free

Copy link
Member Author

Choose a reason for hiding this comment

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

OK. will fix

@frankmorgner
Copy link
Member

The CKU_CONTEXT_SPECIFIC is used only in this context throughout the PKCS#11 specification. This solution seems reasonable for me, but it would need to catch up all the cases when the application would call the C_Login(CKU_CONTEXT_SPECIFIC) and then for some reason would not continue with the expected functions.

Actually, in framework-pkcs15.c we already have a single entry point for locking, (lock_card()), that's called if the PKCS#11 option lock_login is enabled. So I'm pretty confident that by adding special handling of CKU_CONTEXT_SPECIFIC here (or in the callee), we can be sure that the token will also be unlocked.

Anyway, I think that doing the locking in the card driver would be the better option, since this seems quite specific to the PIV card.


/* If not valid, read, cache and test */
if (!(priv->obj_cache[PIV_OBJ_DISCOVERY].flags & PIV_OBJ_CACHE_VALID)) {
r = piv_process_discovery(card);
Copy link
Member

Choose a reason for hiding this comment

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

The comment above states

/* Do not use the cache value but read every time */

But here you're reading the cached value, right?

Copy link
Member Author

Choose a reason for hiding this comment

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

No there is a "!" to say if the cache in not valid, call the piv_process_discovery. This routine will test if the cache is valid or not (in this case it will not be valid) then read from the card add it to the cache and parse it find the AID. So the first time the discovery object is needed it will also get cached.

If the cache was valid, then the "else" in line 2685 will force the discovery object to be read from the card. piv_parse_discovery will then make sure the AID in the data read is the PIV AID, thus proving that the PIV application is active.

Copy link
Member

Choose a reason for hiding this comment

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

The comment above the function let me believe that the function should "not use the cache value", but in your reply (and in your code) you're accessing the cache. I'm just saying that there's an inconsistency. Maybe you just need to change the comment in the code.

Copy link
Member Author

Choose a reason for hiding this comment

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

The piv_process_discovery(card); when called will return a cached value if it is cached. If not it will read the discovery object from the card and also addit it to the cache.

So the line if (!(priv->obj_cache[PIV_OBJ_DISCOVERY].flags & PIV_OBJ_CACHE_VALID))
firsts test if it is NOT cached, then calls piv_process_discovery(card);
So in effect the discovery object will be read and also added to the cache.

The code is working as intended.

Copy link
Member

Choose a reason for hiding this comment

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

Could you then please remove or change the misleading comment /* Do not use the cache value but read every time */?

Copy link
Member

Choose a reason for hiding this comment

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

Well ... the code and comment makes sense to me. The note, that we are reading the value always is already in the previous comment ([...] So we can not use the cache). This comment is supposed to explain just the two following lines, which it does.

The thing is that if the cache is invalid, we call piv_process_discovery() and it calls piv_get_cached_data(), which in the result reads the data from card again regardless the name of the function (we have invalid cache data or the object was not read yet).

I agree that the comment or the code can be little bit more clear.

But can not we explicitly invalidate the cache and then reduce this block to the call to piv_process_discovery() anyway (that is what we do in the else branch also, isn't it?). Would not it be more readable?

	/* invalidate the cache and read the object from card */
	priv->obj_cache[PIV_OBJ_DISCOVERY].flags &= ~PIV_OBJ_CACHE_VALID;
	r = piv_process_discovery(card); 

(I did not test this code, it might have some side effects or issues, but looks much more understandable and we even get some more debug information)

@dengert
Copy link
Member Author

dengert commented Feb 2, 2018

The lock_card may or may not be turned. The new code in piv_pin_cmd will make sure that the lock is on before the verify is sent and turned off if the verify fails or after the next command after a successful verify.

@dengert dengert changed the title PIV detection of AID using Discovery Object before doing select AID - Partial Fix #1234 PIV detection of AID using Discovery Object before doing select AID - Partial Fix #1243 Feb 5, 2018
@dengert dengert added this to In progress in Release 0.18.0 Feb 5, 2018
@dengert
Copy link
Member Author

dengert commented Feb 7, 2018

This PR has been rebased on master. Master now includes #1243
I believe I have made most of the changes as requested.

Except for piv_match_card passing the card->drv_data and a lock to piv_init I see no harm in doing this, as a successful match is always followed by an init. Before piv_match_card returns failure, it cleans up by calling piv_finish and calls sc_unlock

3070         if (i < 0) {
3071                 piv_finish(card);
3072                  /* don't match. Does not have a PIV applet. */
3073                  sc_unlock(card);
3074                  return 0;
3075         }

}

/* make sure our application is active */

/* first see if AID is active AID be reading discovery obkect '7E' */
Copy link
Member

Choose a reason for hiding this comment

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

typo: obkect -> object

@dengert
Copy link
Member Author

dengert commented Feb 7, 2018

@Jakuje FIxed typoes, spaces and tabs

@dengert
Copy link
Member Author

dengert commented Feb 22, 2018

I have rebased and squashed the 13 commits down to 5 without reordering. Thus no code was changed, just rebased.

@frankmorgner I believe I have addressed all of your issues. So again I would like to see this PR committed. @mouse07410 and @Jakuje have also requested that this PR be committed.

It need to be in 0.18.0.

@Jakuje
Copy link
Member

Jakuje commented Feb 22, 2018

I agree. I was just referring to this PR from OpenSSH bug [1] which seems to get resolved by this PR (in addition to disconnect_action=leave) too so everything will not be for worse in OpenSSH with a new version (though I will have to update my authorized_keys).

[1] https://bugzilla.mindrot.org/show_bug.cgi?id=2635

@frankmorgner
Copy link
Member

@dengert you missed the comment about match_card. Complying with the requested change would equally solve the problem in OpenSSH.

We can discuss on removing match_card entirely, but that would be something for a different pull request.

@mouse07410
Copy link
Contributor

mouse07410 commented Feb 23, 2018

you missed the comment about match_card. Complying with the requested change would equally solve the problem in OpenSSH.

I don't know, and would rather not mess with the code that's working. One introduced bug is enough.

I also want to add that this PR (the way it is now) addressed one problem I had with SSH. git push would fail on the first attempt, unless I do something like yubico-piv-tool -a status first. Now I don't need that workaround - SSH access to GitHub works on the first try.

So I request that any more changes are done via different PRs.

@dengert
Copy link
Member Author

dengert commented Feb 23, 2018

https://github.com/frankmorgner

I DID FIX THIS. 10 days ago 990f067 and it is now rebased as aee62c7 with a better commit message. piv_match_card will take a quick look at what card->type a user may have tried to force. So piv_match_card will always return without expecting piv_init to be called. i.e. does not hold a lock or use card->drv_data.

piv_match_card_continued should really be called piv_do_what_we_used_to_do_in_piv_match_card
It is the old piv_match minus the check on a forced card->type.

AND

piv_init can now be called without piv_match_card having been called.

I was not arguing that *_match_card was useful or not. I was arguing that if you are going to call both match_card and init, I did not want to duplicate card commands in both. Some cards need to do a lot of setup before doing any card commands and the only way to pass information from *_match_card to *_init was via card->drv_data. So why not take advantage of it. AND *_init was always preceded by a successful *_match_card.

I addressed your issue of the sc_lock and card-> drv_data being passed between piv_match_card and piv_init.`

@frankmorgner
Copy link
Member

@dengert you can find the comment above marked with a red sign as requested changes. Basically, you should not match always, instead use proper detection (SELECT or ATR).

@graeme-freedman
Copy link

graeme-freedman commented Feb 26, 2018 via email

@dengert
Copy link
Member Author

dengert commented Feb 26, 2018

https://github.com/frankmorgner again you are missing the point. You keep trying to tell me to I can not have a match_card that returns 1, because the init might not be called.

But then what is the point of calling match_card without calling _init?

I am not matching ATRs for all the reasons others are point out to you, PIV is an applet that can be on many cards, and many in the future. We should be flexible enough to deal with future cards without having to code ATRs. The same goes for OpenPGP and all the others @graeme-freedman has also pointed out.

Even Microsoft has always treated the PIV like an applet, and selects the applet. They do not have a list of ATRs of cards that might be PIV cards.

I have changes #1256 around at least 3 times to try and satisfy your comments over the match_card and init. I am not changing it again.

3 of us have been insting for weeks now to commit #1256. All of us have a special interest in PIV, which you do not. So please commit #1256.

In order to satisfy some concerns over the use of <card>_match_card
and <card>_init, this modification will do that at the cost of additional
overhead of repeating some card commands.

Hopefully this commit will not be needed.

 On branch piv-aid-discovery
 Changes to be committed:
	modified:   card-piv.c
@dengert
Copy link
Member Author

dengert commented Feb 28, 2018

In order to get this PR into 0.18.0 I have added one last commit to this PR which I fell in not needed.

I would like to here from @viktorTarasov and @martinpaljak and @frankmorgner on Frank's issue with passing information from a card's match_card function to the card's init function.

Frank has said don't do it. It was not designed to do it.

My stance on this is it is OK to do it. Everywhere in the code the card's match_card function is called and returns 1, the card's init function is called. It has been this way for years.

Earlier versions of this PR took advantage of that and allocated card->dvr_data and called sc_lock to avoid having to redo a lot of code in piv_init. The functionality this PR adds is to determine if the card has the PIV AID selected, and/or if the PIV AID can be selected. This requires much more of the PIV internal s be setup and used in piv_match_card and then passed on to piv_init.

In order to not pass any information between the two routines, (because Frank was against it), I rewrote the piv_match_card to do very little checking and return 1. All the other processing done in the previous piv_match_card was moved to a new function called piv_match_card_continued and this was called from piv_init. Thus no card->dvr_data was allocated and sc_lock was not called and passed.

Frank still did not like this.

I said I would not fix this, but have submitted the latest commit 4222036 Piv_match_card_continued is now called twice, once from pic_match_card and then again from piv_init.
Thus the piv_match_card will only return 1 after checking if the active AID is the PIV AID or the the PIV AID can be selected. piv_init wil cal it again because of Frank's concerns.

I would also like to here from @mouse07410 @Jakuje and anyone else who have been testing this PR all along and asking for it to be committed.

There is not much else that I can do to addresses Frank's concerns. And this PR with or without 4222036 should be in 0.18.0

@mouse07410
Copy link
Contributor

mouse07410 commented Feb 28, 2018

I would also like to hear from @mouse07410...

What can I say? My quick preliminary tests show that this latest commit did not break PIV, and all my cards appear to work... I don't think this is necessary, but if that's what it takes to get this PR merged, so be it.

I really do wish we stopped messing with this PR and committed it.

Regardless, this PR has a prominent place in my fork, where it's been merged for quite some time. Perhaps that's where we should shift our efforts? I'm tired of begging to get things merged.

@Jakuje
Copy link
Member

Jakuje commented Mar 2, 2018

Adding another ack, that the current version works as expected and I did not encounter any concurrency issues while trying to sing with PIV applet and interfering with openpgp driver from different process.

int pin_cmd_verify;
int context_specific;
Copy link
Member

Choose a reason for hiding this comment

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

Maybe add a comment about what this field does and why. Same for other maybe not-so-obvious fields?

@dengert
Copy link
Member Author

dengert commented Mar 2, 2018

int context_specific; is introduced in 27add2e to help the card driver support the PIV "PIN Always" of the card. This NIST requirement says a crypto operation using the PIV Signing key must be preceded by a verify command with no other card commands between them. It is enforced by the card.

It is set in line 3453 and reset in line 3299.

3445         /*
3446          * If this was for a CKU_CONTEXT_SPECFIC login, lock the card one more time.
3447          * to avoid any interference from other applications.  
3448          * Sc_unlock will be called at a later time after the next card command 
3449          * that should be a crypto operation. If its not then it is a error by the 
3450          * calling appication. 
3451          */
3452         if (data->cmd == SC_PIN_CMD_VERIFY && data->pin_type == SC_AC_CONTEXT_SPECIFIC) {
3453                 priv->context_specific = 1;
3454                 sc_log(card->ctx,"Starting CONTEXT_SPECIFIC verify");
3455                 sc_lock(card);
3456         }

It is reset after the next card command which should be the crypto operation. If not then the calling application has changed it mind or is not following the PKCS#11 guidelines for using CKU_CONTEXT_SPECFIC. In either case, the extra lock is released:

3294                 } else {
3295                         /* a command has completed and it is not verify */
3296                         /* If we are in a context_specific sequence, unlock */
3297                         if (priv->context_specific) {
3298                                 sc_log(card->ctx,"Clearing CONTEXT_SPECIFIC lock");
3299                                 priv->context_specific = 0;
3300                                 sc_unlock(card);
3301                         }

When PKCS#11 determines a key has the CKA_ALWAYS_AUTHENTICATE attribute it will do a C_Login(... CKU_CONTEXT_SPECIFIC ...) as required. This ends up as a PIN verify to the card driver. The driver can use this information as needed. In the PIV case, this will cause the extra lock to be done to make sure there is no interference from other processes. The lock is released when the next command is done.

The extra sc_lock and sc_unlock cause the verify command and the next card command to be in the same SCard Transaction. In normal cases the verify is in a transaction by itself and the next command may come at any time. In the CKU_CONTEXT_SPECIFIC case PKCS#11 is saying it will be doing the crypto operation next immediately after the verify.

I could add additional comments if the ones at line 3445 are not clear.

@frankmorgner
Copy link
Member

Looks OK now. There's still a minor issue here, where a superfluous assignment should be better replaced with a comment.

@dengert
Copy link
Member Author

dengert commented Mar 9, 2018 via email

@dengert
Copy link
Member Author

dengert commented Mar 9, 2018

The "superfluous assignment" is not superfluous because in piv_match_card_continued
if the discovery object can not be read this is set:
priv->card_issues |= CI_DISCOVERY_USELESS;
line 3226 and all the other priv->card_issues |= statements just above are executed after piv_match_card_continued thus priv->card_issues may not be zero.

@dengert
Copy link
Member Author

dengert commented Mar 9, 2018

Can we get this PR committed?

@mouse07410
Copy link
Contributor

mouse07410 commented Mar 9, 2018

I'd like to see this PR merged. The code works, and is clear enough. We already had one bug introduced in an attempt to perfect what was good enough already. Enough is enough.

@Jakuje
Copy link
Member

Jakuje commented Mar 13, 2018

Yes, lets merge this. If there will show up some more problems/comments, lets handle them in separate PR/Issues.

@frankmorgner frankmorgner merged commit 7ca16a7 into OpenSC:master Mar 16, 2018
Release 0.18.0 automation moved this from In progress to Done Mar 16, 2018
@dengert
Copy link
Member Author

dengert commented Mar 18, 2018

Thanks.

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

Successfully merging this pull request may close these issues.

None yet

6 participants