2012-11-18

RPM-paket för BankID för 32- och 64-bitars Fedora 17

This is another post about the Swedish digital signing and identification program BankID. Its written in Swedish.

Inledning

Det här är en fortsättning på det tidigare inlägget om BankID. Nu är det dags att paketera BankID i en RPM för enkel installation/uppgradering/mm.

Paketeringen utgår från innehållet i den officiella filen, bisp-4.19.1.11663.tar.gz, men lägger filerna där de skall i Fedora. Paketeringen har också effekten att alla beroenden till andra bibliotek löses och kommer installeras automatiskt när man installerar RPM:en.

RPM:en kan installeras och användas på både 32- och 64-bitars Fedora 17.


Sätt upp byggmiljön för RPM

För att kunna bygga RPM-paket under Fedora så behöver man först installera paktet rpmdevtools:
 $ sudo yum install rpmdevtools
Nästa steg är att köra ett program för att sätta upp katalogstrukturen för RPM-byggena:
 $ rpmdev-setuptree
rpmdev-setuptree skapar katalogen ~/rpmbuild och ett antal kataloger därunder.

Nu är det dags att förbereda RPM-bygget.


Förbered bygget

Ladda ner den officiella tarbollen härifrån (klicka på Ubuntu/Linux): https://install.bankid.com/Download/All och lägg filen i katalogen ~/rpmbuild/SOURCES.

Man behöver även en så kallad spec-fil som beskriver hur RPM:en skall byggas. Nedan finns en sådan spec-fil för BankID. Spar innehållet och lägg det i filen ~/rpmbuild/SPECS/bisp.spec (ersätt versionsnumret på andra raden med det som gäller för den nedtankade filen):
Name:           bisp
Version:        4.19.1.11663
Release:        1%{?dist}
Summary:        BankID Security Application for Linux
Group:          Applications/Internet
License:        Other
URL:            https://install.bankid.com/Download/All
Source0:        https://install.bankid.com/Repository/BISP-%{version}.tar.gz
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Requires:       mozilla-filesystem
BuildRequires:  /usr/bin/hexdump /usr/bin/xxd

# Put "fake" version numbers on the .so files to shut rpmlint up
Provides:      libai.so = %{version}, libbisp.so = %{version}, libtokenapi.so = %{version}  

# Enable possibility to override the default dependency checking
%define _use_internal_dependency_generator 0

# Don't use the build in "provides" since it will "provide" the plugins in /usr/lib/personal
%define __find_provides %{nil}

# We still want to have requirements automatically figured out
%define __find_requires /usr/lib/rpm/find-requires

# Do not strip the binaries
%define __os_install_post %{nil}


%description
BankID Security Application for Linux is a set of programs and a Firefox plugin
used for digital authentication and signing in bank environments.


%prep
%setup -q -n BISP-%{version}


%build
# BankID normally installs in /usr/local and have hard coded paths in the
# binaries that point out files under /usr/local.
#
# When packaging RPMs, you should not put files in /usr/local since that
# location is for locally installed files and not stuff that comes in
# RPM format.
#
# The script below will replace /usr/local with /usr in the files needed
# to be able to package BIPS "correctly".

# Change /usr/local to /usr in scripts and desktop files
sed -i 's/\/usr\/local/\/usr/g' personal.desktop
sed -i 's/\/usr\/local/\/usr/g' personal.sh
sed -i 's/\/usr\/local/\/usr/g' persadm.sh

