Systemd: Aus den Wirren des Paketmanagements

„Aus den Wirren der Paketabhängigkeiten im Dschungel von Systemd“ man könnte einen Roman damit betiteln 🙂  Gestern abend, es war mal wieder Serverupdatezeit, flutschte eine Anzeige eines Fedora-Paketes, daß keinen Sinn machte, über den Bildschirm: qrencode-libs

QR Codes auf einem Server?

Ja, wenn man eine Webseite hat, die z.b. QR Codes ausgibt, weil eine HandyApp einen Bestätigungscode haben will, warum nicht. Dummerweise hatte dieser Server genau einen Job und der hatte nichts mit QR Codes zu tun. „Naja,ok, das wird jemand mal irgendwann für was ausprobiert haben… kann weg!“ denkste Dir so.. und dann kommt das Erwachen: „Wieso will dnf jetzt systemd löschen?“

$ sudo dnf erase qrencode-libs
[sudo] Passwort für  ….. :
Fehler:
Problem: The operation would result in removing the following protected packages: systemd

Das kommt so …

Der Systemd hat eine harte Abhängigkeit auf die lib von dem qrencoder :

$ rpm -q -R systemd | grep qrenc
libqrencode.so.3()(64bit)

ganz genau genommen ist es journalctl:

$ ldd /usr/bin/journalctl |grep qrenc
libqrencode.so.3 => /lib64/libqrencode.so.3 (0x00007fef36540000)

„Kann mir bitte einer erklären, wieso journalctl QR CODES bauen können müßte ?????“

Kann ja wohl nur ein Fehler sein 😉   Der Maintainer bei Redhat war da jetzt anderer Meinung:

What do you mean "wrongfully"? It's "rightfullly" linking against qrencode-libs because that functionality is used by journalctl.
While somewhat unfortunate, it's correct.

Steht aber allein da, denn auch bei Manjaro Linux ist das schon mal jemandem vor mir aufgefallen und siehe:

https://forum.manjaro.org/t/systemd-238-51-1-has-picked-up-a-dependency-on-qrencode/43070

Could this be due to a dirty chroot?

$ ldd `which journalctl`
	linux-vdso.so.1 (0x00007fff47fc9000)
	libpthread.so.0 => /usr/lib/libpthread.so.0 (0x00007f55fd82e000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007f55fd477000)
	libsystemd-shared-238.so => /usr/lib/systemd/libsystemd-shared-238.so (0x00007f55fd027000)
	libqrencode.so.4 => not found

I’m pretty certain journalctl doesn’t need qrencode?

Das sehe ich auch so, trotzdem habe ich mal etwas geforscht und die Ursache gefunden:

journalctl .c

#if HAVE_QRENCODE
/* If this is not an UTF-8 system don’t print any QR codes */
if (is_locale_utf8()) {
fputs(„\nTo transfer the verification key to your phone please scan the QR code below:\n\n“, stderr);
print_qr_code(stderr, seed, seed_size, n, arg_interval, hn, machine);
}
#endif

Jetzt wirds spannend:

Wozu wird das benutzt?

Um, wenn es ein UTF8-System ist UND der Code mit dem QR Support kompiliert wurde, einen Verifikations Schlüssel als QR CODE fürs Handy auszugeben, um mit dem Versiegelungs-Schlüssel abgesicherte Journaleinträge zu prüfen.

Das Verfahren heißt bei Systemd Forward Secure Sealing (FSS).  Keine Ahnung wer das Feature von Journald nutzt. Es klingt jedenfalls nach einer brauchbaren Idee, falls ein Hacker die Einträge manipuliert. Ich bezweifle aber stark, daß der Key dabei per QR Code an ein Handy übermittelt werden muß, statt per SCP an einen anderen Server.

Für diese eine Zeile Code da oben, die kaum jemand jemals einsetzen wird (Bitte Zahlen liefern, wer welche hat), immer noch diese Lib mit sich rumschleppen… was solls, GB sind billig  😉

Wie man sich RPM Scripte ansieht

Heute geht es um RPMs und die Install und Uninstall Scripte die RPMs so mit sich rumschleppen.

Wie man RPM-Files genauer betrachtet

Zunächst schauen wir mal in ein RPM am Beispiel des Zoomclienten für Linux rein, was überhaupt drin ist:

# rpm -ql ./zoom_x86_64.rpm

