<> <> 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