pam


<>
<>
Piszemy własny moduł
PAM
Andrzej Nowak
rtykuł jest skierowany przede wszystkim pliku konfiguracyjnego ani stosu modułów. Obecnie
do administratorów systemów linuksowych, jego zachowanie można kontrolować poprzez odpo-
Aktórzy chcą wprowadzić własne zmiany do wiednie wpisy w /etc/pam.conf. Z innych znanych
swoich systemów zabezpieczeń, ale również do uniksów PAM występuje np. w HP-UX (pojawił się
tych, którzy chcą dowiedzieć się czegoś o PAM w wersji 11). W tym tekście będę koncentrował
od kuchni. się na tzw. Linux-PAM, czyli implementacji PAM
Umiejętność pisania modułów PAM jest bardzo dla Linuksa  pisząc PAM będę miał na myśli
przydatna, gdy tworzymy niestandardowy lub nietypowy Linux-PAM.
system uwierzytelniania. Ponadto przekazywana tu
wiedza z pewnością przyda się przy modyfikacjach Ogólne założenia
konfiguracji PAM wykraczających poza tematy zawarte Ogromna ilość aplikacji i usług uwzględnia i wyko-
na ubogich nieraz stronach manuali. Analiza kodu rzystuje istnienie PAM  są to tzw. pam-aware
zródłowego modułu może często wyjaśnić wiele applications  dzięki czemu zarządzanie dostępem
Na CD:
pozornych nieprawidłowości w jego funkcjonowaniu do nich staje się relatywnie łatwe. Nie potrzeba
albo nietypowych zachowań. ponownej kompilacji programu, aby w dowolny
Na dołączonej do
pisma płycie CD
Przedstawiam wprowadzenie do API systemu sposób zmienić tryb dostępu do niego. Oto
zamieszczone są wyko-
PAM na podstawowym poziomie, ale artykuł wybrane podstawowe założenia systemu PAM,
rzystywane w artykule
wymaga od Czytelnika znajomości języka C na przedstawione w dokumencie RFC #86:
programy i dokumen-
poziomie średnim  tzn. takim, który umożliwi
tacja.
mu zrozumienie przedstawionych kawałków kodu " administrator powinien mieć możliwość wyboru
oraz ewentualnie napisanie własnych. Atutem jest domyślnego mechanizmu autentykacji dla danego
każdy miesiąc doświadczenia linuksowego, które systemu,
Czytelnik posiada. Trzeba to powiedzieć wprost: " konfiguracja autentykacji musi być związana
zabawa z PAM nie zawsze jest łatwa i przyjemna. z konkretną usługą (czyli każda usługa ma
Jednak satysfakcja i korzyść, która płynie z przezwy- swój zestaw reguł dotyczących uwierzytelnia-
ciężenia PAM-owych trudności, jest  zapewniam nia i nadawania praw),
Was  niebagatelna. " istnieje możliwość konfiguracji więcej niż jednego
sposobu autentykacji dla danej aplikacji,
Co to jest PAM? " moduły układa się w stos, aby użytkownik nie
Na początek parę słów do tych, którzy dopiero musiał kilka razy podawać tego samego hasła,
zaczynają poznawać zagadnienie. PAM to skrót od ale z drugiej strony istnieje możliwość poda-
angielskiego Pluggable Authentication Modules. wania różnych haseł podczas próby dostępu
Jest to zestaw bibliotek oraz interfejs programi- do jednej usługi,
styczny dla systemów uniksowych i linuksowych, " aplikacje pam-aware są niezależne od zmian
umożliwiający administratorowi dokładny wybór w PAM, tzn. nie jest wymagana ich rekompilacja
sposobu weryfikacji dostępu do usług. Mechanizm aby zmienić sposób dostępu.
ten jest integralną częścią wielu współczesnych
dystrybucji Linuksa (np. Red Hat, Debian), a także Jak działa PAM?
BSD (FreeBSD od wersji 3.1). Są cztery niezależne dziedziny, w których operuje
Idea PAM powstała w firmie Sun Microsystems PAM:
i bardzo szybko chwyciła. Pierwotnie w Solarisie
PAM występował w postaci niejawnej, tzn. nie było " zarządzanie autentykacją (ang. authentication
management),
" zarządzanie kontem (ang. account manage-
ment),
Autor jest studentem II roku informatyki na Politechni-
ce Gdańskiej. Od sześciu lat interesuje się systemami
" zarządzanie sesją (ang. session manage-
Unix/Linux, a w szczególności problematyką zabezpie-
ment),
czeń systemów komputerowych. Kontakt z autorem:
" zarządzanie hasłem (ang. password manage-
andrzej_nowak@o2.pl
ment).
11 www.haking.pl Haking nr 1
<>
Podczas autentykacji ma miejsce pobranie hasła (ogólnie:
Obsługa autentykacji (authentication
tokena) od użytkownika i sprawdzenie jego poprawności,
management)
a także nadanie użytkownikowi uprawnień.
Jeśli podane hasło jest dobre (ogólnie: jeśli PAM uzna,
Aby poprawnie zainicjalizować możliwości zarządzania autenty-
że użytkownik jest tym, za kogo się podaje), rozpoczyna
kacją we własnym module, dyrektywę #include się proces, w którym PAM sprawdza, czy użytkownik ma
pam_modules.h> należy poprzedzić definicją stałej PAM_SM_
dostęp do konta (account management). Takie problemy
AUTH (#define PAM_SM_AUTH)
Funkcja pam_sm_authenticate() może zwrócić nastę- jak wygaśnięcie hasła albo wyczerpana ilość loginów są
pujące wartości:
zgłaszane właśnie na tym etapie.
Pózniej następuje przejście do zarządzania sesją. PAM
" PAM_SUCCESS  proces autentykacji powiódł się i wszystko
jest obecne zarówno przy otwieraniu sesji, jak i przy jej
jest w porządku,
zamykaniu. Ogólnie moduły znajdujące się w grupie zarzą-
" PAM_AUTH_ERR  nie udało się pomyślnie przeprowadzić
dzania sesją przeprowadzają wszelkie czynności niezbędne
autentykacji,
do poprawnego zalogowania się użytkownika  na przykład
" PAM_USER_UNKNOWN  moduł nie zna takiego użytkowni-
montują dyski, przygotowują usługi.
ka,
Ostatnia grupa, zarządzanie hasłem, jest potrzebna
" PAM_AUTHINFO_UNAVAIL  zawiodła usługa zapewniająca
dostęp do informacji, tylko w momencie próby zmiany uprawnień dostępu (np.
" PAM_CRED_INSUFFICIENT  aplikacja nie ma wystarczają- haseł) przez użytkownika.
cych uprawnień aby przeprowadzić proces uwierzytelnie-
Na konfigurację PAM składają się plik /etc/pam.conf
nia,
oraz pliki w katalogu /etc/pam.d/. Zawierają one nazwy
" PAM_MAXTRIES  jeden lub więcej modułów autentykacji
usług lub programów, a w każdym z nich zawarty jest zestaw
wyczerpał maksymalną ilość prób.
reguł autentykacji dla danej aplikacji. Zawartość katalogu
/etc/pam.d/ i jego plików może się różnić nawet znacznie
Funkcję pam_sm_authenticate() można wywołać z flagą
między poszczególnymi dystrybucjami Linuksa, dlatego nie
PAM_DISALLOW_NULL_AUTHOK. Jej użycie powinno spowodo-
będę omawiał jego struktury  zachęcam do pozwiedzania
wać, że moduł w przypadku natrafienia na pusty token zwróci
wartość PAM_AUTH_ERR. W domyślnym przypadku jednak na własną rękę. Zauważmy, że nie ma gwarancji, że konfi-
moduł powinien zezwalać na użycie pustego tokena (np.
guracja stosu modułów przekopiowana z jednej dystrybucji
pustego hasła).
na inną będzie działać. Co więcej, należy spodziewać się
po takiej operacji niemałych problemów.
Flagi funkcji pam_sm_authenticate():
Przy okazji chciałbym przypomnieć o parametrze jądra,
który można podać przy bootowaniu Linuksa: init=/bin/
" PAM_SILENT,
ash.static (lub inny statyczny shell). Parametr ten pozwala
" PAM_DISALLOW_NULL_AUTHTOK  moduł powinien zwrócić
PAM_AUTH_ERR, jeśli token użytkownika jest pusty; bez zabootować system z ominięciem PAM, z pewnością przyda
tej flagi użytkownik w wymienionym przypadku nie będzie
się w sytuacji, kiedy PAM ma popsutą konfigurację. Przed
pytany o hasło.
przeprowadzeniem bardziej skomplikowanych modyfikacji
warto utworzyć kopie zapasowe plików konfiguracyjnych.
Funkcja pam_sm_setcred() może zwrócić:
Jak powinien wyglądać moduł?
" PAM_SUCCESS,
" PAM_CRED_UNAVAIL  nie udało się ustalić praw użytkow- Z racji faktu, że istnieje ogólny interfejs API dla PAM, każdy
nika, moduł powinien bazować na pewnym ogólnym szkielecie.
" PAM_CRED_EXPIRED  upłynął termin ważności praw
Budując program zgodnie z powszechnymi zaleceniami
użytkownika,
zwiększamy jego niezawodność w sytuacjach kryzysowych
" PAM_USER_UKNOWN  moduł nie zna takiego użytkownika,
 np. przy braku dostępu do sieci. Zachowanie się modułu
" PAM_CRED_ERR  nie udało się ustawić praw użytkowni-
w trudnych warunkach może mieć kluczowe znaczenie dla
ka.
poprawności działania całego systemu.
Przede wszystkim należy pamiętać o niezależnej obsłudze
Flagi funkcji pam_sm_setcred():
każdej z czterech grup zarządzania (autentykacja, konto,
" PAM_ESTABLISH_CRED  ustaw uprawnienia związane
sesja i hasło). Użytkownik może wywoływać obsługę kolej-
z usługą uwierzytelniającą,
nych grup w dowolnej kolejności, więc nie należy polegać na
" PAM_DELETE_CRED  usuń uprawnienia związane
uprzednim poprawnym wykonaniu instrukcji z innej części
z usługą uwierzytelniającą,
kodu niż ta, do której aktualnie odwołuje się system. Jednak
" PAM_REINITIALIZE_CRED  odnów uprawnienia użytkow-
w pewnych sytuacjach możemy przyjąć założenia dotyczące
nika,
wcześniej przeprowadzonych czynności: np. jeżeli wywoły-
" PAM_REFRESH_CRED  przedłuż uprawnienia użytkownika
wana jest funkcja pam_sm_open_session(), to użytkownik
(w czasie).
został wcześniej uwierzytelniony. Moduł powinien zawierać
Haking nr 1 www.haking.pl
12
<>
<>
(pam_handle_t *pamh, int flags,
int argc, const char **argv);
Obsługa konta (account management)
Aby poprawnie zainicjalizować możliwości zarządzania kontem
" Zarządzanie kontem:
we własnym module, dyrektywę #include modules.h> należy poprzedzić definicją stałej PAM_SM_ACCO-
PAM_EXTERN int pam_sm_acct_mgmt
UNT (#define PAM_SM_ACCOUNT)
(pam_handle_t *pamh, int flags,
Funkcja pam_sm_acct_mgmt() może zwrócić następują-
int argc, const char **argv);
ce wartości:
" PAM_SUCCESS, " Zarządzanie sesją:
" PAM_ACCT_EXPIRED  konto wygasło i użytkownik nie
PAM_EXTERN int pam_sm_open_session
może się już logować,
" PAM_AUTH_ERR  wystąpił błąd autentykacji, (pam_handle_t *pamh, int flags,
" PAM_AUTHTOKEN_REQD  hasło wygasło; użytkownik
int argc, const char **argv);
prawdopodobnie zostanie zapytany o nowe,
PAM_EXTERN int pam_sm_close_session
" PAM_USER_UNKNOWN  moduł nie zna takiego użytkowni-
(pam_handle_t *pamh, int flags,
ka.
int argc, const char **argv);
Flagi: jak dla funkcji pam_sm_authenticate().
" Zarządzanie hasłem:
PAM_EXTERN int pam_sm_chauthtok
taki podzbiór sześciu podstawowych funkcji (przedstawiam
(pam_handle_t *pamh, int flags,
je nieco dalej), aby był zdolny do działania przynajmniej
int argc, const char **argv);
w jednej grupie.
Kolejnym istotnym zaleceniem jest poprawna obsłu-
ga wywołań funkcji  spośród wspomnianych wcześniej Czytelnik z pewnością już zauważył, że do każdej funkcji
sześciu  których ciało nie zostanie zaimplementowane. przekazywane są takie same zestawy parametrów. Jest
Twórcy PAM zalecają, aby w zależności od potrzeby funkcje to rozwiązanie służące ujednoliceniu interfejsu modułu.
takie zwracały wartość PAM_SUCCESS, PAM_SERVICE_ERR lub Niezależnie od funkcji, jaką będzie pełnił (np. zarządzanie
PAM_IGNORE (stałe PAM_* oraz ich znaczenia znajdują się kontem czy hasłem), jego konfiguracja będzie przeprowa-
w Ramkach obsługa autentykacji, obsługa konta, obsługa dzana w identyczny sposób. Pierwszy parametr (*pamh) jest
sesji, obsługa hasła). wskaznikiem na uchwyt, przekazywanym do funkcji przez
W dalszej perspektywie należy uwzględnić fakt, że z pliku PAM. Drugi (flags) to flagi, z jakimi jest wywoływany moduł.
konfiguracyjnego mogą zostać przekazane jakieś argumenty, Nie są to zwykłe parametry  te są przekazywane w dwóch
np. debug. Możemy je odczytać w tradycyjny sposób, uży- następnych zmiennych. Argc oznacza ilość argumentów,
wając zmiennych int argc i char *argv[] jako parametrów. **argv jest ich tablicą.
Zwracam uwagę, że zmienna argv[0] nie zawiera nazwy Jak widać obsługą autentykacji (authentication mana-
modułu, jak to ma miejsce w zwykłych programach! Jest gement) zajmują się dwie funkcje: pam_sm_authenticate()
ona wskaznikiem na początek pierwszego argumentu. i pam_sm_setcred(). Do obu (jak do wszystkich omawianych
Programiści powinni także zwrócić uwagę na szereg
innych zagadnień. Przeważnie moduły PAM są ładowane
dynamicznie  nie należy zatem używać zmiennych typu
Obsługa sesji (session management)
static. Zaleca się szerokie wykorzystanie licznych kodów
Aby poprawnie zainicjalizować możliwości zarządzania sesją
powrotu (na Listingach 1-4).
we własnym module, dyrektywę #include modules.h> należy poprzedzić definicją stałej PAM_SM_SES-
Wchodzimy w głąb 
SION (#define PAM_SM_SESSION)
podstawowe funkcje
Funkcje pam_sm_open_session() oraz pam_sm_close_
Oto prototypy sześciu podstawowych funkcji, będących
session() mogą zwrócić następujące wartości:
podstawowymi częściami składowymi modułów PAM:
" PAM_SUCCESS,
" PAM_SESSION_ERR  błąd przy otwieraniu lub zamykaniu
" Autentykacja:
sesji.
PAM_EXTERN int pam_sm_authenticate
Flagi:
(pam_handle_t *pamh, int flags,
int argc, const char **argv);
" PAM_SILENT
PAM_EXTERN int pam_sm_setcred
13
www.haking.pl Haking nr 1
<>
funkcji) można dodać flagi przekazywane przez zmienną
Obsługa hasła (password management)
flags. Odpowiednie flagi dla poszczególnych funkcji zawarte
są w Ramkach obsługa autentykacji, obsługa konta, obsługa
Aby poprawnie zainicjalizować możliwości zarządzania hasłem
sesji, obsługa hasła. Pierwsza z funkcji przeprowadza bezpo-
we własnym module, dyrektywę #include średnie czynności związane z autentykacją, druga zajmuje
modules.h> należy poprzedzić definicją stałej PAM_SM_PAS-
się ustalaniem i przygotowywaniem uprawnień użytkownika.
SWORD (#define PAM_SM_PASSWORD).
Aplikacje powinny wywoływać ją po przeprowadzeniu uwie-
Funkcja pam_sm_chauthtok() może zwrócić następujące
rzytelnienia, ale przed rozpoczęciem sesji.
wartości:
Autentykacja może być przeprowadzana na wiele róż-
" PAM_SUCCESS,
nych sposobów  programiści mają tutaj ogromne pole do
" PAM_AUTHTOK_ERR  nie udało się uzyskać nowego
popisu. Jeśli ktoś ma chęć i możliwości, może napisać kod
hasła,
obsługujący czytnik linii papilarnych albo skaner siatkówki
" PAM_AUTHTOK_RECOVERY_ERR  nie udało się uzyskać
oka i używać takiego modułu do uwierzytelniania użytkow-
starego hasła,
nika przy komputerze z podłączonym urządzeniem. Bardziej
" PAM_AUTHTOK_LOCK_BUSY  hasło jest zablokowane,
prozaicznym pomysłem jest funkcja, która żąda od roota
" PAM_AUTHTOK_DISABLE_AGING  starzenie się hasła
oprócz zwykłego hasła także hasła dnia, np. na stałe wkom-
zostało zablokowane,
pilowanego w moduł  w ten sposób potencjalny włamywacz
" PAM_PERM_DENIED  odmowa dostępu,
miałby małą niespodziankę.
" PAM_TRY_AGAIN  przygotowania do zmiany hasła nie
Kawałek kodu zajmujący się weryfikacją dostępu powiodły się,
" PAM_USER_UNKNOWN  moduł nie zna takiego użytkowni-
do konta (account management, pam_sm_acct_mgmt() )
ka.
w ogólności powinien umożliwić ustalenie, czy użytkownik
ma w danej chwili zezwolenie na wstęp. Można się spodzie-
Flagi:
wać, że użytkownik przeszedł wcześniej uwierzytelnianie.
" PAM_CHANGE_EXPIRED_AUTHTOK  oznacza, że hasło
Pam_sm_acct_mgmt() jest dobrym miejscem, od którego
powinno być zmieniane tylko w przypadku wygaśnięcia; ta
można zacząć pisanie własnych procedur w module. Dla
flaga musi występować w połączeniu z dwiema następny-
przykładu, można w tym miejscu zamieścić funkcję, która
mi,
sprawdza dzień tygodnia. Jeśli jest piątek, nie wpuszcza
" PAM_PRELIM_CHECK  sprawdzanie gotowości modułu do
do systemu użytkowników próbujących zalogować się przy
zmiany hasła użytkownika; jeśli moduł nie jest gotowy,
użyciu klientów SSH spod Windows (ten złośliwy pomysł nie
powinien zwrócić PAM_TRY_AGAIN,
jest świeży, bowiem istnieje już moduł do serwera Apache,
" PAM_UPDATE_AUTHTOK  moduł powinien zmienić hasło
który w piątki odmawia współpracy z przeglądarkami Internet
użytkownika w tym wywołaniu funkcji pam_sm_chauth-
Explorer). Z praktyczniejszych pomysłów: można pokusić się
tok().
o umieszczenie wewnątrz omawianej funkcji kod wpuszcza-
Uwaga: funkcja pam_sm_chatuthtok() jest wywoływana dwa
jący do systemu określonych użytkowników o określonych
razy: najpierw z flagą PAM_PRELIM_CHECK, a następnie (jeśli
godzinach  podobny przykład rozważymy dalej.
moduł nie zwróci błędu PAM_TRY_AGAIN) z flagą PAM_UPDATE_
Zarządzanie sesją to część odpowiedzialna za czynno-
AUTHTOK.
ści, które trzeba wykonać bezpośrednio przed otwarciem
dostępu do usługi i po jego zamknięciu. Przykłady: logowanie
informacji o otwarciu sesji, montowanie i odmontowanie ta jest zdefiniowana w dokumentacji do PAM (Linux-PAM
katalogów, wyświetlanie komunikatów, przygotowywanie Module Writers' Guide).
danych etc.
Funkcja pam_sm_chauthtok jest wywoływana, gdy moduł " debug  po otrzymaniu tego argumentu moduł powinien
zostanie dołączony do stosu grupy password (Password wyrzucać do logów większe ilości informacji niż normalnie
Management). Użytkownik chcący zmienić hasło będzie musiał (debug information),
przebić się przez niespodzianki, które mu przygotujemy. Dla " try_first_pass  stosowane przy modułach passwd i auth;
przykładu, możliwości modułu pam_cracklib mogą okazać w przypadku otrzymania takiego argumentu, moduł powi-
się dla nas niewystarczające, bo z bliżej nieokreślonego nien spróbować skorzystać z hasła przekazanego przez
powodu chcemy, aby każdy użytkownik posiadał w haśle wcześniejszy moduł, a jeśli to się nie powiedzie, spytać
przynajmniej trzy znaki $. Wtedy odpowiednio napisana o swoje hasło,
funkcja pam_sm_chauthok umieszczona we własnym module " use_first_pass  jak wyżej, z jedną różnicą: gdy próba
PAM pomoże ten nakaz wyegzekwować. uwierzytelnienia z otrzymanym hasłem nie powiedzie
się, moduł powinien zakończyć ją z negatywnym wyni-
Argumenty do modułu kiem,
Moduł powinien (ale nie musi) obsługiwać grupę standardo- " expose_account  użycie tego argumentu oznacza, że
wych argumentów, które może przekazać użytkownik. Grupa administrator nie ma nic przeciwko ujawnianiu informacji
Haking nr 1 www.haking.pl
14
14
<>
<>
Jeśli funkcja przyjmie flagę PAM_SILENT, nie powinna zwra-
Listing 1. pam_czas.c
cać do aplikacji żadnego tekstu (np. błędów lub informacji
debugujących).
#include
#include
#include Współpraca z aplikacjami
#include
Na razie mogło by się wydawać, że moduł PAM jest wielkim
#define PAM_SM_ACCOUNT
altruistą  wiele daje i nie bierze nic w zamian. Biblioteka
#include
libpam zapewnia zestaw funkcji niezbędnych do interakcji
#include
ze światem PAM  z innymi modułami oraz korzystającymi
PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, z nich programami. Ich prototypy i dokładny opis działania
int flags, int argc, const char **argv) {
znajdziecie w dokumentacji, podczas gdy ja omówię jedynie
int odpowiedz = PAM_AUTH_ERR, ret = PAM_AUTH_ERR;
ich znaczenie:
const char *uzytkownik = NULL, *tempptr = NULL;
char *info = "Nie mozesz sie teraz zalogowac.\n";
" pam_set_data(), pam_get_data()  służą do zapisu
struct pam_conv *rozmowa;
struct pam_message komunikat i odczytu stanu sesji; użycie zmiennych typu static
struct pam_message *pkomunikat = &komunikat;
nie jest wskazane, w zamian mamy do dyspozycji ten
struct pam_response *resp = NULL;
mechanizm,
time_t sekundy;
" pam_set_item(), pam_get_item()  służą do zapisu
struct tm czas;
i odczytu zmiennych PAM_*,
ret = pam_get_user(pamh, &uzytkownik, NULL); " pam_get_user()  wczytuje nazwę użytkownika,
if(ret != PAM_SUCCESS) return ret;
" pam_putenv(), pam_getenv()  zarządzanie zmiennymi
środowiskowymi PAM,
if(!strncmp(uzytkownik,"root",4))
" pam_getenvlist()  wczytuje całą listę zmiennych śro-
odpowiedz = PAM_SUCCESS;
dowiskowych związanych z PAM,
else {
sekundy = time(NULL); " pam_strerror()  formatuje komunikat o błędzie w oparciu
if(sekundy == -1) return ret;
o numer (kod) błędu,
" pam_fail_delay()  implementuje obsługę opóznień po
localtime_r(&sekundy, &czas);
nieudanej autentykacji.
if((czas.tm_hour < 8) || (czas.tm_hour > 15)) {
if(flags&PAM_SILENT) return odpowiedz; Przykładowe moduły
Poniżej omawiam kod dwóch prostych, małych modułów,
komunikat.msg_style = PAM_TEXT_INFO;
które napisałem na potrzeby artykułu  aby pokazać działanie
komunikat.msg = tempptr =
wspomnianych mechanizmów PAM. yródła modułów znaj-
malloc(strlen(info)+1);
dziecie również na płytce dołączonej do pisma. Zakładamy,
sprintf(tempptr,"%s",info);
pam_get_item(pamh, PAM_CONV, że będą one ładowane dynamicznie, więc nie będę omawiał
(const void **)&rozmowa);
aspektów związanych z kompilacją statyczną (jest to dobrze
rozmowa->conv(1, (const struct pam_message
opisane w dokumentacji). Kilka ważniejszych struktur z API
**)&pkomunikat, &resp,
PAM znajdziecie na Listingu 3.
rozmowa->appdata_ptr);
usleep(2000000);
free(tempptr); Przykładowy moduł #1  pam_czas
Pierwszy moduł (pam_czas.c, na Listingu 1) będzie służył do
if (resp)
wpuszczania użytkowników innych niż root tylko w określonych
_pam_drop_reply(resp, 1);
godzinach (8-15) i będzie przeznaczony do pracy w grupie
} else odpowiedz = PAM_SUCCESS;
zarządzania kontem. Moglibyśmy również napisać go tak,
}
return odpowiedz; żeby pracował w grupie session albo uwierzytelniającej
}
 byłoby to również w pełni poprawne rozwiązanie.
Dozwolone godziny umieścimy na stałe w module, chociaż
związanych z kontem logującego się użytkownika, na naturalnie można zrobić odczyt z pliku konfiguracyjnego, jeśli
przykład jego imienia i nazwiska, ktoś ma taką potrzebę. Nie ma większych przeciwwskazań,
" no_warn  moduł nie powinien zgłaszać żadnych ostrze- jeśli chodzi o korzystanie z różnych bibliotek (np. math)
żeń do wywołującego programu, w modułach PAM  należy tylko pamiętać o prawidłowej
" use_mapped_pass  argument ten pozwala na użycie kompilacji kodu zródłowego. Kompilator nie zawsze zgłosi
hasła podanego przez użytkownika w celu uzyskania wszystkie problemy  jeśli korzystamy z makr PAM, trzeba
przez moduł informacji o uwierzytelnianiu pochodzącej pamiętać o dołączeniu nagłówka security/_pam_macros.h,
z innego zródła. bo gcc nam o tym nie przypomni.
15
15
15
www.haking.pl Haking nr 1
<>
Listing 2. pam_custom_motd.c
#include if(dfmotd == NULL) dfmotd = DEFAULT_MOTD;
#include if(cmpath == NULL) cmpath = CUSTOM_MOTD_DIR;
#include
#include if(cmpath[strlen(cmpath)-1] == '/')
#include snprintf(bufor,1023,"%s%s",cmpath,uzytkownik);
#include else
#include snprintf(bufor,1023,"%s/%s",cmpath,uzytkownik);
#define PAM_SM_SESSION if(stat(bufor,&st))
snprintf(bufor,1023,"%s",dfmotd);
#include if ((fd = open(bufor, O_RDONLY, 0)) >= 0)
#include {
if ((fstat(fd, &st) < 0) || !st.st_size)
#define DEFAULT_MOTD "/etc/motd" return PAM_IGNORE;
#define CUSTOM_MOTD_DIR "/etc/cmotd/" komunikat.msg = tmpptr = malloc(st.st_size+1);
if(!komunikat.msg) return PAM_IGNORE;
PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, read(fd, tmpptr, st.st_size);
int flags, tmpptr[st.st_size] = '\0';
int argc, close(fd);
const char **argv)
komunikat.msg_style = PAM_TEXT_INFO;
{
pam_get_item(pamh, PAM_CONV,
const char *uzytkownik = NULL;
(const void **)&rozmowa);
char *tmpptr = NULL;
rozmowa->conv(1, (const struct
char *dfmotd = NULL, *cmpath = NULL, *plik = NULL;
pam_message **)&pkomunikat,
struct pam_conv *rozmowa;
&resp, rozmowa->appdata_ptr);
struct pam_message komunikat,
free(tmpptr);
struct pam_message *pkomunikat = &komunikat;
struct pam_response *resp = NULL;
if (resp)
int fd, ret; struct stat st;
_pam_drop_reply(resp, 1);
char bufor[1024];
}
return PAM_SUCCESS;
if(flags&PAM_SILENT) return PAM_IGNORE;
}
ret = pam_get_user(pamh, &uzytkownik, NULL);
PAM_EXTERN int pam_sm_close_session(
if(ret != PAM_SUCCESS) return ret;
pam_handle_t *pamh,
int flags,
for(;argc-- > 0; ++argv)
int argc,
{
const char **argv)
if(!strncmp(*argv,"default_motd=",13))
{
dfmotd = (char *) strdup(13+*argv);
return PAM_IGNORE;
if(!strncmp(*argv,"cmotd_path=",10))
}
cmpath = (char *) strdup(10+*argv);
}
Po zdefiniowaniu zmiennych ładujemy wskaznik na nazwę Należy pamiętać, że omawiany moduł pełni funk-
użytkownika przyjetą przez PAM (funkcja pam_get_user()). cję jedynie strażnika wejścia do systemu. PAM nie zajmu-
Jeśli użytkownikiem jest root, zezwalamy mu na wstęp nie- je się sesjami w toku, chyba że ktoś w ich trakcie potrze-
zależnie od godziny. W przeciwnym wypadku sprawdzamy buje autentykacji, autoryzacji, nowej sesji lub zmiany
lokalny czas i porównujemy go z dozwolonymi godzinami hasła.
logowania. Jeśli wolno teraz otworzyć sesję, zwracamy Kompilację przeprowadzamy komendą:
wartość PAM_SUCCESS. Jeśli nie, wyświetlamy przy użyciu
mechanizmu conversation komunikat o błędzie. gcc -shared modul.c -o modul.so.
Mechanizm conversation jest zalecanym sposobem
komunikacji z użytkownikiem. Na potrzeby testowania Gotową bibliotekę należy skopiować do katalogu /lib/security/.
modułu można posługiwać się np. printf(), lecz użycie Aby przetestować moduł, możemy dopisać linijkę do pliku
takich funkcji w gotowym produkcie nie jest zalecane. /etc/pam.d/system-auth (RedHat), w grupie account:
Funkcja usleep() pozwala upewnić się, że użytkownik
account required /lib/security/pam_czas.so
zdąży przeczytać komunikat (mógłby on zostać zmazany
przez następny moduł).
Haking nr 1 www.haking.pl
16
16
<>
<>
koniec ustawiamy typ informacji na tekstową i wysyłamy ją
Listing 3. Wybrane struktury dostępne w API PAM:
przy użyciu funkcji pam_conv->conv().
Kompilacja i instalacja przebiega prawie tak samo, jak
// Podstawowa struktura mechanizmu conversation:
przy module pam_czas. Zmienia się tylko linia, którą należy
struct pam_conv {
int (*conv)
dopisać do pliku i jej położenie (na grupę session):
(int num_msg,
const struct pam_message **msg,
session required /lib/security/pam_custom_motd.so
struct pam_response **resp,
void *appdata_ptr);
Dobre praktyki
void *appdata_ptr;
};
programistyczne
Moduł powinien zachowywać się poprawnie w problematycz-
// Struktura pam_message:
nych sytuacjach, takich jak brak dostępu do pliku, brak
struct pam_message {
pamięci itp. Zaleca się nadpisywanie losowymi danymi albo
int msg_style;
zerami wszelkich zmiennych, w których przechowywane były
const char *msg;
};
hasła. Uparty programista mógłby odzyskać pamięć zwol-
nioną po module i odczytać z niej poufne dane.
// Struktura pam_response:
Autorzy dokumentacji PAM kierują naszą uwagę na
struct pam_response {
różnicę między numerami ID zwracanymi przez funkcje
char *resp;
getuid(), geteuid() oraz pam_get_user()  dlatego nie
int resp_retcode;
};
należy ich wszystkich wrzucać do jednego koszyka. Podają
oni bardzo dobry przykład  użytkownik A używa programu
setuid na użytkownika B, aby stać się użytkownikiem C.
Przykładowy moduł #2  pam_custom_motd Funkcja getuid zwróci ID użytkownika A, geteuid użytkownika
Drugi moduł (pam_custom_motd.c) jest nieco bardziej B, a pam_get_user  C.
skomplikowany niż wcześniejszy. Kod zródłowy jest oparty Naturalnie istnieje szereg wskazówek nie związanych
o moduł pam_motd, standardowo dostępny z PAM, jednak z bezpieczeństwem. Przeważnie moduły PAM są ładowane
jest on wzbogacony o drobny, całkiem funkcjonalny szcze- dynamicznie  nie należy zatem używać zmiennych typu
gół. Jeśli zostanie znaleziony plik /etc/cmotd/USER (gdzie static. Dobrą praktyką jest wrzucanie wszelkich informacji
USER jest loginem użytkownika otwierającego sesję), jest on o błędach do logów systemowych. Użytkownik powinien być
wyświetlany jako message of the day (MOTD). W przeciwnym niepokojony jedynie niezbędnymi komunikatami, np. Logo-
razie użytkownik ujrzy zawartość /etc/motd. wanie zabronione albo Logowanie nie powiodło się.
Wykorzystujemy funkcję pam_sm_open_session(), bo W dokumentacji do PAM można znalezć zalecane
wyświetlanie komunikatów przy logowaniu powinno odby- poziomy logowania dla poszczególnych grup komunikatów.
wać się na etapie session management. W deklaracjach W dokumentacji przypomina się również o konieczności
zmiennych umieszczamy wszystkie stosowne wpisy, w tym inicjalizacji struktur używanych w funkcjach konwersacyj-
deklaracje dla struktury conversation (jak we wcześniejszym nych (conversation). Trzeba przewidzieć przypadek, w którym
module) i dla tymczasowego kilobajtowego bufora, w którym taka funkcja zwróci niepoprawne dane, albo w ogóle nic
będziemy przechowywać ścieżkę do katalogu (kilobajt nie zwróci  dobrze zainicjalizowana struktura ułatwia jego
powinien wystarczyć). Jeśli moduł nie jest zobowiązany identyfikację.
siedzieć cicho (w tym wypadku idea traci sens i trzeba
wyjść z funkcji), wczytujemy nazwę użytkownika, a następnie Końcowe wskazówki
argumenty do modułu, podawane w odpowiednich plikach Spośród dostępnych modułów PAM warto na początek
konfiguracyjnych PAM. przeanalizować zródła czterech z nich: pam_permit, pam_
Z argumentów wczytujemy do zmiennych ścieżki domyśl- deny, pam_issue i pam_warn. Ich funkcje to odpowiednio:
nego motd (parametr default_motd=...) i katalogu zawiera- wpuszczanie każdego, nie wpuszczanie nikogo, wyświetlanie
jącego motd dla poszczególnych użytkowników (parametr /etc/issue, logowanie zmiennych PAM do pliku. Wspomniane
cmotd_path=...). Jeśli parametry nie zostały podane, będziemy moduły czytelnik znajdzie w zródłach PAM. Warto także wni-
używać domyślnych nazw zdefiniowanych na początku kodu. kliwie przeczytać dokumentację, która jest może miejscami
Tworząc pełną nazwę pliku do wyświetlenia sprawdzamy, niejasno napisana (tylko miejscami), ale za to jest dosyć
czy użytkownik podał końcowy ukośnik w nazwie katalogu. bogata. I  oczywiście  eksperymentować samemu.ną
Funkcja stat sprawdza, czy istnieje specjalny plik motd
dla użytkownika  jeśli nie, będzie wyświetlany domyślny
W Sieci:
motd.
Następnie wczytujemy zawartość pliku do bufora, na który
" http://www.kernel.org/pub/linux/libs/pam/
przy okazji wskazuje element msg struktury pam_message. Na
17
17
17
www.haking.pl Haking nr 1


Wyszukiwarka

Podobne podstrony:
Binder Pam Swatka
zarzad pam
WSTEP KAT W PAM (2)
ALG KAT W PAM (2)
Z3 PAM~1
Binder Pam Swatka
07 03 PAM Zintegrować i rozpalić płomień Diamentowego Promienia
pam 8
Hołd złożony wielkim Polakom w wierszach C K N Bema pam~C1C
08 08 PAM Otwarcie Galaktycznej Bramy Nieskończoności
Jenoff Pam W sieci tajemnic(1)

więcej podobnych podstron