# Function to replace strings in binary files
function patch_strings_in_file() {
    local FILE="$1"
    local PATTERN="$2"
    local REPLACEMENT="$3"

    # Find all unique strings in FILE that contain the pattern 
    STRINGS=$(strings ${FILE} | grep ${PATTERN} | sort -u -r)

    if [ "${STRINGS}" != "" ] ; then
        echo "File '${FILE}' contain strings with '${PATTERN}' in them:"

        for OLD_STRING in ${STRINGS} ; do
            # Create the new string with a simple bash-replacement
            NEW_STRING=${OLD_STRING//${PATTERN}/${REPLACEMENT}}

            # Create null terminated ASCII HEX representations of the strings
            OLD_STRING_HEX="$(echo -n ${OLD_STRING} | xxd -g 0 -u -ps -c 256)00"
            NEW_STRING_HEX="$(echo -n ${NEW_STRING} | xxd -g 0 -u -ps -c 256)00"

            if [ ${#NEW_STRING_HEX} -le ${#OLD_STRING_HEX} ] ; then
                # Pad the replacement string with null terminations so the
                # length matches the original string
                while [ ${#NEW_STRING_HEX} -lt ${#OLD_STRING_HEX} ] ; do
                    NEW_STRING_HEX="${NEW_STRING_HEX}00"
                done

                # Now, replace every occurrence of OLD_STRING with NEW_STRING 
                echo -n "Replacing ${OLD_STRING} with ${NEW_STRING}... "
                hexdump -ve '1/1 "%.2X"' ${FILE} | \
                sed "s/${OLD_STRING_HEX}/${NEW_STRING_HEX}/g" | \
                xxd -r -p > ${FILE}.tmp
                mv ${FILE}.tmp ${FILE}
                echo "Done!"
            else
                echo "New string '${NEW_STRING}' is longer than old" \
                     "string '${OLD_STRING}'. Skipping."
            fi
        done
    fi
}

# List binary files to patch
FILES=$(ls *.so personal.bin persadm)

OLD_LIB_BASE_PATH="/usr/local/lib/personal"
NEW_LIB_BASE_PATH="/usr/lib/personal"

OLD_BIN_BASE_PATH="/usr/local/bin"
NEW_BIN_BASE_PATH="/usr/bin"

# Replace /usr/local/lib/personal with /usr/lib/personal and
# /usr/local/bin with /usr/bin
for FILE in ${FILES} ; do
    patch_strings_in_file ${FILE} ${OLD_LIB_BASE_PATH} ${NEW_LIB_BASE_PATH}
    patch_strings_in_file ${FILE} ${OLD_BIN_BASE_PATH} ${NEW_BIN_BASE_PATH}
done


%install
rm -rf %{buildroot}

# Create install layout
install -m 755 -d %{buildroot}%{_bindir}
install -m 755 -d %{buildroot}%{_libdir}/personal
install -m 755 -d %{buildroot}%{_libdir}/personal/icons
install -m 755 -d %{buildroot}%{_libdir}/personal/config
install -m 755 -d %{buildroot}%{_libdir}/personal/lang
install -m 755 -d %{buildroot}%{_libdir}/mozilla/plugins
install -m 755 -d %{buildroot}%{_datadir}/applications
install -m 755 -d html

# Install files in the buildroot
# Install libplugins.so as libbisp.so to not conflict file wise with a manual install
install -m 755 libplugins.so %{buildroot}%{_libdir}/mozilla/plugins/libbisp.so
install -m 755 libai.so libtokenapi.so %{buildroot}%{_libdir}
install -m 755 libBranding.so libCardEdb.so libCardGTOClsc.so libCardOberthur.so \
               libCardPrisma.so libCardSetec.so libCardSiemens.so libP11.so persadm \
               personal.bin %{buildroot}%{_libdir}/personal
install -m 755 persadm.sh %{buildroot}%{_bindir}/persadm
install -m 755 personal.sh %{buildroot}%{_bindir}/personal
# Install personal.desktop as bisp-personal.desktop to not conflict whith a manual install
install -m 644 personal.desktop %{buildroot}%{_datadir}/applications/bisp-personal.desktop
install -m 644 nexus_logo_32x32.png %{buildroot}%{_libdir}/personal/icons
install -m 644 BankID_Security_Application_Help* html
install -m 644 Personal.cfg %{buildroot}%{_libdir}/personal/config
chmod 644 BankIDUbuntu_ReadMe*
for D in $(ls ../lang) ; do
    install -m 755 -d %{buildroot}%{_libdir}/personal/lang/${D}
    install -m 644 ../lang/${D}/*.mo %{buildroot}%{_libdir}/personal/lang/${D}
done


%post
/sbin/ldconfig


%postun
/sbin/ldconfig


%files
%defattr(-,root,root,-)
%doc Release.txt html BankIDUbuntu_ReadMe*
%dir %{_libdir}/personal
%{_libdir}/personal/*
%{_libdir}/*.so
%{_libdir}/mozilla/plugins/*.so
%{_bindir}/*
%{_datadir}/applications/*.desktop


%changelog
* Sun Nov 18 2012 Name - 4.19.1.11663-1
- Initial release
Under %build-sektionen så görs en del fixar i filerna som utgör BankID. Det som sker är att sökvägen /usr/local byts ut mot /usr på valda ställen. När man bygger "riktiga" RPM:er så bör filer installeras på sina korrekta platser och det ser spec-filen till. Filen libplugins.so installeras dessutom som libbisp.so och filen personal.desktop installeras som bisp-personal.desktop. Det här är för att inte krocka filnamnsmässigt med en installation gjord med det "officiella" installationsscriptet. Filnamnsbytena påverkar inte funktionen.

Förberedelsen är nu klar. Dags att bygga RPM:en.


Bygg RPM:en

Med förberedelserna enligt ovan så är byggsteget enkelt. Kör bara:
 $ rpmbuild -bb --target i386 ~/rpmbuild/SPEC/bisp.spec
Om allt går bra så har du nu fått en RPM: ~/rpmbuild/RPMS/i386/bisp-4.19.1.11663-1.fc17.i386.rpm.

Genom att ange växlen --target i386 så kan man bygga RPM:en i både 32- och 64-bitars Fedora och ändå få en 32-bitars RPM. Eftersom BankID är ett 32-bitars program så är detta vad vi vill ha.


Installera RPM:en

Nu när RPM:en är klar så installeras den enligt följande:
 $ sudo yum install ~/rpmbuild/RPMS/i386/bisp-4.19.1.11663-1.fc17.i386
Alla beroenden som krävs kommer nu installeras automatiskt.

Om du kör 64-bitars Fedora så behöver du även installera nspluginwrapper.i686, nspluginwrapper.x86_64 och eventuellt oxygen-gtk2.i686 (om du kör KDE) för att det skall fungera. Det här är samma beroenden som beskrevs i det tidigare inlägget om BankID.


Tag bort det originalinstallerade BankID

Om du redan har BankID installerat enligt det tidigare inlägget så måste du nu avinstallera det. RPM:en som du byggt krockar inte filmässigt med en manuell installation, men du kan möjligen få problem när båda är installerade samtidigt.

Alltså, kör följande för att avinstallera en installation gjord från den officiella "tarbollen" (byt ur versionsnumret till det som gäller för din installation):
 $ sudo /usr/local/lib/personal/install.4.19.1.11663.sh u
 $ sudo rm -f /usr/lib/mozilla/plugins/libplugins.so
Glöm inte bort att starta om Firefox efter installationen!

Om du är riktigt petig så tar du även bort raden /usr/local/lib från filen /etc/ld.so.conf. Raden läggs till av det officiella installationsscriptet men tas inte bort vid en avinstallation.


Sammanfattning

Efter att ha installerat BankID RPM:en så använder du BankID på samma sätt som tidigare. Samma förutsättningar gäller som om du hade installerat BankID med hjälp av det officiella scriptet, dvs du behöver starta personal innan du surfar till en sida som kräver BankID. Se det föregående inlägget för alla detaljer.

2012-11-04

Patch strings in binary files with sed

So, you have a binary file that you need to patch. Perhaps it is a pre compiled proprietary program or dynamic library that contains hard coded paths (text strings) that you need to change.

If the file had been a text file, then sed would probably come to your rescue. For binary files there are hex editors available, but they require manual handling and can't be scripted. Other binary patch programs are out there as well but might not be packaged in your favorite distribution and compiling things from source is boring. You could also have the need to do the patching in a packaging stage when building say an RPM.

So, how can you use sed then?

Well, it's quite simple. Just convert the binary file to ASCII HEX with hexdump, patch it with sed and the convert it back to binary with xxd:
hexdump -ve '1/1 "%.2X"' file.bin | \
sed "s/<pattern>/<replacement>/g" | \
xxd -r -p > file.bin.patched
Of course there are caveats to this approach. The most significant one is that you can't replace a string with a string that is longer then the original one. Shorter is OK though. Another one is that the strings must be null terminated, but this is almost always the case. You also have to create <pattern> and <replacement> yourself as the ASCII HEX representations of the null terminated strings with their null terminator present. Further, <replacement> must be padded to the same length as <pattern>.

Lets take a concrete example:

You have a binary named foo that uses some plugins. The plugins are assumed to be in /usr/local/lib/foo and this path is hard coded in foo. You want to package foo with its plugins using your distributions packaging system and use the file system layout that your distribution uses.

So you will put foo in /usr/bin and put all the plugins in /usr/lib/foo. But foo would look in /usr/local/lib/foo when trying to load plugins and will therefore fail.

But, if we could replace all occurrences of /usr/local/lib/foo in foo with /usr/lib/foo everything shold work. Since the length of /usr/lib/foo is shorter then /usr/local/lib/foo this is doable.

Here is an example script that does the replacement:
#!/bin/bash

function patch_strings_in_file() {
    local FILE="$1"
    local PATTERN="$2"
    local REPLACEMENT="$3"

    # Find all unique strings in FILE that contain the pattern 
    STRINGS=$(strings ${FILE} | grep ${PATTERN} | sort -u -r)

    if [ "${STRINGS}" != "" ] ; then
        echo "File '${FILE}' contain strings with '${PATTERN}' in them:"

        for OLD_STRING in ${STRINGS} ; do
            # Create the new string with a simple bash-replacement
            NEW_STRING=${OLD_STRING//${PATTERN}/${REPLACEMENT}}

            # Create null terminated ASCII HEX representations of the strings
            OLD_STRING_HEX="$(echo -n ${OLD_STRING} | xxd -g 0 -u -ps -c 256)00"
            NEW_STRING_HEX="$(echo -n ${NEW_STRING} | xxd -g 0 -u -ps -c 256)00"

            if [ ${#NEW_STRING_HEX} -le ${#OLD_STRING_HEX} ] ; then
                # Pad the replacement string with null terminations so the
                # length matches the original string
                while [ ${#NEW_STRING_HEX} -lt ${#OLD_STRING_HEX} ] ; do
                    NEW_STRING_HEX="${NEW_STRING_HEX}00"
                done

                # Now, replace every occurrence of OLD_STRING with NEW_STRING 
                echo -n "Replacing ${OLD_STRING} with ${NEW_STRING}... "
                hexdump -ve '1/1 "%.2X"' ${FILE} | \
                sed "s/${OLD_STRING_HEX}/${NEW_STRING_HEX}/g" | \
                xxd -r -p > ${FILE}.tmp
                chmod --reference ${FILE} ${FILE}.tmp
                mv ${FILE}.tmp ${FILE}
                echo "Done!"
            else
                echo "New string '${NEW_STRING}' is longer than old" \
                     "string '${OLD_STRING}'. Skipping."
            fi
        done
    fi
}

patch_strings_in_file foo "/usr/local/lib/foo" "/usr/lib/foo"
Please note that this way of replacing strings does not allow for full blown regexp usage. You could probably modify the script to be more versatile if you have more complex needs. 

As always, watch out when you patch binary files. There could be circumstances when a replacement as described above will break the binary. Test, test, test and test some more...

PrettyPrint