Allerdings sieht man dabei nur die Dateien des RPM, aber nicht die Scripte oder die Info des RPM:

# less zoom_x86_64.rpm

Ja, genau, less 🙂 Wer hätte das gedacht, daß dies beides gleichzeitig anzeigt 🙂

Name : zoom
Version : 2.8.183302.0415
Release : 1
Architecture: x86_64
Install Date: (not installed)
Group : default
Size : 246299262
License : see https://www.zoom.us/
Signature : RSA/SHA1, Di 16 Apr 2019 09:02:21 CEST, Key ID b903bf1861a7c71d
Source RPM : zoom-2.8.183302.0415-1.src.rpm
Build Date : Di 16 Apr 2019 08:59:45 CEST
Build Host : localhost
Relocations : / 
Packager : Zoom Linux Team <linux-dev@zoom.us>
Vendor : Zoom Video Communications, Inc.
URL : https://www.zoom.us
Summary : Zoom, #1 Video Conferencing and Web Conferencing Service \nZoom, the cloud meeting company, unifies cloud video conferencing, simple online meetings, and group messaging into one easy-to-use platform. Our solution offers the best video, audio, and screen-sharing experience across Zoom Rooms, Windows, Mac, Linux, iOS, Android, and H.323/SIP room systems.
Description :
Zoom, #1 Video Conferencing and Web Conferencing Service \nZoom, the cloud meeting company, unifies cloud video conferencing, simple online meetings, and group messaging into one easy-to-use platform. Our solution offers the best video, audio, and screen-sharing experience across Zoom Rooms, Windows, Mac, Linux, iOS, Android, and H.323/SIP room systems.
-rw-rw-r-- 1 root root 513 Jul 26 2018 /opt/zoom/Droplet.pcm
-rw-rw-r-- 1 root root 140 Sep 13 2018 /opt/zoom/Qt/WebSockets/qmldir
-rw-rw-r-- 1 root root 2637 Sep 13 2018 /opt/zoom/Qt/labs/calendar/DayOfWeekRow.qml
-rw-rw-r-- 1 root root 7835 Sep 13 2018 /opt/zoom/Qt/labs/calendar/DayOfWeekRow.qmlc
-rw-rw-r-- 1 root root 2703 Sep 13 2018 /opt/zoom/Qt/labs/calendar/MonthGrid.qml
-rw-rw-r-- 1 root root 8747 Sep 13 2018 /opt/zoom/Qt/labs/calendar/MonthGrid.qmlc
-rw-rw-r-- 1 root root 2645 Sep 13 2018 /opt/zoom/Qt/labs/calendar/WeekNumberColumn.qml
-rw-rw-r-- 1 root root 7835 Sep 13 2018 /opt/zoom/Qt/labs/calendar/WeekNumberColumn.qmlc
-rwxrwxr-x 1 root root 117816 Sep 13 2018 /opt/zoom/Qt/labs/calendar/libqtlabscalendarplugin.so
-rw-rw-r-- 1 root root 4968 Sep 13 2018 /opt/zoom/Qt/labs/calendar/plugins.qmltypes
-rw-rw-r-- 1 root root 187 Sep 13 2018 /opt/zoom/Qt/labs/calendar/qmldir
-rwxrwxr-x 1 root root 64624 Sep 13 2018 /opt/zoom/Qt/labs/folderlistmodel/libqmlfolderlistmodelplugin.so
-rw-rw-r-- 1 root root 2341 Sep 13 2018 /opt/zoom/Qt/labs/folderlistmodel/plugins.qmltypes
-rw-rw-r-- 1 root root 124 Sep 13 2018 /opt/zoom/Qt/labs/folderlistmodel/qmldir

Geht natürlich auch direkt mit rpm :

# rpm -qi foo.rpm

und wer auch die Changelogs sehen will:

# rpm -qi –changelog foo.rpm

Jetzt ging es aber um die Installationsscripte, die ausgeführt werden, wenn man Pakete installiert oder eben deinstalliert:

# rpm -qp –scripts foo.rpm

Wenn man das beim Beispiel Zoom macht, kommt das raus:

# rpm -qp –scripts zoom_x86_64.rpm

Warnung: zoom_x86_64.rpm: Header V4 RSA/SHA1 Signature, Schlüssel-ID 61a7c71d: NOKEY
postinstall scriptlet (using /bin/sh):
#!/bin/bash
# Program:
# script to be run after package installation

