от Sudo(7-06-2004)
рейтинг (15)
[ добре ]
[ зле ]
Вариант за отпечатване
Реших да напиша тази статия по повод неотдавнашна дискусия
водена в www.linux-bg.org за това как да пуснем през
Apache-то скрипт който да изпълнява административни функции,
разбирай изисква superuser права.
Е имаше и друг повод, трябваше ми и на мен подобно
решение:) Трябваше да предоствя WEB интерфейс за
спиране/пускане на Internet достъп към дадена мрежа, а това
аз мога да направя само с iptables.
Разбира се удобството на UNIX/Linux е в това че можем да
решим задачата по няколко начина "thats why I love my
Linux". Това решение което представям не претендира да
е най-доброто, нито най-лесното, нито дори най-сигурното,
винги когато става въпрос за ескалация на привилегии трябва
много ама много да се внимава какво всъщност правим или
както често четем по разни лицензи, readme-та и т.н. USE IT
ON YOUR OWN RISK !!!
А сега към условието на задачата:
1. Имам скрипт (писан на Perl) който генерира прост HTML
вход/изход където потребителя пуска и спира Internet
достъпа.
2. Имам bash-скрипт който реално върши това с помоща на
iptables, скрипта приема един параметър [start|stop].
3. И естествено най-интересната част от задачата, как така
от Apache което е стартирано с правата на потребител nobody
UID=99 (Slackware 9.1) да получим права UID=0, нужни за
iptables.
Първото нещо за което се сетих беше естествено suid бита на
изпълнимите файлове (Perl скрипта или bash скрипта). Е за
suid-нат Perl скрипт ми трябва и suidperl, а такъв
стандартно в Slack-a нямам. Остана suid на bash скрипта,
chmod 4755 my_buggy_script, изпълнявам и ... да ама НЕ :(
Естествено СЛЕД като настъпах мотиката няколко пъти се
зарових в документация, чичко Гугъл разпитвах и стигнах до
някои интересни неща:
1. bash v.2 и нагоре не търпи suid bit, дропва привилегиите
и това е. Тук една малка скоба при Debian не било точно така
ами не знам как си, ама сега пък цял Debian да слагам заради
един бит пък бил той и suid ... :)
2. Можело да си компилирам Apache-то с поддръжка на suexec,
хубаво ама и там едни страшни работи пише ...
3. И разбира се най-хубавото: не съм първия който се
сблъсква с подобен проблем.
Е стигнахме и до решението.
Между Perl скрипта и bash скрипта се слага едно междинно
ниво - парсер (малка C програма) която да ескалира
привилегиите до нужното ни нам UID=0 така щото bash-a да си
мисли че root го стартира.
Ето я и самата програма, нищо сложно в нея: дефинираме си
вътре скрипта който искаме да се изпълни, вдигаме
привилегиите и изпълняваме скрипта с параметрите които Perl
скрипта е подал.
----- my_suexec.c -----
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int main(int argc, char ** argv) {
char *action =
"/usr/local/bin/my_buggy_script";
if (argc != 2) return 0;
setuid(0); // Here we go as
root
if ( strcmp(argv[1],
"start") == 0 ) {
execv(action, argv);
}
else if ( strcmp(argv[1],
"stop") == 0 ) {
execv(action, argv);
}
else {
printf("%s \n", "Bad
params!");
}
return 0;
}
-----end my_suexec.c -----
компилираме: gcc -s -o my_suexec my_suexec.c
cp my_suexec /usr/local/sbin
Тук много важно е да направим:
chmod 4755 my_suexec
иначе горното "setuid(0)" няма от къде да си
вземе правата, и сме готови.
Естествено някой може да каже "Какво всъщност правиш
ти? Това което хората са написали на 600+ реда с всичките му
там проверки ти си написал на 5 реда?" Да така е с една
малка подробност командата която ще се изпълни е hard-copy
вътре в програмката, а не се приема като параметър. Друг ще
каже: да ама по сигурно е "execv(action,NULL)" да
така е, но отдолу скрипта трябва да е интелигентен за да
знае start или stop да пусне. Изобщо, това както казах НЕ Е
универсалното решение на проблема с ескалацията на
привилегиите, това е само отправна точка за тези от вас
които имат нужда от нещо подобно.
Ето и цялата постановка за тези които ги интересува:
---status.cgi---
#!/usr/bin/perl
use CGI;
$q = new CGI;
$Action = $q->param('Action'); # v promenlivata
Action vliza POST-a ot formata na html-a (start/stop)
if ( $Action eq
"Start" ) {
`/usr/local/sbin/my_suexec start`;
}
elsif ( $Action eq
"Stop" ) {
`/usr/local/sbin/my_suexec stop`;
}
else {
print "Bad params!\n";
}
...
Малко HTML тук
...
---end status.cgi---
Програмата описана по-горе...
---my_buggy_script---
inet_start() {
# Create dummy file showing that Inet is ON
if [ ! -f /tmp/inet_dummy ] ; then
echo 1 > /tmp/inet_dummy
fi
/usr/sbin/iptables -t nat -A POSTROUTING -s
localnet/24 -o $EXTIF -j MASQUERADE
/usr/sbin/iptables -t nat -A PREROUTING -s
localnet/24 -p tcp --dport 80 -d any/0 -j REDIRECT
--to-ports 8080
}
inet_stop() {
# Delete dummy file showing that Inet is OFF
if [ -f /tmp/inet_dummy ] ; then
rm /tmp/inet_dummy
fi
/usr/sbin/iptables -t nat -I PREROUTING -s
localnet/24 -p tcp --dport 80 -d any/0 -j REDIRECT
--to-ports 9090 #ie > /dev/null
/usr/sbin/iptables -t nat -I POSTROUTING -s
localnet/24 -o $EXTIF -j DROP
}
case "$1" in
'start')
inet_start
;;
'stop')
inet_stop
;;
*)
echo "usage $0 start|stop"
esac
---end my_buggy_script---
P.S. Днес едни такива "глобални" проблеми се
обсъждат в сайта така че се опасявам и аз да не отнеса нещо
силно емоционално, но ако имате предложения, подобрения или
въобще нещо свързано с темата на статията, моля заповядайте,
предполагам че все някой може да има някаква полза от това
писание :)
<< Пример за употреба на Access Control Lists с Линукс | Slackware ядро + ALSA >>
|