AppArmor protection for your Apache (including mod_php, mod_python and others)

The biggest weak of Apache httpd web server is lack of security when using it in multiuser enviroment.
All httpd processes run under the same UID and GID which means that user JOE can create simple
php script which when run via httpd will be able to open and read other users web files (which means
that other users database passwords hidden somewhere in web configuration files are not protected).

There are some ways to protect your files:

  • FastCGI (mainly for PHP; allows to run php scripts under different privileges)
  • CGI (running PHP and other scripts through suexec)
  • nonstandard apache MPMs like peruser, metuxmpm (allow to run parts of httpd with different UID/GID; unfortunately very alpha quality)

The primary problem with all above is that performance drops dramaticly.

That’s where AppArmor comes to a rescue.
AppArmor is kind of SuSE response to SELinux. SELinux is preety good
when it comes to creating fine grained policy rules but tends to be quite complicated when it comes to writting policies for programs.
The AppArmor on the other hand is much simpler but also quite limited. AppArmor can be used to restrict file and capabilities(7)
access only. That’s enough for us – we want exactly that – limit access to parts of filesystem. 
The primary reason why we choose AppArmor over SELinux here is the ,,change hat” functionality.
It allows to define subpolicies for a program and the program is able to switch between subpolicies.
apache-mod_apparmor allows to switch subpolicy on per virtual host, directory and location basis!

How to do that?

First you need to patch you kernel with apparmor patches (these are very small and non-intrusive);
most of AppArmor lives in separate directory in kernel tree: security/apparmor so it’s not conflicting
even with grsecurity patches. You will also need to download and build apparmor-parser, apparmor-profiles
and apparmor-utils packages. All these available on AppArmor Home Page.

AppArmor keeps policy files in /etc/apparmor.d/. These are simple text files, for example /etc/apparmor.d/usr.sbin.httpd.prefork policy:

# vim:syntax=apparmor
# Last Modified: Tue Dec 12 02:37:27 2006
#include
/usr/sbin/httpd.prefork flags=(complain) {
#include
#include
/usr/sbin/httpd.prefork mr,
capability setuid,
capability setgid,
capability kill,
capability dac_override,
capability dac_read_search,

/etc/httpd/apache.conf r,
/etc/httpd/conf.d r,
/etc/httpd/conf.d/* r,
/etc/httpd/ssl/* r,
/usr/lib{,64}/apache/*.so mr,
/etc/httpd/webapps.d r,
/etc/gai.conf r,
/etc/httpd/magic r,
/etc/mime.types r,
/usr/share/file/magic* r,
/etc/openssl/** r,

/var/log/httpd/** w,
/var/log/archive/httpd/* w,

/etc/php4/** r,
/usr/lib{,64}/php4/*.so mr,
/etc/php/** r,
/usr/lib{,64}/php/*.so mr,

/var/run/httpd.pid rw,
/var/run/httpd/** rw,

/proc/[0-9]*/attr/current rw,
/etc/snmp/** r,
/usr/share/snmp/** r,

/usr/share/perl5/** r,
/usr/lib{,64}/perl5/** r,
/usr/lib{,64}/perl5/**.so* mr,

/usr/X11R6/lib{,64}/lib*.so* mr,

^HANDLING_UNTRUSTED_INPUT flags=(complain) {
/home/services/httpd/** r,
/var/log/httpd/** w,
/var/log/archive/httpd/* w,
/home/users/**/.htaccess r,
}

