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,12 @@ 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+ return Err ( CKR_DEVICE_ERROR ) ?;
116+ }
110117 Ok ( CK_ULONG :: from_str_radix ( & col[ 1 ..] , 16 ) ?)
111118}
112119
@@ -153,6 +160,10 @@ pub struct NSSStorage {
153160 config : NSSConfig ,
154161 /// Thread-safe connection to the underlying SQLite database(s).
155162 conn : Arc < Mutex < Connection > > ,
163+ /// Columns cache for the main tables (should have the same size as
164+ /// known attributes, but could be more if we open a newer DB than we
165+ /// know of).
166+ cols : Vec < CK_ULONG > ,
156167 /// Key cache and encryption/decryption context for authenticated
157168 /// attributes.
158169 keys : KeysWithCaching ,
@@ -333,6 +344,72 @@ impl NSSStorage {
333344 }
334345
335346 tx. commit ( ) ?;
347+ /* Call check_columns for tis side effect of initializing self.cols */
348+ Self :: check_columns ( & mut conn, & mut self . cols , NSS_PUBLIC_SCHEMA , NSS_PUBLIC_TABLE ) ?;
349+ Self :: check_columns ( & mut conn, & mut self . cols , NSS_PRIVATE_SCHEMA , NSS_PRIVATE_TABLE ) ?;
350+ Ok ( ( ) )
351+ }
352+
353+ fn pragma_columns (
354+ conn : & mut MutexGuard < ' _ , rusqlite:: Connection > ,
355+ schema : & str ,
356+ table : & str
357+ ) -> Result < Vec < String > > {
358+ let mut stmt = conn. prepare (
359+ & format ! ( "SELECT * FROM {}.pragma_table_info('{}')" , schema, table)
360+ ) ?;
361+ let mut cols = Vec :: < String > :: new ( ) ;
362+ let mut rows = stmt. query ( [ ] ) ?;
363+ while let Some ( row) = rows. next ( ) ? {
364+ cols. push ( row. get ( 1 ) ?) ;
365+ }
366+ if cols. len ( ) == 0 {
367+ return Err ( CKR_GENERAL_ERROR ) ?;
368+ }
369+ return Ok ( cols) ;
370+ }
371+
372+ /* check that the database has all the expected columns, add missing ones
373+ * as needed. This is to support transition of existing databases to be
374+ * able to operate with the new PKCS#11 3.2 attributes */
375+ fn check_columns (
376+ conn : & mut MutexGuard < ' _ , rusqlite:: Connection > ,
377+ cols_cache : & mut Vec < CK_ULONG > ,
378+ schema : & str ,
379+ table : & str
380+ ) -> Result < ( ) > {
381+
382+ let cols = Self :: pragma_columns ( conn, schema, table) ?;
383+
384+ if cols_cache. len ( ) == 0 {
385+ /* initialize with db order */
386+ for val in & cols {
387+ let attr = match nss_col_to_type ( val) {
388+ Ok ( a) => a,
389+ Err ( _) => continue , /* ignore non attribute columns */
390+ } ;
391+ cols_cache. push ( attr) ;
392+ }
393+ }
394+
395+ let mut not_found = NSS_KNOWN_ATTRIBUTES . to_vec ( ) ;
396+ for val in & cols {
397+ let typ = match nss_col_to_type ( val) {
398+ Ok ( t) => t,
399+ Err ( _) => continue , /* ignore non attribute columns */
400+ } ;
401+ /* For now we just ignore columns that are not known */
402+ if let Some ( idx) = not_found. iter ( ) . position ( |i| * i == typ) {
403+ not_found. swap_remove ( idx) ;
404+ }
405+ }
406+
407+ /* Now check if any known attribute was not found and add the
408+ * related column to the schema */
409+ for typ in not_found {
410+ let _ = conn. execute ( & format ! ( "ALTER TABLE {}.{} ADD COLUMN a{:x}" , schema, table, typ) , params ! [ ] ) ?;
411+ cols_cache. push ( typ) ;
412+ }
336413 Ok ( ( ) )
337414 }
338415
@@ -341,31 +418,26 @@ impl NSSStorage {
341418 /// Maps NSS column names (e.g., "a81") to attribute types and converts
342419 /// stored BLOBs/numbers back into `Attribute` values. Handles the
343420 /// special NULL value.
344- fn rows_to_object ( mut rows : Rows , all_cols : bool ) -> Result < Object > {
421+ fn rows_to_object ( & self , mut rows : Rows , attrs : & [ CK_ATTRIBUTE ] ) -> Result < Object > {
345422 let mut obj = Object :: new ( ) ;
346423
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
424+ let ( cols, offset) = if attrs. len ( ) == 0 {
425+ ( Cow :: Borrowed ( & self . cols ) , 1 )
426+ } else {
427+ let mut c = Vec :: < CK_ULONG > :: with_capacity ( attrs. len ( ) ) ;
428+ for a in attrs {
429+ c. push ( a. type_ ) ;
356430 }
357- None => return Err ( CKR_GENERAL_ERROR ) ? ,
431+ ( Cow :: Owned ( c ) , 0 )
358432 } ;
359433
360- let first = if all_cols { 1 } else { 0 } ;
361-
362434 if let Some ( row) = rows. next ( ) ? {
363- for i in first ..cols. len ( ) {
435+ for i in 0 ..cols. len ( ) {
364436 /* skip NSS vendor attributes */
365437 if ignore_attribute ( cols[ i] ) {
366438 continue ;
367439 }
368- let bn: Option < & [ u8 ] > = row. get_ref ( i) ?. as_blob_or_null ( ) ?;
440+ let bn: Option < & [ u8 ] > = row. get_ref ( i + offset ) ?. as_blob_or_null ( ) ?;
369441 let blob: & [ u8 ] = match bn {
370442 Some ( ref b) => b,
371443 None => continue ,
@@ -477,7 +549,7 @@ impl NSSStorage {
477549 let conn = self . conn . lock ( ) ?;
478550 let mut stmt = conn. prepare ( sql) ?;
479551 let rows = stmt. query ( rusqlite:: params_from_iter ( query. params ) ) ?;
480- Self :: rows_to_object ( rows, attrs. len ( ) == 0 )
552+ self . rows_to_object ( rows, attrs)
481553 }
482554
483555 /* Searching for Objects:
@@ -879,7 +951,7 @@ impl Storage for NSSStorage {
879951 self . config . read_only ,
880952 ) ?;
881953 match check_table ( & mut conn, NSS_PUBLIC_SCHEMA , NSS_PUBLIC_TABLE ) {
882- Ok ( _) => ( ) ,
954+ Ok ( _) => Self :: check_columns ( & mut conn , & mut self . cols , NSS_PUBLIC_SCHEMA , NSS_PUBLIC_TABLE ) ? ,
883955 Err ( e) => ret = e. rv ( ) ,
884956 }
885957 }
@@ -892,7 +964,7 @@ impl Storage for NSSStorage {
892964 ) ?;
893965 match check_table ( & mut conn, NSS_PRIVATE_SCHEMA , NSS_PRIVATE_TABLE )
894966 {
895- Ok ( _) => ( ) ,
967+ Ok ( _) => Self :: check_columns ( & mut conn , & mut self . cols , NSS_PRIVATE_SCHEMA , NSS_PRIVATE_TABLE ) ? ,
896968 Err ( e) => ret = e. rv ( ) ,
897969 }
898970 }
@@ -1420,6 +1492,7 @@ impl StorageDBInfo for NSSDBInfo {
14201492 Ok ( Box :: new ( NSSStorage {
14211493 config : config,
14221494 conn : conn,
1495+ cols : Vec :: with_capacity ( NSS_KNOWN_ATTRIBUTES . len ( ) ) ,
14231496 keys : KeysWithCaching :: default ( ) ,
14241497 } ) )
14251498 }
0 commit comments