Skip to content

Commit 8f50a16

Browse files
committed
Handle malformed CKA_PARAMETER_SET in NSS DB
Some versions of NSS have a bug where the CKA_PARAMETER_SET attribute is written to the database as a raw, platform-endian CK_ULONG (4 or 8 bytes) instead of the expected 4-byte big-endian database integer. This change adds a workaround to correctly read this attribute from malformed databases. It attempts to parse the stored blob as both a 32-bit and 64-bit integer, checking both little-endian and big-endian formats. Since the valid values for this attribute are small numbers, we can reliably recover the intended value, ensuring compatibility. Signed-off-by: Simo Sorce <simo@redhat.com>
1 parent 86781c8 commit 8f50a16

File tree

1 file changed

+75
-9
lines changed

1 file changed

+75
-9
lines changed

src/storage/nssdb/mod.rs

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,16 @@ const NSS_PIN_MAX: usize = 500;
7373
/// Length of the salt used for NSS password hashing (from SHA1_LENGTH).
7474
const NSS_PIN_SALT_LEN: usize = 20;
7575

76+
/// CKA_PARAMETER_SET bug fallback guard value:
77+
/// Generally numeric identifiers for parameters set are small,
78+
/// The largest so far is SLH-DSA with a value of 0x0C, but we'll give
79+
/// a little more space to avoid having to constantly tweak this in the
80+
/// future. It is unlikely any function will ever need more than 0x8F
81+
/// identifiers unless they decide to assign non consequent identifiers
82+
/// at some point in the future. By then we can probably drop this
83+
/// fallback code entriely
84+
const CKP_MAX_IDENTIFIER: u32 = 0x8F;
85+
7686
/// Formats an internal UID string from the table name and numeric NSS object ID.
7787
fn nss_id_format(table: &str, id: u32) -> String {
7888
format!("{}-{}-{}", NSS_ID_PREFIX, table, id)
@@ -430,6 +440,55 @@ impl NSSStorage {
430440
Ok(())
431441
}
432442

443+
/// NSS broke the database format by not converting CKA_PARAMETER_SET to a
444+
/// database ulong as it should have. We try to be compatible on reading
445+
/// nss databases that were malformed this way (we do not bother trying
446+
/// to write because there is no correct way to do it as what's written
447+
/// is platform's CK_ULONG and endianness dependent, so there is no stable
448+
/// "bad format". Luckily CKA_PARAMETER_SET has only a very small set of
449+
/// valid values and does not store arbitrary integers so we can also
450+
/// detect endianness violations on reading.
451+
fn cka_parameter_set_fixup(&self, blob: &[u8]) -> Result<CK_ULONG> {
452+
match blob.len() {
453+
4 => {
454+
let bytes: [u8; 4] = match blob.try_into() {
455+
Ok(b) => b,
456+
Err(_) => return Err(CKR_ATTRIBUTE_VALUE_INVALID)?,
457+
};
458+
/* assume correct format by default, try inverse endianness
459+
* later */
460+
let number = u32::from_be_bytes(bytes);
461+
if number < CKP_MAX_IDENTIFIER {
462+
return Ok(number as CK_ULONG);
463+
}
464+
/* try the other endianness */
465+
let number = u32::from_le_bytes(bytes);
466+
if number < CKP_MAX_IDENTIFIER {
467+
return Ok(number as CK_ULONG);
468+
}
469+
}
470+
8 => {
471+
let bytes: [u8; 8] = match blob.try_into() {
472+
Ok(b) => b,
473+
Err(_) => return Err(CKR_ATTRIBUTE_VALUE_INVALID)?,
474+
};
475+
/* assume little endianness by default case as LE are the
476+
* most common 64bit platforms */
477+
let number = u64::from_le_bytes(bytes);
478+
if number < CKP_MAX_IDENTIFIER as u64 {
479+
return Ok(number as CK_ULONG);
480+
}
481+
/* try the other endianness */
482+
let number = u64::from_be_bytes(bytes);
483+
if number < CKP_MAX_IDENTIFIER as u64 {
484+
return Ok(number as CK_ULONG);
485+
}
486+
}
487+
_ => (),
488+
}
489+
return Err(CKR_ATTRIBUTE_VALUE_INVALID)?;
490+
}
491+
433492
/// Converts rows returned from an NSS DB query into a PKCS#11 `Object`.
434493
///
435494
/// Maps NSS column names (e.g., "a81") to attribute types and converts
@@ -460,22 +519,29 @@ impl NSSStorage {
460519
}
461520
let bn: Option<&[u8]> =
462521
row.get_ref(i + offset)?.as_blob_or_null()?;
463-
let blob: &[u8] = match bn {
522+
let mut blob: &[u8] = match bn {
464523
Some(ref b) => b,
465524
None => continue,
466525
};
467526
let atype = AttrType::attr_id_to_attrtype(cols[i])?;
468527
let attr = match atype {
469528
AttrType::NumType => {
470-
if blob.len() != 4 {
471-
return Err(CKR_ATTRIBUTE_VALUE_INVALID)?;
472-
}
473-
let bytes: [u8; 4] = match blob.try_into() {
474-
Ok(b) => b,
475-
Err(_) => return Err(CKR_ATTRIBUTE_VALUE_INVALID)?,
529+
/* Handle NSS bug with CKA_PARAMETER_SET storage */
530+
let ulong = if cols[i] == CKA_PARAMETER_SET {
531+
match self.cka_parameter_set_fixup(blob) {
532+
Ok(u) => u,
533+
Err(e) => return Err(e),
534+
}
535+
} else {
536+
let bytes: [u8; 4] = match blob.try_into() {
537+
Ok(b) => b,
538+
Err(_) => {
539+
return Err(CKR_ATTRIBUTE_VALUE_INVALID)?
540+
}
541+
};
542+
let number = u32::from_be_bytes(bytes);
543+
number as CK_ULONG
476544
};
477-
let number = u32::from_be_bytes(bytes);
478-
let ulong = number as CK_ULONG;
479545
Attribute::from_attr_slice(
480546
cols[i],
481547
atype,

0 commit comments

Comments
 (0)