55//! with the SQLite databases used by NSS for storing certificates, public
66//! keys, private keys, and trust settings.
77
8+ use std:: borrow:: Cow ;
89use std:: fmt:: Write as _;
910use std:: path:: Path ;
1011use std:: sync:: { Arc , Mutex , MutexGuard } ;
@@ -107,6 +108,13 @@ fn nss_id_parse(nssid: &str) -> Result<(String, u32)> {
107108/// (`CK_ULONG`). The column name format is 'a' followed by the hex value of
108109/// the attribute type.
109110fn nss_col_to_type ( col : & str ) -> Result < CK_ULONG > {
111+ if match col. chars ( ) . next ( ) {
112+ Some ( c) => c,
113+ None => '_' ,
114+ } != 'a'
115+ {
116+ return Err ( CKR_DEVICE_ERROR ) ?;
117+ }
110118 Ok ( CK_ULONG :: from_str_radix ( & col[ 1 ..] , 16 ) ?)
111119}
112120
@@ -153,6 +161,10 @@ pub struct NSSStorage {
153161 config : NSSConfig ,
154162 /// Thread-safe connection to the underlying SQLite database(s).
155163 conn : Arc < Mutex < Connection > > ,
164+ /// Columns cache for the main tables (should have the same size as
165+ /// known attributes, but could be more if we open a newer DB than we
166+ /// know of).
167+ cols : Vec < CK_ULONG > ,
156168 /// Key cache and encryption/decryption context for authenticated
157169 /// attributes.
158170 keys : KeysWithCaching ,
@@ -333,6 +345,88 @@ impl NSSStorage {
333345 }
334346
335347 tx. commit ( ) ?;
348+ /* Call check_columns for tis side effect of initializing self.cols */
349+ Self :: check_columns (
350+ & mut conn,
351+ & mut self . cols ,
352+ NSS_PUBLIC_SCHEMA ,
353+ NSS_PUBLIC_TABLE ,
354+ ) ?;
355+ Self :: check_columns (
356+ & mut conn,
357+ & mut self . cols ,
358+ NSS_PRIVATE_SCHEMA ,
359+ NSS_PRIVATE_TABLE ,
360+ ) ?;
361+ Ok ( ( ) )
362+ }
363+
364+ fn pragma_columns (
365+ conn : & mut MutexGuard < ' _ , rusqlite:: Connection > ,
366+ schema : & str ,
367+ table : & str ,
368+ ) -> Result < Vec < String > > {
369+ let mut stmt = conn. prepare ( & format ! (
370+ "SELECT * FROM {}.pragma_table_info('{}')" ,
371+ schema, table
372+ ) ) ?;
373+ let mut cols = Vec :: < String > :: new ( ) ;
374+ let mut rows = stmt. query ( [ ] ) ?;
375+ while let Some ( row) = rows. next ( ) ? {
376+ cols. push ( row. get ( 1 ) ?) ;
377+ }
378+ if cols. len ( ) == 0 {
379+ return Err ( CKR_GENERAL_ERROR ) ?;
380+ }
381+ return Ok ( cols) ;
382+ }
383+
384+ /* check that the database has all the expected columns, add missing ones
385+ * as needed. This is to support transition of existing databases to be
386+ * able to operate with the new PKCS#11 3.2 attributes */
387+ fn check_columns (
388+ conn : & mut MutexGuard < ' _ , rusqlite:: Connection > ,
389+ cols_cache : & mut Vec < CK_ULONG > ,
390+ schema : & str ,
391+ table : & str ,
392+ ) -> Result < ( ) > {
393+ let cols = Self :: pragma_columns ( conn, schema, table) ?;
394+
395+ if cols_cache. len ( ) == 0 {
396+ /* initialize with db order */
397+ for val in & cols {
398+ let attr = match nss_col_to_type ( val) {
399+ Ok ( a) => a,
400+ Err ( _) => continue , /* ignore non attribute columns */
401+ } ;
402+ cols_cache. push ( attr) ;
403+ }
404+ }
405+
406+ let mut not_found = NSS_KNOWN_ATTRIBUTES . to_vec ( ) ;
407+ for val in & cols {
408+ let typ = match nss_col_to_type ( val) {
409+ Ok ( t) => t,
410+ Err ( _) => continue , /* ignore non attribute columns */
411+ } ;
412+ /* For now we just ignore columns that are not known */
413+ if let Some ( idx) = not_found. iter ( ) . position ( |i| * i == typ) {
414+ not_found. swap_remove ( idx) ;
415+ }
416+ }
417+
418+ /* Now check if any known attribute was not found and add the
419+ * related column to the schema */
420+ for typ in not_found {
421+ let _ = conn. execute (
422+ & format ! (
423+ "ALTER TABLE {}.{} ADD COLUMN a{:x}" ,
424+ schema, table, typ
425+ ) ,
426+ params ! [ ] ,
427+ ) ?;
428+ cols_cache. push ( typ) ;
429+ }
336430 Ok ( ( ) )
337431 }
338432
@@ -341,31 +435,31 @@ impl NSSStorage {
341435 /// Maps NSS column names (e.g., "a81") to attribute types and converts
342436 /// stored BLOBs/numbers back into `Attribute` values. Handles the
343437 /// special NULL value.
344- fn rows_to_object ( mut rows : Rows , all_cols : bool ) -> Result < Object > {
438+ fn rows_to_object (
439+ & self ,
440+ mut rows : Rows ,
441+ attrs : & [ CK_ATTRIBUTE ] ,
442+ ) -> Result < Object > {
345443 let mut obj = Object :: new ( ) ;
346444
347- /* FIXME: move sourcing columns to db open */
348- let cols = match rows. as_ref ( ) {
349- Some ( s) => {
350- let cstr = s. column_names ( ) ;
351- let mut ctypes = Vec :: < CK_ULONG > :: with_capacity ( cstr. len ( ) ) ;
352- for cs in & cstr {
353- ctypes. push ( nss_col_to_type ( cs) ?) ;
354- }
355- ctypes
445+ let ( cols, offset) = if attrs. len ( ) == 0 {
446+ ( Cow :: Borrowed ( & self . cols ) , 1 )
447+ } else {
448+ let mut c = Vec :: < CK_ULONG > :: with_capacity ( attrs. len ( ) ) ;
449+ for a in attrs {
450+ c. push ( a. type_ ) ;
356451 }
357- None => return Err ( CKR_GENERAL_ERROR ) ? ,
452+ ( Cow :: Owned ( c ) , 0 )
358453 } ;
359454
360- let first = if all_cols { 1 } else { 0 } ;
361-
362455 if let Some ( row) = rows. next ( ) ? {
363- for i in first ..cols. len ( ) {
456+ for i in 0 ..cols. len ( ) {
364457 /* skip NSS vendor attributes */
365458 if ignore_attribute ( cols[ i] ) {
366459 continue ;
367460 }
368- let bn: Option < & [ u8 ] > = row. get_ref ( i) ?. as_blob_or_null ( ) ?;
461+ let bn: Option < & [ u8 ] > =
462+ row. get_ref ( i + offset) ?. as_blob_or_null ( ) ?;
369463 let blob: & [ u8 ] = match bn {
370464 Some ( ref b) => b,
371465 None => continue ,
@@ -477,7 +571,7 @@ impl NSSStorage {
477571 let conn = self . conn . lock ( ) ?;
478572 let mut stmt = conn. prepare ( sql) ?;
479573 let rows = stmt. query ( rusqlite:: params_from_iter ( query. params ) ) ?;
480- Self :: rows_to_object ( rows, attrs. len ( ) == 0 )
574+ self . rows_to_object ( rows, attrs)
481575 }
482576
483577 /* Searching for Objects:
@@ -879,7 +973,12 @@ impl Storage for NSSStorage {
879973 self . config . read_only ,
880974 ) ?;
881975 match check_table ( & mut conn, NSS_PUBLIC_SCHEMA , NSS_PUBLIC_TABLE ) {
882- Ok ( _) => ( ) ,
976+ Ok ( _) => Self :: check_columns (
977+ & mut conn,
978+ & mut self . cols ,
979+ NSS_PUBLIC_SCHEMA ,
980+ NSS_PUBLIC_TABLE ,
981+ ) ?,
883982 Err ( e) => ret = e. rv ( ) ,
884983 }
885984 }
@@ -892,7 +991,12 @@ impl Storage for NSSStorage {
892991 ) ?;
893992 match check_table ( & mut conn, NSS_PRIVATE_SCHEMA , NSS_PRIVATE_TABLE )
894993 {
895- Ok ( _) => ( ) ,
994+ Ok ( _) => Self :: check_columns (
995+ & mut conn,
996+ & mut self . cols ,
997+ NSS_PRIVATE_SCHEMA ,
998+ NSS_PRIVATE_TABLE ,
999+ ) ?,
8961000 Err ( e) => ret = e. rv ( ) ,
8971001 }
8981002 }
@@ -1420,6 +1524,7 @@ impl StorageDBInfo for NSSDBInfo {
14201524 Ok ( Box :: new ( NSSStorage {
14211525 config : config,
14221526 conn : conn,
1527+ cols : Vec :: with_capacity ( NSS_KNOWN_ATTRIBUTES . len ( ) ) ,
14231528 keys : KeysWithCaching :: default ( ) ,
14241529 } ) )
14251530 }
0 commit comments