Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Memory ownership in PKCS #11 C_FindObjects where ulMaxObjectCount != 1

The authors of PKCS #11 v2.40 utilize a common pattern when an API returns a variable length list of items. In APIs such as C_GetSlotList and C_GetMechanismList, the application is expected to call the APIs twice. In the first invocation, a pointer to a CK_ULONG is set to the number of items that will be returned on the next invocation. This allows the application to allocate enough memory and invoke the API again to retrieve the results.

The C_FindObjects call also returns a variable number of items, but it uses a different paradigm. The parameter CK_OBJECT_HANDLE_PTR phObject is set to the head of the result list. The parameter CK_ULONG_PTR pulObjectCount is set to the number of items returned, which is ensured to be less than CK_ULONG ulMaxObjectCount.

The standard does not explicitly say that phObject must be a valid pointer to a block of memory large enough to hold ulMaxObjectCount CK_OBJECT_HANDLEs.

One could interpret the standard as meaning that the application must pessimistically allocate enough memory for ulMaxObjectCount objects. Alternately, one could interpret the standard as meaning that the PKCS #11 implementation will allocate pulObjectCount CK_OBJECT_HANDLEs and it is then the application's responsibility to free that memory. This later interpretation seems suspect however, as no where else in the standard does the implementation of PKCS #11 ever allocate memory.

The passage is:

C_FindObjects continues a search for token and session objects that 
match a template, obtaining additional object handles. hSession is 
the session’s handle; phObject points to the location that receives 
the list (array) of additional object handles; ulMaxObjectCount is 
the maximum number of object handles to be returned; pulObjectCount 
points to the location that receives the actual number of object 
handles returned.

If there are no more objects matching the template, then the location 
that pulObjectCount points to receives the value 0.

The search MUST have been initialized with C_FindObjectsInit.

The non-normative example is not very helpful, as it sets ulMaxObjectCount to 1. It does, however, allocate the memory for that one entry. Which seems to indicate that the application must pessimistically pre-allocate the memory.

CK_SESSION_HANDLE hSession;
CK_OBJECT_HANDLE hObject;
CK_ULONG ulObjectCount;
CK_RV rv;
.
.
rv = C_FindObjectsInit(hSession, NULL_PTR, 0);
assert(rv == CKR_OK);
while (1) {
 rv = C_FindObjects(hSession, &hObject, 1, &ulObjectCount);
 if (rv != CKR_OK || ulObjectCount == 0)
 break;
 .
 .
}
rv = C_FindObjectsFinal(hSession);
assert(rv == CKR_OK);

Specification Link: http://docs.oasis-open.org/pkcs11/pkcs11-base/v2.40/pkcs11-base-v2.40.pdf

like image 902
Huckle Avatar asked Sep 16 '15 06:09

Huckle


1 Answers

Yes, it would appear that the application is responsible for allocating space for the object handles returned by C_FindObjects(). The example code does this, even though it only requests a single object handle at a time, and so should you.

You could just as well rewrite the example code to request multiple object handles, e.g. like this:

#define MAX_OBJECT_COUNT 100  /* arbitrary value */

K_SESSION_HANDLE hSession;
CK_OBJECT_HANDLE hObjects[MAX_OBJECT_COUNT];
CK_ULONG ulObjectCount, i;
CK_RV rv;

rv = C_FindObjectsInit(hSession, NULL_PTR, 0);
assert(rv == CKR_OK);
while (1) {
  rv = C_FindObjects(hSession, hObjects, MAX_OBJECT_COUNT, &ulObjectCount);
  if (rv != CKR_OK || ulObjectCount == 0) break;
  for (i = 0; i < ulObjectCount; i++) {
    /* do something with hObjects[i] here */
  }
}
rv = C_FindObjectsFinal(hSession);
assert(rv == CKR_OK);

Presumably, the ability to request multiple object handles in a single C_FindObjects() call is intended as a performance optimization.

FWIW, this is pretty much exactly how many C standard library functions like fread() work as well. It'd be extremely inefficient to read data from a file one byte at a time with fgetc(), so the fread() function lets you allocate an arbitrarily large buffer and read as much data as will fit into it.

like image 195
Ilmari Karonen Avatar answered Oct 02 '22 22:10

Ilmari Karonen