echo "run post install script, action is $1..."

#ln -s -f /opt/zoom/ZoomLauncher /usr/bin/zoom

#$1 folder path
function remove_folder
{
if [ -d $1 ]; then
rm -rf $1
fi
}

echo current home is $HOME
remove_folder "$HOME/.cache/zoom"

update-mime-database /usr/share/mime || true
#update-desktop-database || true
if [ -x "/usr/bin/update-desktop-database" ]; then 
update-desktop-database || true
fi
postuninstall scriptlet (using /bin/sh):
#!/bin/bash
# Program:
# script to be run after package removal

echo "run post uninstall script, action is $1 ..."

[ "$1" == "0" ] || exit 0

echo current home is $HOME

if [ -L "/usr/bin/zoom" ]; then 
rm /usr/bin/zoom 
fi

#$1 folder path
function remove_folder
{
if [ -d $1 ]; then
rm -rf $1
fi
}

#$1 file path
function remove_file
{
if [ -f $1 ]; then
rm -f $1
fi
}

remove_folder "/opt/zoom"
remove_folder "$HOME/.zoom/logs"
remove_folder "$HOME/.cache/zoom"
#remove_file "$HOME/.config/zoomus.conf"

#logged_in_users=$(who -q | head -n 1)
#sorted_users=$(echo "$logged_in_users"|tr " " "\n"|sort|uniq|tr "\n" " ")
#for user in $sorted_users;do
# echo "removing $(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.zoom..."
# remove_folder "$(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.zoom"
# echo "removing $(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.config/zoomus.conf..."
# remove_file "$(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.config/zoomus.conf"
#done

Orange ist das Installscript, Grün ist das Uninstallscript

Denn Sie wissen nicht, was Sie tun

Ok, was fällt uns daran auf?

echo current home is $HOME
...
remove_folder "/opt/zoom"
remove_folder "$HOME/.zoom/logs"
remove_folder "$HOME/.cache/zoom"

Es soll also im Home des Users etwas gelöscht werden ? Jetzt zeige ich Euch mal was das Script ausgegeben hat :

# dnf erase zoom
Abhängigkeiten sind aufgelöst.
================================================================================================================================================================================================================================================================================
Paket Arch Version Paketquelle Größe
================================================================================================================================================================================================================================================================================
Entfernen:
zoom x86_64 2.8.183302.0415-1 @@commandline 235 M

Transaktionsübersicht
================================================================================================================================================================================================================================================================================
Entfernen 1 Paket

Freigegebener Speicherplatz: 235 M
Ist dies in Ordnung? [j/N]:j
Transaktionsüberprüfung wird ausgeführt
Transaktionsprüfung war erfolgreich.
Transaktion wird getestet
Transaktionstest war erfolgreich.
Transaktion wird ausgeführt
Vorbereitung läuft : 1/1 
Löschen : zoom-2.8.183302.0415-1.x86_64 1/1 
Ausgeführtes Scriptlet: zoom-2.8.183302.0415-1.x86_64 1/1 
run post uninstall script, action is 0 ...
current home is /root
Überprüfung läuft : zoom-2.8.183302.0415-1.x86_64 1/1

Entfernt:
zoom.x86_64 2.8.183302.0415-1

Fertig.

Na? Wer hats gesehen?  … Richtig: „current home is /root“  wie sinnvoll dort Configs u.ä. zu löschen oder gar anzulegen, wo Root doch gar nicht der User ist, des es ausgeführt hat. Um das zu beheben, war der Code hier mal gedacht:

#logged_in_users=$(who -q | head -n 1)
#sorted_users=$(echo "$logged_in_users"|tr " " "\n"|sort|uniq|tr "\n" " ")
#for user in $sorted_users;do
# echo "removing $(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.zoom..."
# remove_folder "$(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.zoom"
# echo "removing $(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.config/zoomus.conf..."
# remove_file "$(grep -w ^$user /etc/passwd | cut -d ":" -f6)""/.config/zoomus.conf"
#done

Selbst wenn das einkommentiert gewesen wäre, wäre das wenig hilfreich gewesen, weil was wenn der User gar nicht eingeloggt ist, wenn das Uninstallscript läuft? Genau, dann würden die Files auch nicht gelöscht werden 😀  Da haben sie es dann gleich gelassen 😉

Einmal mit Profis und so..

