OpenSC / libp11

PKCS#11 wrapper library
GNU Lesser General Public License v2.1
298 stars 183 forks source link

User not logged in after logging in #521

Open mihaicristiantanase opened 7 months ago

mihaicristiantanase commented 7 months ago

I have an issue with key pair generation. This simplified code:

void runSimplifiedTest(char* lib, char* passwd)
{
    PKCS11_CTX* ctx = PKCS11_CTX_new();
    PKCS11_CTX_load(ctx, lib);

    PKCS11_SLOT* slots = NULL;
    unsigned int nslots;
    PKCS11_enumerate_slots(ctx, &slots, &nslots);
    PKCS11_SLOT* slot = PKCS11_find_token(ctx, slots, nslots);

    PKCS11_login(slot, 0, passwd);

    PKCS11_open_session(slot, 1);
    PKCS11_login(slot, 0, passwd);
    PKCS11_generate_key(slot->token, 0, 2048, "test_label", (unsigned char*)"testid", 6);
    if (ERR_peek_last_error()) {
        fprintf(stderr, "Generated errors:\n");
        ERR_print_errors_fp(stderr);
    }
}

shows the following error: 4407954944:error:81082101:PKCS#11 module:func(130):User not logged in:p11_key.c:322:

mihaicristiantanase commented 7 months ago

If I remove the first PKCS11_login call , I get a successful key pair generation.

mihaicristiantanase commented 7 months ago

From what I found, the second call to PKCS11_login finds the user already logged in, even though a new session was created for read-write operations (PKCS11_open_session) in the mean time.

mihaicristiantanase commented 7 months ago

If I remove the first PKCS11_login call , I get a successful key pair generation.

And if I were to insert a PKCS11_logout just before the second PKCS11_login, the operation is also successful.

dengert commented 7 months ago

With pure PKCS11 an application can have multiple session to the same slot. C_login takes a session parameter, and changes the session state CK_STATE from CK_RO_xxx to CK_R_xxx. There are 5 possible session states. When used with a smartcard which in , C_Login is also used to change the state on the smart card and which may have a user pin and an SO pin.

(If I am reading the code correctly) https://github.com/OpenSC/libp11/blob/master/src/p11_slot.c#L220 libp11_login will create a session if one is not found for CKU_USER or CKU_SO. In your case PKCS11_login(slot, 0, passwd); will create a CK_RO_USER at line 320 and the C_Login at line 324 will convert it to a RW session.

So somehow it is getting confused because you first ask it to login and create a session, then create a new session and then do the login.

If you really want to see what PKCS11 calls are being made see OpenSC SPY: https://github.com/OpenSC/OpenSC/wiki/Using-OpenSC

mihaicristiantanase commented 7 months ago

I've just finished a step by step debugging with LLDB and it shows that the problem is in the second call to PKCS11_login which finds the client already logged in and it skips everything else: https://github.com/OpenSC/libp11/blob/dd0a8c63c0311674cabfd781753e8374ff531409/src/p11_slot.c#L227

To me, this looks to be like a bug in PKCS11_open_session which invokes https://github.com/OpenSC/libp11/blob/dd0a8c63c0311674cabfd781753e8374ff531409/src/p11_slot.c#L110 Shouldn't this function reset the login state? Or maybe there should be a more complex mechanism that handles login state per session (not a session independent flag like it is at the moment: slot->logged_in). This might be the cause of confusion that you mentioned in the previous comment...

