[client, management] Fix the edge cases left open in the certificate posture check (#8174)

This commit is contained in:
Viktor Liu
2026-10-15 12:14:26 +02:00
committed by GitHub
parent 85db35194e
commit 9d837c6ae6
38 changed files with 718 additions and 206 deletions
+116
View File
@@ -70,3 +70,119 @@ func loadFirst(t *testing.T, paths ...string) *Module {
t.Skip("p11-kit trust module not installed")
return nil
}
// TestSlotList_RetriesWhenTheListGrows: a token plugged in between the size query and the
// fill makes the module answer CKR_BUFFER_TOO_SMALL; the list has to be asked for again.
func TestSlotList_RetriesWhenTheListGrows(t *testing.T) {
calls := 0
m := &module{cGetSlotList: func(_ byte, slots *ulong, count *ulong) ulong {
calls++
switch {
case slots == nil && calls == 1:
*count = 1
case slots != nil && calls == 2:
*count = 2
return rvBufferTooSmall
case slots == nil:
*count = 2
default:
*count = 2
backing := unsafe.Slice(slots, 2)
backing[0], backing[1] = 7, 9
}
return rvOK
}}
slots, err := m.slotList()
require.NoError(t, err)
assert.Equal(t, []ulong{7, 9}, slots, "the list is read again at its new size")
}
func TestSlotList_GivesUpOnAListThatKeepsGrowing(t *testing.T) {
m := &module{cGetSlotList: func(_ byte, slots *ulong, count *ulong) ulong {
if slots == nil {
*count = 1
return rvOK
}
return rvBufferTooSmall
}}
_, err := m.slotList()
assert.Error(t, err, "a module that never settles on a size is an error, not a loop")
}
func TestCheckFunctionListVersion(t *testing.T) {
assert.NoError(t, checkFunctionListVersion(version{major: 2, minor: 40}))
assert.NoError(t, checkFunctionListVersion(version{major: 3, minor: 0}))
assert.Error(t, checkFunctionListVersion(version{major: 1, minor: 0}), "a pre-2.0 list has no entry points to index")
assert.Error(t, checkFunctionListVersion(version{}), "an empty version is not a function list")
}
// fakeFindModule answers C_FindObjects from batches, a handle per entry, and reports how
// the search was torn down.
func fakeFindModule(batches [][]ulong, finalized *int) *module {
next := 0
return &module{
cFindObjectsInit: func(ulong, *attribute, ulong) ulong { return rvOK },
cFindObjectsFinal: func(ulong) ulong { *finalized++; return rvOK },
cFindObjects: func(_ ulong, objects *ulong, max ulong, count *ulong) ulong {
if next >= len(batches) {
*count = 0
return rvOK
}
batch := batches[next]
next++
copy(unsafe.Slice(objects, max), batch)
*count = ulong(len(batch))
return rvOK
},
}
}
var findTemplate = []Attribute{{Type: AttrClass, Value: ULong(ClassPrivateKey)}}
// TestFindObjects_ReadsPastAShortBatch: PKCS#11 ends a search with an empty batch, not with
// one shorter than asked for, so a module that hands out its handles in small batches must
// still have all of them returned.
func TestFindObjects_ReadsPastAShortBatch(t *testing.T) {
finalized := 0
m := fakeFindModule([][]ulong{{1, 2}, {3}, {4, 5, 6}}, &finalized)
objects, err := m.findObjects(1, findTemplate)
require.NoError(t, err)
assert.Equal(t, []Object{1, 2, 3, 4, 5, 6}, objects, "every batch is read until the module returns an empty one")
assert.Equal(t, 1, finalized, "the search is finished once")
}
func TestFindObjects_StopsAtAnEmptyBatch(t *testing.T) {
finalized := 0
m := fakeFindModule(nil, &finalized)
objects, err := m.findObjects(1, findTemplate)
require.NoError(t, err)
assert.Empty(t, objects, "a search that finds nothing returns nothing")
assert.Equal(t, 1, finalized)
}
// TestFindObjects_BoundsAModuleThatNeverEnds: a module that keeps returning handles must
// not make the daemon allocate without limit.
func TestFindObjects_BoundsAModuleThatNeverEnds(t *testing.T) {
finalized := 0
endless := &module{
cFindObjectsInit: func(ulong, *attribute, ulong) ulong { return rvOK },
cFindObjectsFinal: func(ulong) ulong { finalized++; return rvOK },
cFindObjects: func(_ ulong, _ *ulong, _ ulong, count *ulong) ulong {
*count = findBatch
return rvOK
},
}
_, err := endless.findObjects(1, findTemplate)
assert.Error(t, err, "an endless search is cut off")
assert.Equal(t, 1, finalized, "and still finished")
}