Wieso habe ich das eigentlich nach Benutzung gelöscht? Ach ja, wegen der sechs CVE Sicherheitslücken allein in den statisch gelinkten QT5 Libraries seit September 2018 😀

#CVE IDCWE ID# of ExploitsVulnerability Type(s)Publish DateUpdate DateScoreGained Access LevelAccessComplexityAuthenticationConf.Integ.Avail.
1CVE-2018-19873119Overflow2018-12-262019-01-08
7.5
NoneRemoteLowNot requiredPartialPartialPartial
An issue was discovered in Qt before 5.11.3. QBmpHandler has a buffer overflow via BMP data.
2CVE-2018-198723692019-03-212019-04-24
4.3
NoneRemoteMediumNot requiredNoneNonePartial
An issue was discovered in Qt 5.11. A malformed PPM image causes a division by zero and a crash in qppmhandler.cpp.
3CVE-2018-198714002018-12-262019-04-25
4.3
NoneRemoteMediumNot requiredNoneNonePartial
An issue was discovered in Qt before 5.11.3. There is QTgaFile Uncontrolled Resource Consumption.
4CVE-2018-198704762018-12-262019-04-25
6.8
NoneRemoteMediumNot requiredPartialPartialPartial
An issue was discovered in Qt before 5.11.3. A malformed GIF image causes a NULL pointer dereference in QGifHandler resulting in a segmentation fault.
5CVE-2018-19869202018-12-262019-04-25
4.3
NoneRemoteMediumNot requiredNoneNonePartial
An issue was discovered in Qt before 5.11.3. A malformed SVG image causes a segmentation fault in qsvghandler.cpp.
6CVE-2018-198655322018-12-052019-05-10
5.0
NoneRemoteLowNot requiredPartialNoneNone
A keystroke logging issue was discovered in Virtual Keyboard in Qt 5.7.x, 5.8.x, 5.9.x, 5.10.x, and 5.11.x before 5.11.3.

Den Rest der App zu analysieren habe ich mir dann gleich erspart 😉

Beworben wurde das RPM übrigens mit „ab Fedora 21“ .. das ist ja auch nur schon 4 Jahre her. Daher hat ich dann das Filedatum der Libs verwundert, denn die stammen von 2018. Ob das damit wirklich noch „ab Fedora 21“ läuft, hmm, wer weiß, ich probier es jedenfalls nicht aus 😉

Die Größe von installierten Paketen ermitteln – RPM Edition

Was Niko in seinem Techgruben-Beitrag Größe von installierten Paketen anzeigen für debianbasierte Paketmanager schön gezeigt hat, geht leider bei RPMs nicht ganz so leicht, aber das wird uns nicht aufhalten. @Nico: Guter Beitrag.Weitermachen 🙂  In meinem Beitrag geht es heute nicht nur um die Größe, sondern auch um das „wie man zum Ergebnis kommt“. Wer die Lösung sucht, die ist ganz am Ende.

Die Größe von installierten Paketen ermitteln

Die Option „-q“ steht bei rpm für Query, also die Datenbankabfrage. rpm -q paketname  liefert den vollständigen Paketnamen aus. Einfaches Beispiel:

$ rpm -q firefox
firefox-65.0-4.fc28.x86_64

Wir haben den Paketnamen, klasse. Und nun ? Wenn wir mehr über Firefox wissen möchten, dann müssen wir RPM das sagen:

$ rpm -qi firefox
Name : firefox
Version : 65.0
Release : 4.fc28
Architecture: x86_64
Install Date: Mo 18 Feb 2019 12:01:46 CET
Group : Unspecified
Size : 221446243
License : MPLv1.1 or GPLv2+ or LGPLv2+
Signature : RSA/SHA256, Di 05 Feb 2019 12:51:38 CET, Key ID e08e7e629db62fb1
Source RPM : firefox-65.0-4.fc28.src.rpm
Build Date : Mo 04 Feb 2019 19:07:57 CET
Build Host : buildvm-20.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager : Fedora Project
Vendor : Fedora Project
URL : https://www.mozilla.org/firefox/
Bug URL : https://bugz.fedoraproject.org/firefox
Summary : Mozilla Firefox Web browser
Description :
Mozilla Firefox is an open-source web browser, designed for standards
compliance, performance and portability.

