Under Construction
Verifizieren der digitalen Signatur eines Filesets
Wir zeigen den Ablauf bei der Verifizierung eines Filesets im Detail. Dazu verwenden wir als Beispiel das Fileset-Update bos.rte.libc in der Version 7.3.2.1, das wir im temporären Verzeichnis /tmp/lpps abgelegt haben:
# ls -l /tmp/lpps
total 25352
-rw-r--r-- 1 root system 4122 Jul 23 14:04 .toc
-rw-r--r-- 1 root system 12970240 Jul 23 14:04 bos.rte.libc.7.3.2.1.U
#
Beim Installieren eines Filesets wird zur Verifizierung der digitalen Signatur das Shell-Skript /usr/sbin/pkgverify auf gerufen. Als Argument wird der absolute Pfad der BFF-Datei des Filesets angegeben:
/usr/sbin/pkgverify /tmp/lpps/bos.rte.libc.7.3.2.1.U
Das Shell-Skript kann man aber auch als root aufrufen und die Verifizierung für ein Fileset manuell starten. Wir haben im nachfolgenden nur die wesentlichen Schritte des Shell-Skripts für den obigen Fall beschrieben.
Zuerst werden der Paketname und das Build-Datum ermittelt:
+128 # determine package name / date of creation
+129 package=$($awk -v lfn=${infile##*/} '{ if ($1 == lfn) {print $(NF-1); exit} }' "${infile%/*}/.toc")
+130 timestamp=$($restore -Tvqf $infile 2>&1 | $awk -F": " '/backup/ {print $NF; exit}' | $awk '{ gsub(/CDT |CST /,""); print}' | $awk '{ gsub(/ /,"_"); print}' | $awk '{ gsub(/:/,"-"); print}')
Hinweis: In der Variablen infile ist der absolute Pfad der BFF-Datei hinterlegt.
Die beiden Variablen package und timestamp haben dann für unseren Fall die folgenden Werte:
# echo $package
bos
# echo $timestamp
Thu_Mar_21_11-13-28_2024
#
Dann wird eine Liste der zugehörigen Filesets des Pakets extrahiert:
+132 # build list of supported filesets
+133 FilesetList=$($installp -Ld $tmpstorage 2>/dev/null |$awk -v lfn=$package -F: '{ if ($1 == lfn) print $1":"$2":"$3":"$5}')
Ergibt in unserem Fall:
# echo $FilesetList
bos:bos.rte.libc:7.3.2.1:S
#
Hinweis: Die 4 Felder sind der Paketname, der Fileset-Name, die Version und der Fileset-Typ (hier „S“ für Single Update).
Bei mehreren Filesets wird das erste Fileset verwendet. Name des Filesets, Version und Typ werden in eigene Variablen extrahiert:
+139 match=$(echo $FilesetList | $awk '{print $1}')
+140 fileset=$(echo $match | $awk -F: '{print $2}')
+141 vrmf=$(echo $match | $awk -F: '{print $3}')
+142 ftype=$(echo $match | $awk -F: '{print $4}')
In unserem Fall erhält man die folgenden Werte für die Variablen fileset, vrmf und ftype:
# echo $fileset
bos.rte.libc
# echo $vrmf
7.3.2.1
# echo $ftype
S
#
Die Version wird in ihre 4 Bestandteile zerlegt:
+157 # build odmadd stanza for catalog record search
+158 ver=$(echo $vrmf | $cut -f1 -d".")
+159 rel=$(echo $vrmf | $cut -f2 -d".")
+160 mod=$(echo $vrmf | $cut -f3 -d".")
+161 fix=$(echo $vrmf | $cut -f4 -d".")
Was in unserem Fall die folgenden Werte ergibt:
# echo $ver
7
# echo $rel
3
# echo $mod
2
# echo $fix
1
#
Damit liegen alle nötigen Informationen vor, um im DSC (ODM dsc_inventory) nach dem Eintrag für die spezielle Version des Filesets bos.rte.libc zu suchen:
+163 # obtain the key & signature from dsc (NOTE: empty ftype is allowed)
+164 key_id=$(load_key_from_catalog $package $fileset $timestamp $ver $rel $mod $fix $ftype)
+165 signature=$(load_sig_from_catalog $package $fileset $timestamp $ver $rel $mod $fix $ftype)
Die beiden Funktionen load_key_from_catalog und load_sig_from_catalog starten letztlich einfach nur das folgende odmget Kommando:
# ODMDIR=/usr/lib/objrepos /usr/bin/odmget -q “pkg_name=bos AND lpp_name=bos.rte.libc AND ver=7 AND rel=3 AND mod=2 AND fix=1 AND timestamp=Thu_Mar_21_11-13-28_2024 AND ftype=S” dsc_inventory
dsc_inventory:
pkg_name = "bos"
lpp_name = "bos.rte.libc"
ver = 7
rel = 3
mod = 2
fix = 1
ftype = "S"
signature = "lRjpNE9gD+6nWyvPQJH0RoZgISpJrXxYUwlJoZuqfjCNfUWy73WgduuZRnkGeEeGHrC/LGy1VBH+NgXDfqKN+NxKZQmS7IA+wo4G0LsqcidzDIKE4ONbSOhQeA9k8izFxeFrLqFLmkntq6S3vcvku+5OF7ahoy6CuCmdczg580bs/SuQpEjp46XdDHwb6S8YlYBLYWvxunOlXVLneJBaOzCY/KGrKPbnHEwUhKwxamv3xoPWdqI7nOSjCHYoysNVUsIbukYId/XdmVeSIrC8/6EWmuxvZG/aHM0GDAoQdLy6zSNQ8zMlCBM2rfJcVxkgKNHFkzuNPDTW7aFwXVJ4XA=="
timestamp = "Thu_Mar_21_11-13-28_2024"
key = "3"
#
Dabei extrahiert die Funktion load_key_from_catalog das Feld key und die Funktion load_sig_from_catalog das Feld signature:
# echo $key_id
3
# echo $signature
lRjpNE9gD+6nWyvPQJH0RoZgISpJrXxYUwlJoZuqfjCNfUWy73WgduuZRnkGeEeGHrC/LGy1VBH+NgXDfqKN+NxKZQmS7IA+wo4G0LsqcidzDIKE4ONbSOhQeA9k8izFxeFrLqFLmkntq6S3vcvku+5OF7ahoy6CuCmdczg580bs/SuQpEjp46XdDHwb6S8YlYBLYWvxunOlXVLneJBaOzCY/KGrKPbnHEwUhKwxamv3xoPWdqI7nOSjCHYoysNVUsIbukYId/XdmVeSIrC8/6EWmuxvZG/aHM0GDAoQdLy6zSNQ8zMlCBM2rfJcVxkgKNHFkzuNPDTW7aFwXVJ4XA==
#
Hinweis: Die gefundene key_id ist die ID des zugehörigen Zertifikats, nicht die ID des zugehörigen Public-Keys. Zertifikat und Public-Key stehen normalerweise beide in der ODM dsc_key.
Als nächstes wird daher der ODM-Eintrag des Zertifikats mit der ermittelten ID (key_id) gesucht:
+170 # check if key needs extracting from keystore
+171 set_global_record $key_id KEY
+172 record_exists=$?
Auch die Funktion set_global_record startet odmget um das Zertifikat zu finden:
# ODMDIR=/usr/lib/objrepos odmget -q id=3 dsc_key
dsc_key:
id = 3
type = "certificate"
alias = "aixpublic_73"
location = "/etc/security/pkgverify/certfile/aixpublic_73.pem"
modulus = "b19c33e5eb0b4e2fbdcff3b2eeec31d5"
hash = "sha256"
keystore = "03"
#
Wurde ein Eintrag gefunden (das ist der Standardfall), ist der Exit Status der Funktion set_global_record gleich 1 und es werden eine Reihe von globalen Variablen gesetzt:
# echo $record_exists
1
# echo $key_id
3
# echo $key_type
certificate
# echo $key_alias
aixpublic_73
# echo $key_location
/etc/security/pkgverify/certfile/aixpublic_73.pem
# echo $key_modulus
b19c33e5eb0b4e2fbdcff3b2eeec31d5
# echo $key_hash
sha256
#
Nun wird über den gefundenen Zertifikats-Eintrag in dsc_key der zugehörige Keystore ermittelt und mit Hilfe des Keystores der zum Zertifikat gehörende Public-Key:
+176 # locate verification key
+177 s_id=$($odmget -q "type=certificate AND id=$key_id" ${odm_key} 2>/dev/null| $awk '/keystore/{print $3 ; exit}')
+178 k_id=$($odmget -q "type=key AND keystore=$s_id" ${odm_key} 2>/dev/null| $awk '/id/{print $3 ; exit}')
Die Keystore-ID wird in der Variablen s_id abgespeichert, die ID des Public-Keys in der Variablen k_id:
# echo $s_id
"03"
# echo $k_id
1
#
Mit der ermittelten ID des Public-Keys (k_id) wird nun der zugehörige ODM-Eintrag aus der dsc_key ODM geladen:
+191 set_global_record $k_id KEY
Dabei werden die folgenden Variablen gesetzt:
# echo $key_id
1
# echo $key_type
key
# echo $key_alias
aixpublic_73
# echo $key_location
/etc/security/pkgverify/key/aixpublic_73.key
# echo $key_modulus
8fc5d19e0ad949c89ab9469531101b37
# echo $key_hash
sha256
#
Der für die Verifizierung der Signatur zu verwendende Public-Key steht in der Datei /etc/security/pkgverify/key/aixpublic_73.key (Variable key_location) und der zu verwendende Algorithmus ist sha256 (Variable key_hash).
Die ganz oben ermittelte Signatur aus dem DSC wird nun in eine temporäre Datei geschrieben und dann vom Base64 Format zurück in ein binäres Format konvertitert:
+210 # verify signature
+211 echo $signature > $tmpsign
+212 $openssl base64 -d -in $tmpsign -out $tmpfile || exit 7
Damit kann nun die Verifizierung der digitalen Signatur mit dem openssl Kommando erfolgen:
+213 $openssl dgst -${key_hash} -verify $key_location -signature $tmpfile $infile || exit 8
Die Verifizierung ist für dieses Fileset erfolgreich:
# /usr/bin/openssl dgst -sha256 -verify /etc/security/pkgverify/key/aixpublic_73.key -signature /tmp/_pkgverify_12779880.dir/_pkgverify_12779880.dgst /tmp/lpps/bos.rte.libc.7.3.2.1.U
Verified OK
#
Alle temporär angelegten Dateien und Verzeichnisse werden im Anschluß gelöscht.
Der gezeigte Fall ist der einfachste Ablauf bei der Verifizierung, der in der Regel auch immer verwendet wird. Das Skript sieht aber noch ein paar weitere Fälle vor, falls ein Public-Key und/oder ein Zertifikat nicht existiert, werden diese mit Hilfe des Keystores automatisch erzeugt. Das ist aber dann nur einmalig notwendig.