From 86e36eb36c29bb9f3f88771d9cb8ca60695c313a Mon Sep 17 00:00:00 2001
From: Simo Sorce <simo@redhat.com>
Date: Mon, 23 Oct 2023 16:32:17 -0400
Subject: [PATCH] Implement support for CKA_ALWAYS_AUTHENTICATE

Fixes #42

Signed-off-by: Simo Sorce <simo@redhat.com>
---
 src/asymmetric_cipher.c | 10 ++++++++++
 src/objects.c           | 34 +++++++++++++++++++++++++++++++++-
 src/objects.h           |  1 +
 src/session.c           | 31 ++++++++++++++++++++++++++++---
 src/session.h           |  4 ++++
 src/signature.c         | 10 ++++++++++
 6 files changed, 86 insertions(+), 4 deletions(-)

diff --git a/src/asymmetric_cipher.c b/src/asymmetric_cipher.c
index a4f33083..bd8e3bc8 100644
--- a/src/asymmetric_cipher.c
+++ b/src/asymmetric_cipher.c
@@ -245,6 +245,7 @@ static int p11prov_rsaenc_decrypt(void *ctx, unsigned char *out, size_t *outlen,
     CK_OBJECT_HANDLE handle;
     CK_ULONG out_size = *outlen;
     int result = RET_OSSL_ERR;
+    bool always_auth = false;
     CK_RV ret;
 
     P11PROV_debug("decrypt (ctx=%p)", ctx);
@@ -296,6 +297,15 @@ static int p11prov_rsaenc_decrypt(void *ctx, unsigned char *out, size_t *outlen,
         goto endsess;
     }
 
+    always_auth =
+        p11prov_obj_get_bool(encctx->key, CKA_ALWAYS_AUTHENTICATE, false);
+    if (always_auth) {
+        ret = p11prov_context_specific_login(session, NULL, NULL, NULL);
+        if (ret != CKR_OK) {
+            goto endsess;
+        }
+    }
+
     ret = p11prov_Decrypt(encctx->provctx, sess, (void *)in, inlen, out,
                           &out_size);
     if (ret != CKR_OK) {
diff --git a/src/objects.c b/src/objects.c
index cb92843d..11318ccd 100644
--- a/src/objects.c
+++ b/src/objects.c
@@ -498,6 +498,35 @@ CK_ATTRIBUTE *p11prov_obj_get_attr(P11PROV_OBJ *obj, CK_ATTRIBUTE_TYPE type)
     return NULL;
 }
 
+bool p11prov_obj_get_bool(P11PROV_OBJ *obj, CK_ATTRIBUTE_TYPE type, bool def)
+{
+    CK_ATTRIBUTE *attr = NULL;
+
+    if (!obj) {
+        return def;
+    }
+
+    for (int i = 0; i < obj->numattrs; i++) {
+        if (obj->attrs[i].type == type) {
+            attr = &obj->attrs[i];
+        }
+    }
+
+    if (!attr || !attr->pValue) {
+        return def;
+    }
+
+    if (attr->ulValueLen == sizeof(CK_BBOOL)) {
+        if (*((CK_BBOOL *)attr->pValue) == CK_FALSE) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    return def;
+}
+
 CK_KEY_TYPE p11prov_obj_get_key_type(P11PROV_OBJ *obj)
 {
     if (obj) {
@@ -566,8 +595,9 @@ P11PROV_CTX *p11prov_obj_get_prov_ctx(P11PROV_OBJ *obj)
 
 /* CKA_ID
  * CKA_LABEL
+ * CKA_ALWAYS_AUTHENTICATE
  * CKA_ALLOWED_MECHANISMS see p11prov_obj_from_handle() */
-#define BASE_KEY_ATTRS_NUM 3
+#define BASE_KEY_ATTRS_NUM 4
 
 #define RSA_ATTRS_NUM (BASE_KEY_ATTRS_NUM + 2)
 static int fetch_rsa_key(P11PROV_CTX *ctx, P11PROV_SESSION *session,
@@ -588,6 +618,7 @@ static int fetch_rsa_key(P11PROV_CTX *ctx, P11PROV_SESSION *session,
     FA_SET_BUF_ALLOC(attrs, num, CKA_PUBLIC_EXPONENT, true);
     FA_SET_BUF_ALLOC(attrs, num, CKA_ID, false);
     FA_SET_BUF_ALLOC(attrs, num, CKA_LABEL, false);
+    FA_SET_BUF_ALLOC(attrs, num, CKA_ALWAYS_AUTHENTICATE, false);
     ret = p11prov_fetch_attributes(ctx, session, object, attrs, num);
     if (ret != CKR_OK) {
         /* free any allocated memory */
@@ -745,6 +776,7 @@ static CK_RV fetch_ec_key(P11PROV_CTX *ctx, P11PROV_SESSION *session,
     }
     FA_SET_BUF_ALLOC(attrs, num, CKA_ID, false);
     FA_SET_BUF_ALLOC(attrs, num, CKA_LABEL, false);
+    FA_SET_BUF_ALLOC(attrs, num, CKA_ALWAYS_AUTHENTICATE, false);
     ret = p11prov_fetch_attributes(ctx, session, object, attrs, num);
     if (ret != CKR_OK) {
         /* free any allocated memory */
diff --git a/src/objects.h b/src/objects.h
index dcf958b3..97ace2f6 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -21,6 +21,7 @@ CK_SLOT_ID p11prov_obj_get_slotid(P11PROV_OBJ *obj);
 CK_OBJECT_HANDLE p11prov_obj_get_handle(P11PROV_OBJ *obj);
 CK_OBJECT_CLASS p11prov_obj_get_class(P11PROV_OBJ *obj);
 CK_ATTRIBUTE *p11prov_obj_get_attr(P11PROV_OBJ *obj, CK_ATTRIBUTE_TYPE type);
+bool p11prov_obj_get_bool(P11PROV_OBJ *obj, CK_ATTRIBUTE_TYPE type, bool def);
 CK_KEY_TYPE p11prov_obj_get_key_type(P11PROV_OBJ *obj);
 CK_ULONG p11prov_obj_get_key_bit_size(P11PROV_OBJ *obj);
 CK_ULONG p11prov_obj_get_key_size(P11PROV_OBJ *obj);
diff --git a/src/session.c b/src/session.c
index 9f16e278..421462c4 100644
--- a/src/session.c
+++ b/src/session.c
@@ -390,7 +390,7 @@ CK_SLOT_ID p11prov_session_slotid(P11PROV_SESSION *session)
 /* returns a locked login_session if _session is not NULL */
 static CK_RV token_login(P11PROV_SESSION *session, P11PROV_URI *uri,
                          OSSL_PASSPHRASE_CALLBACK *pw_cb, void *pw_cbarg,
-                         struct p11prov_slot *slot)
+                         struct p11prov_slot *slot, CK_USER_TYPE user_type)
 {
     char cb_pin[MAX_PIN_LENGTH + 1] = { 0 };
     size_t cb_pin_len = 0;
@@ -452,7 +452,7 @@ static CK_RV token_login(P11PROV_SESSION *session, P11PROV_URI *uri,
 
     P11PROV_debug("Attempt Login on session %lu", session->session);
     /* Supports only USER login sessions for now */
-    ret = p11prov_Login(session->provctx, session->session, CKU_USER, pin,
+    ret = p11prov_Login(session->provctx, session->session, user_type, pin,
                         pinlen);
     if (ret == CKR_USER_ALREADY_LOGGED_IN) {
         ret = CKR_OK;
@@ -481,6 +481,31 @@ static CK_RV token_login(P11PROV_SESSION *session, P11PROV_URI *uri,
     return ret;
 }
 
+CK_RV p11prov_context_specific_login(P11PROV_SESSION *session, P11PROV_URI *uri,
+                                     OSSL_PASSPHRASE_CALLBACK *pw_cb,
+                                     void *pw_cbarg)
+{
+    P11PROV_SLOTS_CTX *sctx = NULL;
+    P11PROV_SLOT *slot = NULL;
+    CK_RV ret;
+
+    ret = p11prov_take_slots(session->provctx, &sctx);
+    if (ret != CKR_OK) {
+        return CKR_GENERAL_ERROR;
+    }
+
+    slot = p11prov_get_slot_by_id(sctx, p11prov_session_slotid(session));
+    if (!slot) {
+        ret = CKR_GENERAL_ERROR;
+    }
+
+    ret =
+        token_login(session, uri, pw_cb, pw_cbarg, slot, CKU_CONTEXT_SPECIFIC);
+
+    p11prov_return_slots(sctx);
+    return ret;
+}
+
 static CK_RV check_slot(P11PROV_CTX *ctx, P11PROV_SLOT *slot, P11PROV_URI *uri,
                         CK_MECHANISM_TYPE mechtype, bool rw)
 {
@@ -689,7 +714,7 @@ static CK_RV slot_login(P11PROV_SLOT *slot, P11PROV_URI *uri,
         /* we seem to already have a valid logged in session */
         ret = CKR_OK;
     } else {
-        ret = token_login(session, uri, pw_cb, pw_cbarg, slot);
+        ret = token_login(session, uri, pw_cb, pw_cbarg, slot, CKU_USER);
     }
 
 done:
diff --git a/src/session.h b/src/session.h
index 23efaa04..37b13602 100644
--- a/src/session.h
+++ b/src/session.h
@@ -21,6 +21,10 @@ CK_RV p11prov_take_login_session(P11PROV_CTX *provctx, CK_SLOT_ID slotid,
                                  P11PROV_SESSION **_session);
 void p11prov_return_session(P11PROV_SESSION *session);
 
+CK_RV p11prov_context_specific_login(P11PROV_SESSION *session, P11PROV_URI *uri,
+                                     OSSL_PASSPHRASE_CALLBACK *pw_cb,
+                                     void *pw_cbarg);
+
 typedef CK_RV (*p11prov_session_callback_t)(void *cbarg);
 void p11prov_session_set_callback(P11PROV_SESSION *session,
                                   p11prov_session_callback_t cb, void *cbarg);
diff --git a/src/signature.c b/src/signature.c
index 0100426b..bbbeb41f 100644
--- a/src/signature.c
+++ b/src/signature.c
@@ -775,6 +775,7 @@ static CK_RV p11prov_sig_operate_init(P11PROV_SIG_CTX *sigctx, bool digest_op,
     CK_SESSION_HANDLE sess;
     CK_SLOT_ID slotid;
     bool reqlogin = false;
+    bool always_auth = false;
     CK_RV ret;
 
     P11PROV_debug("called (sigctx=%p, digest_op=%s)", sigctx,
@@ -838,6 +839,15 @@ static CK_RV p11prov_sig_operate_init(P11PROV_SIG_CTX *sigctx, bool digest_op,
                       "Failed to open session on slot %lu", slotid);
     }
 
+    if (reqlogin) {
+        always_auth =
+            p11prov_obj_get_bool(sigctx->key, CKA_ALWAYS_AUTHENTICATE, false);
+    }
+
+    if (always_auth) {
+        ret = p11prov_context_specific_login(session, NULL, NULL, NULL);
+    }
+
 done:
     if (ret != CKR_OK) {
         p11prov_return_session(session);