Die Option „i“ gibt uns also detaillierte Informationen über das Paket aus. Da könnte man mit grep und viel Bashmagie was drehen. Bleibt noch das Problem, daß wir den Paketnamen brauchen. Das können wir leicht umgehen, in dem wir pauschal alle Pakete auflisten lassen. Die Option „-a“ macht es möglich:  rpm -qia  . Probiert es nicht aus, das ist echt viel an Infos 🙂

Das Queryformat

Natürlich geht das besser, denn RPM bietet an, daß es die Infos genauso ausgibt wie man es braucht. Die nötige Option lautet „–queryformat“ :

$ rpm -q firefox –queryformat „%{NAME} uses %{SIZE:humaniec}\n“
firefox uses 211M

Schön, damit kann man arbeiten 🙂 Die Größe nach vorn, Paketname dahinter:

$ rpm -q firefox –queryformat „%{SIZE:humaniec}:%{NAME}\n“
211M:firefox

Das ist schon besser. Da kann man einen Sort drauf ansetzen:

rpm -qa –queryformat „%{SIZE:humaniec}:%{NAME}\n“ | sort -r -n

„-r -n“  Reverse, also groß zuerst, und numerisch sortieren. Sort erkennt automatisch, was keine Zahl mehr ist und sortiert entsprechend. Aber stimmt die Ausgabe auch wirklich?

Schauen wir uns mal die Top 30 an :

$ rpm -qa –queryformat „%{SIZE:humaniec}:%{NAME}\n“ | sort -r -n | head -n 30

1024K:python2-coverage
1022K:openssh-server
1019K:xorg-x11-drv-wacom
1019K:libgcrypt
1018K:tix
1016K:libmx
1015K:vte
1015K:python2-simplejson
1010K:pyzy
1009K:ncurses-libs
1008K:libappstream-glib
1007K:libxcb
1007K:libodfgen
1007K:boost-locale
1006K:eris
1006K:dracut
1004K:libxcb
1004K:libtomcrypt
1003:openoffice-brand-impress
1002:openoffice-brand-writer
1000:openoffice-brand-math
1000:openoffice-brand-draw
1000:openoffice-brand-calc
1000:openoffice-brand-base
999K:at-spi
997K:vte3
996K:mdadm
995K:rubberband
995K:ncurses-libs
994K:cjs

Ein Paket mit 1024 KB, also 1 MB, soll das größte Paket in der Liste sein? Das kann nicht sein.  Ist es auch nicht. Schon das Firefoxpaket von oben hat 211 MB. Wo ist da jetzt der Fehler?

Ist klar, wir lassen die Paketgröße mit dem Zusatz „:humaniec“ ausgeben. Das erzeugt die 211M oder 1024K , weil „211M:firefox“ besser lesbar ist für Menschen, also „221446243:firefox“. Nun ist aber 1024 größer als 211 🙂 Sort hat also alles richtig gemacht, aber die sortierten Informationen waren falsch vorbereitet.

So wird es besser

$ rpm -qa –queryformat „%{SIZE}:%{NAME}\n“ | sort -r -n | head -n 30
2104146639:0ad-data
573182723:supertuxkart-data
526795437:wesnoth-data
369138973:wine-debuginfo
346355557:linux-firmware
319608404:naev-data
250326767:wine-staging64
242843598:skypeforlinux
236453994:wine-core
221999655:wine-staging-common
221446243:firefox

Wenig überraschend toppt 0.AD alle anderen Pakete mit seinen 2GB an Grafikdaten 😉 Firefox mit seinen 211 M liegt abgeschlagen auf Platz 11 😉

Es geht auch etwas kürzer, aber nicht viel:

rpm -qa –qf „%{SIZE}:%{NAME}\n“ | sort -rn | head -30

Natürlich habe ich mal rumgeschaut, was zu dem Thema schon mal verfasst wurde. Leider war nichts besseres da, da es wohl schon das Optimum ist. Aber man kann natürlich immer noch was daraus lernen, z.b. wie man mit AWK Bytes in MB umrechnen kann :

rpm -qa –qf ‚%{SIZE} %{NAME}\n‘ | awk ‚{printf(„%sMb %s\n“, $1 / 1000 / 1000, $2)}‘ | sort -k1 -n

Dummerweise funktioniert das Sort in der Ausgabe nicht sauber, aber der AWK Teil ist nett 🙂
(Quelle für den AWK Part: list-all-packages-by-installed-size-bytes-on-rpm-distros )