^HAT_no_access flags=(complain) {
/home/services/httpd/** r,
/var/log/httpd/** w,
/var/log/archive/httpd/* w,
}

#include
}

r – read, w – write, ix – inherited policy on execution, * – simple globbing, ** – glob that also matches slash character (there is more of these of course – see man apparmor.d(5) for details).

HANDLING_UNTRUSTED_INPUT and HAT_no_access are HATs configuration (HAT is previously mentioned subpolicy). By default mod_apparmor runs in HANDLING_UNTRUSTED_INPUT hat. That hat can be changed from configuration file for example:

AADefaultHatName HAT_no_access


AADefaultHatName HAT_domain_org
[…]


AADefaultHatName HAT_other_domain_com
[…]

will cause that mod_apparmor sets appropriate hat on per virtual host basis (as mentioned earlier we can use AADefaultHatName in Location and Directory directives, too).

Now we need to sets policies for these new vhost hats, but first we will put common rules into single abstraction/httpd-hat file that later will be used in HAT policies:

#include

capability setuid,
capability setgid,

/proc/[0-9]*/mounts r,
/proc/filesystems r,

/home/services/httpd/** r,
/var/log/httpd/** w,
/var/log/archive/httpd/* w,

/usr/lib{,64}/perl5/** r,
/usr/lib{,64}/perl5/**.so* mr,

/etc/mysql/mysql-client.conf r,
/etc/services r,
/etc/protocols r,
/etc/nsswitch.conf r,
/etc/hosts r,
/etc/host.conf r,
/etc/resolv.conf r,
/etc/mtab r,
/etc/fstab r,
/etc/xml/* r,
/etc/fonts/** r,

/usr/share/** r,

/var/cache/fontconfig/* r,
/var/run/php r,
/var/run/php/** rw,
/var/run/nscd/socket rw,
/tmp r,
/tmp/** rwl,

/bin/* ixr,
/usr/bin/* ixr,

/usr/lib{,64}/lib*.so* mr,
/usr/X11R6/lib{,64}/lib*.so* mr,

/usr/lib{,64}/ImageMagick-** r,
/usr/lib{,64}/ImageMagick-**.so* mr,

and finally HAT policies in abstractions/httpd-users:

^HAT_domain_org {
#include
/home/users/web-pages/domain_org rw,
/home/users/web-pages/domain_org/** rw,
/home/users/web-pages/domain_org/cgi-bin/** ixrw,
}

^HAT_other_domain_com {
#include
/home/users/web-pages/other_domain_com rw,
/home/users/web-pages/other_domain_com/** rw,
/home/users/web-pages/other_domain_com/cgi-bin/** ixrw,
}

That’s all. We load policy using rcapparmor init script (/etc/rc.d/init.d/apparmor in PLD/Linux). We can put profile into complain mode (everything is logged but no restriction is in effect) or in enforce mode (apparmor will enforce profile and log rejects). Example /var/log/audit/audit.log:

type=UNKNOWN[1500] msg=audit(1166014976.862:130983): REJECTING r access to /var/cache/fontconfig/2ee5dd3f6641dbe23533346fa3fce51a-x86-64.cache-2 (convert(13663) pro
file /usr/sbin/httpd.prefork active HAT_domain_org)
type=UNKNOWN[1500] msg=audit(1166015781.907:130984): REJECTING r access to /etc/fonts/conf.avail/20-fix-globaladvance.conf (convert(17219) profile /usr/sbin/httpd.p
refork active HAT_domain_org)
type=UNKNOWN[1500] msg=audit(1166015781.907:130985): REJECTING r access to /etc/fonts/conf.avail/20-lohit-gujarati.conf (convert(17219) profile /usr/sbin/httpd.pref
ork active HAT_domain_org)
[…]
type=UNKNOWN[1500] msg=audit(1166016116.536:131037): REJECTING r access to /var/cache/fontconfig/2ee5dd3f6641dbe23533346fa3fce51a-x86-64.cache-2 (convert(17596) pro
file /usr/sbin/httpd.prefork active HAT_other_domain_com)
type=UNKNOWN[1500] msg=audit(1166016139.393:131038): REJECTING r access to /var/tmp (httpd.prefork(7442) profile /usr/sbin/httpd.prefork active HAT_other_domain_com)
type=UNKNOWN[1500] msg=audit(1166016139.405:131039): REJECTING r access to /var/tmp (httpd.prefork(7442) profile /usr/sbin/httpd.prefork active HAT_other_domain_com)
type=UNKNOWN[1500] msg=audit(1166016139.429:131040): REJECTING r access to /var/tmp (httpd.prefork(7442) profile /usr/sbin/httpd.prefork active HAT_other_domain_com)

This log file is very usefull when creating policy (of course apparmor provides some tools that will create policy for you by parsing log file but I was doing everything manually with vim in one hand and tail in second).

Note that .htaccess checking is done at HANDLING_UNTRUSTED_INPUT level, before vhost HAT is applied.

ps. you will probably need to pass capability.disable=1 selinux=off when booting kernel. Otherwise apparmor won’t even load.

Jabra BT-500 Bluetooth Headset and Linux

BT500 works quite well when paired with Linux. To get the thing working you will need BlueZ stack (already in recent kernels), utilities (bluez-utils.spec) and ALSA Bluetooth driver – snd-bt-sco. There is one issue with BT500 (where you will hear no sound, just single beep at start and end of audio stream) which is handled by sco-mtu patch.

After setting headset in paring mode issue:

[root@tarm ~]# hcitool inq
Inquiring …
00:07:A4:BE:95:EE clock offset: 0x51ed class: 0x200404

Then pair with bt500:

[root@tarm ~]# hcitool cc 00:07:A4:BE:95:EE
[root@tarm ~]#

you will be asked for PIN code (enter: 0000).

[root@tarm ~]# btsco -v 00:07:A4:BE:95:EE
btsco v0.41
Device is 1:0
Voice setting: 0x0060
RFCOMM channel 1 connected
recieved AT*GNMK

From now one there should be second ALSA card available:

[root@tarm ~]# cat /proc/asound/cards
0 [Intel ]: HDA-Intel – HDA Intel
HDA Intel at 0xb0000000 irq 169
1 [Headset ]: Bluetooth SCO – BT Headset
BT Headset 1

Run your favorite application (twinkle perhaps – very nice SIP phone with G.711 support, ideal for connecting to Asterisk PBX) and choose BT Headset in sound setup (available also trough OSS emulation as /dev/dsp1 in my case).

You can view setting using alsamixer or amixer using -c 1 switch:

[root@tarm ~]# amixer -c 1
Simple mixer control ‘Master’,0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 – 15
Mono: 0 [0%]
Simple mixer control ‘Mic’,0
Capabilities: volume volume-joined
Playback channels: Mono
Capture channels: Mono
Limits: 0 – 15
Mono: 0 [0%]
Simple mixer control ‘AGC’,0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]
Simple mixer control ‘Loopback’,0
Capabilities: pswitch pswitch-joined
Playback channels: Mono
Mono: Playback [off]

When pressing buttons on headset the commands are sent to Bluetooth dongle and are visible by btsco program. You can put your own script in .btscorc which will be executed when commands arrive. By this method you can for example connect hang up button on headset with skype disconnect button (through skype DBUS API).

Suspend to RAM on IBM ThinkPad Z60m with SATA drive

Suspend on this laptop works quite well beside one important thing – SATA driver (ata_piix). It doesn’t resume properly, scsi subsystem timeouts, filesystem reports errors and the whole fs is remounted read only.

Recently Hugh Dickins found a way to make resume reliable:
http://lkml.org/lkml/2006/4/21/303

Apply it to latest kernel (2.6.17rc2 in my case) and use suspend userspace tool s2ram.

It isn’t best way (patch violates layers in libata driver) but that’s all. It’s usable until clean solution is found.

Fingerprint reader in ThinkPad Z60m

Various IBM/Lenovo ThinkPad notebooks contain interesting feature – fingerprint reader. It is possible to use it under Linux for authentication. Thanks to PAM module we can easily use fingerprint reader to biometrically authenticate various apps.

Installation

To get fingerprint reader working under Linux (PLD/Linux in this case) we need:

Usage

Sample programs from BioAPI implementation will allow you to verify if fingerprint reader works correctly and will allow to dump your fingerprints data into *.bin files. *.bin files are later used to verify if fingerprint is correct one.

Example PAM configuration for sudo with authentication done through BioAPI:

[root@tarm ~]# cat /etc/pam.d/sudo
#%PAM-1.0
auth sufficient pam_bioapi.so {5550454b-2054-464d-2f45-535320425350} /etc/bioapi/pam/
auth required pam_unix.so
account required pam_unix.so
password required pam_cracklib.so difok=2 minlen=8 dcredit=2 ocredit=2 retry=3
password sufficient pam_bioapi.so {5550454b-2054-464d-2f45-535320425350} /etc/bioapi/pam/
password required pam_unix.so md5 shadow use_authtok
password required pam_make.so /var/db
session required pam_unix.so
session required pam_env.so
session required pam_limits.so change_uid

(/etc/bioapi/… contains *.bin files with scanned fingerprints)

How it works?

[arekm@tarm ~]$ sudo zsh
Verification start
Put finger

When the finger was moved too fast:

Swipe too fast
Put finger

When the scan had bad quality (I just moved my finger quite fast and not in correct way):

Bad quality
Put finger

The finger was passed over right side of sensor only:

Finger is too right
Put finger

Correct scan, finger passed properly:

Image processing
Scanned good image
Operation succeeded
[root@tarm ~]#

Example of unlocking KDE lock screen:

Unlocking KDE lock screen with fingerprint

Interesting links:

ThinWiki article about ThinkPad FingerPrint reader.

2007-01-18 UPDATE:
There is open source implementation that is able to use fingerprint device in thinkpads:
ThinkFinger

PLD GNU/Linux

PLD GNU/Linux – Polish Linux Distribution

Arkadiusz Miśkiewicz$Id: pld-art.lyx,v 1.1 2000/03/12 21:54:23 misiek Exp $

1 Odrobina historii.

W 1998 roku niewielka grupa osób postanowiła stworzyć pierwszą polską dystrybucję Linuxa. Projekt przyjął nazwę Polish Linux Distribution (PLD GNU/Linux). W wyniku wielu dyskusji postanowiono równocześnie prowadzić dwie linie rozwoju dystrybucji. Pierwszą określaną mianem ,,stable” (przeznaczoną dla szerokiego odbiorcy) oraz drugą ,,devel” (jak nazwa wskazywała przeznaczoną dla wąskiego grona developerów oraz administratorów). Rok później obie linie rozwojowe zostały połączone w jedną całość (rozwijaną do dziś). Zaprzestano używania nazw stable oraz devel.


2 Co na pokładzie ?

Podstawowymi cechami charakteryzującymi PLD GNU/Linuxa sa:

* IPv6. PLD GNU/Linux zawiera kompletny zestaw pakietów umożliwiający pracę w sieciach komputerowych wykorzystujących protokół IPv6 nazywany również IPng – Internet Protocol Next Generation (Protokół Internetowy
Następnej Generacji). Zainteresowanych protokołem IPv6 odsyłam na strony: http://www.6bone.pl/ (Polish 6bone Network) oraz http://www.ipv6.org/.

* Kerberos 5. Wsparcie dla systemu autentykacji Kerberos rozwijanej w europie (oczywiście wspierającej IPv6).

* OpenSSL. Dzięki OpenSSL użytkownik może w sposób bezpieczny łączyć się ze stronami www, odbierać pocztę i wiele innych. Dostarczamy szereg programów wykorzystujących możliwości SSL np. apache (serwer www), lynx (przeglądarka www), postfix, zmailer (agenci transferu poczty), fetchmail (program do ściągania poczty), stunnel (bezpieczne tunelowanie) i wiele innych.

* OpenSSH. Ze względów bezpieczeństwa podstawowym programem do zdalnej pracy w PLD jest SSH w wersji ,,free” rozwijanej przez OpenBSD Team.

* PAM. Oferujemy własną wersję modułowego systemu autentyfikacji PAM (znacznie rozbudowaną w stosunku do oryginału), a rozwijaną przez jednego z członków Zespołu PLD.

* Wsparcie dla rozwojowych jąder i programów. Ze względu na fakt iż kilku naszych developerów lubuje się w nowinkach dostarczanych przez Linusa i społeczność Linuxową – w PLD można znaleść oprogramowanie umożliwiające wykorzystanie np. z wysoko wydajnego systemu plików z kroniką (RaiserFS), nowego systemu firewall/NAT (netfilter), znacznie ulepszonego bridge, modułu zarządzania woluminami (lvm) itp.

* Wolny wybór. Nie narzucamy użytkownikom określonych rozwiązań. Staramy się udostępnić szereg pakietów z których użytkownik wybierze sobie te, które najlepiej mu odpowiadają. Dlatego też dostarczamy np. kilka agentów transferu poczty (postfix, qmail, sendmail, zmailer, exim, …), różne serwery www (apache, boa, thttpd, …) czy kilka
serwerów typu inetd (inetd, rlinetd, xinetd) itd. Podobnie jest z zarządcami okien Xwindows. W PLD znaleść można np. zarówno KDE (właśnie aktualizowane do wersji beta2.0), GNOME jak i Xfce czy WindowMakera.

* Standaryzacja. Staramy się by PLD GNU/Linux odpowiadał współczesnym standardom takim jak: FHS 2.0 (File System Hierarchy 2.0), SUSv2 (Single Unix(R) Specification version 2).

* Język polski. Wszystkie pakiety rpm dostarczane przez Zespół PLD zawierają opisy w języku polskim. Do programów dołączamy tłumaczenia dostarczane przez GNU Translation Project (http://www.ceti.com.pl/~kravietz/gnu/gnu_tp.html) oraz strony podręcznika systemowego tłumaczone w ramach Projektu Tłumaczenia Manuali (http://ptm.linux.pl/).

* Inne.

Jak widać PLD GNU/Linux zawiera ogrom oprogramowania w możliwie najnowszych wersjach umożliwiającego wykorzystanie wszelkich cech jakie daje jądro Linuxa. Dystrybucja przeznaczona jest dla wszystkich zainteresowanych szczególnie administratorów i studentów ,,zapaleńców”. Osobiście jednak nie polecam PLD początkującym użytkownikom, choć docelowo dystrybucja ma być przyjazna nawet dla osób mających styczność z Linuxem po raz pierwszy.

3 Chcę pomoć.

Wszyscy chętni do pomocy są mile widziani. Wyjaśnię pokrótce zasady tworzenia pakietów w PLD. Wszyscy developerzy wykorzystują system kontroli wersji zasobów – CVS. Jest to bardzo proste i efektywne narzędzie
(polskie opisy obsługi CVSu znajdują się na naszej stronie www w dziale technikalia). Poprawienie, uaktualnienie jakiegoś programu sprowadza się do ściągnięcia pliku spec oraz źródeł danego programu z repozytorium CVS (można to zrobić wykorzystując skrypt ,,builder” znajdujący się w module SPECS repozytorium CVS), poprawieniu błędów, uzupełnieniu itp, a następnie potwierdzeniu (przesłaniu) poprawek do serwera CVS. Odpowiednie osoby przejrzą zmiany dokonane przez developera/ów i jeśli uznają, że pakiet rpm jest ukończony przebudują go i umieszczą na oficjalnym serwerze ftp. Wszelkie konsultacje dotyczące kształtu pakietów prowadzone są na liście pld-list, której adres podam na końcu artykułu. Przed zabraniem się do pracy nad określonym pakietem można zapytać się na liście czy inna osoba już się nim nie zajmuje. Aktualnie najważniejszą sprawą jest dokończenie pakietów bazowych (należą do nich m.in. dialog, basesystem, dev, dev86, getty_ps i kilka pomniejszych) oraz poprawienie wszelkich zauważonych błędów.

W tym momecie warto wspomnieć, że Zespół PLD udostępnia także autorom oprogramowania nie należącym do Zespołu PLD swoje zasoby (tj. serwer CVS, listy dyskusyjne (z archiwum i wyszukiwarkami), miejsce na serwerze ftp). Z takiej możliwości skorzystało już wiele projektów jak np. LinuxDOC (dokumentacja, http://www.linuxdoc.org/), xmps (świetny odtwarzacz wideo MPEG, http://www-eleves.enst-bretagne.fr/~chavarri/xmps/), irssi (klient irc), emma (obsługa finansów), minicom (terminal, http://www.clinet.fi/~walker/minicom.html), GNU TP Polska. Jeśli tworzysz jakiś interesujący program – daj nam znać.

4 Pytania.

Postaram się odpowiedzieć na kilka typowych pytań dotyczących PLD:

* kiedy PLD będzie gotowe ?
Tego naprawdę nikt nie wie. Problem z powolnym rozwojem dystrybucji wynika głównie z faktu, że zajmują się nim osoby mające przede wszystkim inne obowiązki przez co nie mają czasu na skoncentrowanie się wyłącznie na PLD. Jeśli potrafisz i chcesz pomóc – przyłącz się. Skończymy dystrybucję szybciej.

* dlaczego planowany instalator będzie działał w trybie tekstowym, a nie graficznym ?
Jest na to bardzo prosta odpowiedź: po prostu tekstowy jest wygodniejszy ! Sam Xserwer wymagany w przypadku instalatora graficznego pożera sporo zasobów systemowych, a przecież nie każdy ma maszynę z 64MB ramu i kilkuset megahercowym procesorem. Ponadto instalator graficzny jest bardzo niewygodny w przypadku instalowania systemu przy użyciu tzw. kickstartu czyli automatyczniej instalacji na podstawie uprzednio przygotowanej konfiguracji. Niemniej jednak nie wykluczamy możliwości powstania graficznego instalatora jako drugiego, równożędnego.

* nie ma jeszcze instalatora więc jak zainstalować PLD ?
Możliwości jest kilka. Najprościej dla początkujących będzie po prostu ręcznie zaktualizować inną dystrybucję (RH, SuSE, Mandrake) do PLD GNU/Linuxa. Sposób ten jest prosty aczkolwiek niezbyt wygodny. Jako pierwsze należy zaktualizować następujące pakiety: filesystem, setup, glibc, rpm. Pozostałe pakiety nie są krytyczne i można je aktualizować stopniowo. Warto w tym momencie pamiętać o kilku opcjach rpm’a przydatnych podczas instalacji: –nodeps (nie będą sprawdzane zależności pomiędzy pakietami), –force (wymusza instalację pakietu nawet gdy pakiet
został już poprzednio zainstalowany), –oldpackage (zainstaluj pakiet nawet gdy w systemie już została zaistalowana starsza wersja pakietu). Na szczególną uwagę należy zwrócić na pakiet rc-scripts (skrypty startowe w PLD). By móc używać rc-scripts należy koniecznie wkompilować w jądro opcję “Kernel/User netlink socket” oraz zainstalować pakiet
iproute2. Można również udać się do znajomego, który już PLD posiada zainstalowane i po prostu przegrać zainstalowaną dystrybucję na własny dysk. Postaramy się wkrótce przygotować alternatywny sposób instalacji nie wymagający innych dystrybucji ani znajomych z zainstalowanym PLD (w formie dyskietki instalacyjnej – http://cvsweb.pld.org.pl/index.cgi/bootdisk/).

* masz jakieś pytania ?
Uruchom przeglądarkę www, załaduj stronę http://faq.pld.org.pl/ i zapytaj. Z pewnością Ci odpowiemy.

5 Na koniec.

Mimo iż dystrybucja PLD jest nadal w rozwoju warto się nią zainteresować. Zapraszamy zarówno do używania jak i tworzenia. Czekamy pod adresem: pld-list@pld.org.pl na pytania oraz sugestie.

6 Co i gdzie ?

* http://www.pld.org.pl/ – podstawowe informacje o PLD. Warto zainteresować się działem technikalia, w którym wyjaśniono sposób pracy z CVS, budowanie pakietów rpm, podstawy IPv6.

* http://www.ipv6.pld.org.pl/ – (dostęp tylko poprzez protokół IPv6).

* http://lists.pld.org.pl/ – archiwa i wyszukiwarka list dyskusyjnych.

* http://faq.pld.org.pl/ – Najczęściej Zadawane Pytania (z możliwością zadawania i odpowiadania na już zadane pytania).

* http://bugs.pld.org.pl/ – baza błędów dostrzeżonych w PLD GNU/Linuxie

* http://cvsweb.pld.org.pl/ – interfejs www do serwera CVS.

* ftp://ftp.pld.org.pl/ – główny serwer FTP (TASK Gdańsk)

* cvs.pld.org.pl – główny serwer CVS (dostępny tylko dla developerów)

* anoncvs.pld.org.pl – serwer CVS dla każdego (dostęp tylko do odczytu)

Listy dyskusyjne w domenie @pld.org.pl:

* pld-list – główna lista (dyskusja o wszystkich aspektach związanych z PLD GNU/Linuxem)

* pld-rc-scripts – dyskusja dotycząca skryptów startowych

* pld-installer – dyskusja dotycząca instalatora

* pld-www – sprawy związane ze stronami www PLD

* pld-cvs-commit – na tą listę automatycznie wysyłane są informacje
o zmianach jakie zaszły w repozytorium CVS.

Na listę można zapisać się wysyłając pusty list na adres: nazwalisty-subscribe@pld.org.pl Osoby niezaznajomione z obsługą listserwerów proszone są o wysłanie pustego listu na adres nazwalisty-help@pld.org.pl.

Tekst (C) 2000 Wydawnictwo Software. Wydrukowany w Linux+.