(lldb) r
Process 8269 launched: 'open-session-and-login' (x86_64)
Process 8269 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003bfb open-session-and-login`runSimplifiedTest(lib="/usr/local/lib/libeToken.dylib", passwd="00000000") at open-session-and-login.c:103:15
   100          PKCS11_login(slot, 0, passwd);
   101 
   102          PKCS11_open_session(slot, 1);
-> 103          PKCS11_login(slot, 0, passwd);
   104          PKCS11_generate_key(slot->token, 0, 2048, "test_label", (unsigned char*)"testid", 6);
   105          if (ERR_peek_last_error()) {
   106                  fprintf(stderr, "Generated errors:\n");
Target 0: (open-session-and-login) stopped.

(lldb) s
libp11.3.dylib was compiled with optimization - stepping may behave oddly; variables may not be available.
Process 8269 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100131cf0 libp11.3.dylib`PKCS11_login(pslot=0x000000010060a8d0, so=0, pin="00000000") at p11_front.c:171:30 [opt]
   168 
   169  int PKCS11_login(PKCS11_SLOT *pslot, int so, const char *pin)
   170  {
-> 171          PKCS11_SLOT_private *slot = PRIVSLOT(pslot);
   172          if (check_slot_fork(slot) < 0)
   173                  return -1;
   174          return pkcs11_login(slot, so, pin);
Target 0: (open-session-and-login) stopped.

(lldb) n
Process 8269 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100131cf4 libp11.3.dylib`PKCS11_login(pslot=0x000000010060a8d0, so=0, pin="00000000") at p11_front.c:172:6 [opt]
   169  int PKCS11_login(PKCS11_SLOT *pslot, int so, const char *pin)
   170  {
   171          PKCS11_SLOT_private *slot = PRIVSLOT(pslot);
-> 172          if (check_slot_fork(slot) < 0)
   173                  return -1;
   174          return pkcs11_login(slot, so, pin);
   175  }
Target 0: (open-session-and-login) stopped.

(lldb) 
Process 8269 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100131d00 libp11.3.dylib`PKCS11_login(pslot=<unavailable>, so=0, pin="00000000") at p11_front.c:174:9 [opt]
   171          PKCS11_SLOT_private *slot = PRIVSLOT(pslot);
   172          if (check_slot_fork(slot) < 0)
   173                  return -1;
-> 174          return pkcs11_login(slot, so, pin);
   175  }
   176 
   177  int PKCS11_logout(PKCS11_SLOT *pslot)
Target 0: (open-session-and-login) stopped.

(lldb) s
Process 8269 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step in
    frame #0: 0x0000000100130e50 libp11.3.dylib`pkcs11_login(slot=0x000000010060a550, so=0, pin="00000000") at p11_slot.c:221 [opt]
   218   * Authenticate with the card.
   219   */
   220  int pkcs11_login(PKCS11_SLOT_private *slot, int so, const char *pin)
-> 221  {
   222          PKCS11_CTX_private *ctx = slot->ctx;
   223          CK_SESSION_HANDLE session;
   224          int rv;
Target 0: (open-session-and-login) stopped.

(lldb) n
Process 8269 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = step over
    frame #0: 0x0000000100130e64 libp11.3.dylib`pkcs11_login(slot=0x000000010060a550, so=0, pin="00000000") at p11_slot.c:226:22 [opt]
   223          CK_SESSION_HANDLE session;
   224          int rv;
   225 
-> 226          if (slot->logged_in >= 0)
   227                  return 0; /* Nothing to do */
   228 
   229          /* SO needs a r/w session, user can be checked with a r/o session. */
Target 0: (open-session-and-login) stopped.

(lldb) p slot->logged_in 
(int8_t) $0 = '\0'

(lldb) c
Process 8269 resuming
Generated errors:
4295917056:error:81082101:PKCS#11 module:func(130):User not logged in:p11_key.c:322:
Process 8269 exited with status = 0 (0x00000000) 
dengert commented 7 months ago

You can submit a PR if you think this is important.

mihaicristiantanase commented 7 months ago

Sure, I can do that. A workaround (without fully understanding the interaction between a session and the login status) I was thinking about is to simply reset the login state whenever a new session is opened. But, before doing any changes, can you clarify the following?

  1. are multiple login statuses (one per each session) something to care about? As I mentioned previously, the current implementation associates a login status to a slot and, thus is session independent.
  2. How many sessions will be available at the end of this scenario?
    • open a RO session with no login
    • open a RW session with no login
    • open a RW session with USER login
    • open a RO session with SO login
dengert commented 7 months ago

@mtrojnar any comments on this?