Wprowadzenie do PNIAM (Pluggable Non Interactive Autentication Modules)

Autor: Grzegorz Stanisławski

PNIAM jest biblioteką i zestawem dynamicznie ładowanych modułów, których zadaniem jest najkrócej mówiąc autentykacja i autoryzacja użytkownika. PNIAM powstało na bazie doświadczeń zdobytych podczas implementacji PAM i głowną zadaniem tej biblioteki, jest dostarczenie podobnej a nawet wieększej funkcjonalności oraz usunięcie wad, które zostały odkryte w PAM'ie. Poniższy tekst opisuje na razie jedynie strone "developerska" pniam, opis ja używać biblioteki powstanie gdy będzie ona na tyle rozwinięta, aby móc przynajmniej w jakiś stopniu zastąpić rozwiązania obecnie stosowane czyli PAM'a.

Definicje.

Autentykacja - jest to procedura mająca na celu znalezienie odpowiedzi czy użytkownik jest rzeczywiscie tym za kogo się podaje. Zwykle autentykacja sprowadza sie do przedstawienia się użyktownika przy pomocy unikatowej nazwy (login) oraz uwiarygodnienie się przy pomocy odpowiedniego hasła, choć bardziej zaawansowane systemy mogą kożystać z innych danych jak np. odcisk palca ;-)

Autoryzacja - Odpowiedz na pytanie czy użytkownik, przeważnie już zweryfikowany przez procedure autentykacji, może uzyskać dostęp do aplikacji/zasobu, którego żąda oraz dostarczenie aplikacji danych o użytkowniku.

Po co.

Jedną z głównych wad jakie wytykano PAM'owi było nie dotykanie problemu autoryzacji poza kwestią dostępu do zasobów. Dane o użytkowniku zarówno aplikacja jak i same moduły musiały znajdować sobie na własną ręke, najczęsciej przeszukując odpowiednimi funkcjami glibc'a pliki /etc/{passwd|shadow}. Mechanizm wbudowany w glibc umożliwiał wprawdzie dostanie sie do tych danych również jesli były one w bazie NIS, jednak inne bazy danych były niedostępne.

Próbą rozwiązania tego problemy było pojawienie sie biblioteki pwdb, której zadaniem była autoryzacja i to tylko w kwestii dostarczania danych. Pwdb jednak nigdy nie rozwinęło się poza zastąpienie funkcjonalności oferowanej już przez glibc (nss_switch). W dodatku, konstrukcja samego PAM'a powodowała że dane dostarczone przez pwdb modułowi pam_pwdb ginęły po zakończeniu każdego stadium autentykacji. W efekcie plik /etc/passwd był przeszukiwany przez moduł pam_pwdb cztery razy, nie licząc kolejnych wykonywanych już przez samą aplikacje, która musiała jakoś te dane uzyskać i najczęsciej robiła to przy pomocy funkcji oferowanych przez glibc.

Istniała wprawdzie teoretycznie możliwośc gromadzenia danych potrzebnych do autoryzacji i przekazania ich aplikacji poprzez wywołanie pam_set_credentials oraz pam_env jednak nie zostal opracowany zaden standard do tego i zadna aplikacja z niego nie kożystała. Co więcej SUN twierdzi, że np lista grup do których należy user nie są daną która zasługuje na bycie "credential" w związku z czym nie powinna być ustalana przez jakilolwiek moduł w wywołaniu funkcji pam_set_credentials.

Kolejną wadą PAM'a była jego interaktywność, Moduły miały prawo same komunikować się z użytkownikiem, co w wielu przypadkach prowadziło do problemów. Problem ten dało się w prawdzie omijać ale nie było to spójne ani odporne na ewentualne komplikacje rozwiązanie (partrz PAM w sambie).
Bylo tego więcej ale akurat nie pamiętam ;-)

Jak działa PNIAM.

Pniam obsługuje zarówno autentykacje jak i autoryzacje.
Całą procedure udostępniania zasobu wykonuje się przy pomocy czterech funkcji realzujących kolejno autentykację, autoryzację, otwarcie i zamknięcie sesji oraz jednej struktury danych, co umożliwia współprace z bazami danych w których autoryzacja i autentykacja są realizowane podczas jednego wywołania np. RADIUS. Odpowiednia tablica jest wypełniana danymi potrzebnymi do autoryzacji już podczas autentykacji, dane te czekają następnie na "swój moment".

Komunikacja pomiędzy aplikacją a biblioteką, czy bardziej dokładnie odpowiednim modułem, odbywaq się w następujący sposób. Aplikacja podaje, jakie dane o użytkowniku ją interesują. Zwykle będą to: UID, GID, GROUPS, GECOS, SHELL i HOME, oraz jakie dane jest w stanie sama dostarczyc. Przeważnie USER(name) PLAIN_PASSWORD, TTY lub RHOST. Jesli je już zna może je do struktury wprowadzić (np, gdy jest uruchamiana nieinteraktywnie i dane te były przekazane w wierszu polecenia) Uruchamia procedure autentykacji z biblioteki, która przeglądając plik konfiguracyjny uruchamia odpowiednie moduły. Moduly na podstawie tych danych weryfikują użytkownika, zwracając PNIAM_OK, PNIAM_FAIL, PNIAM_IGNORE lub PNIAM_AGAIN odpowiednio w przypadku sukcesu, porażki, braku możliwości podjęcia takiej decyzji (ewentualnie modułowi może byc wszystko jedno) lub w razie jakiś dodatkowych pytań.

W ostatnim przypadku, struktura danych PNIAM'a zawiera dodatkowe pytania, na które aplikacja powinna udzielić odpowiedzi (struktura zawiera również domyślny prompt którym można się zapytać użytkownika o tę daną), lub jeśli nie jest wstanie tego zrobić (np. nieinteraktywne działanie) zaznaczyć jako niedostępne. Kolejne wywołanie funkcji autentykującej powinno, o le nie pojawią się nowe pytania zakończyć sukcesem bądź porażką. Biblioteka sprawdza kody zwracane przez moduły i zwraca PNIAM_OK jeżeli wystąpiły same PNIAM_OK lub PNIAM_IGNORE, oraz PNIAM_FAIL jesli pojawiło się choć jedno PNIAM_FAIL bądź same PNIAM_IGNORE. Podobnie wygląda procedura autoryzacji z tym, że biblioteka dba aby wszyskie dane, które "chciała" aplikacja a których nie oznaczyła jako PNIAM_ITEM_OPTIONAL zostały dostarczone.

Funkcje otwarcia i zamknięcia sesji dotyczą tylko zbierania danych na temat wykorzystania aplikacji/zasobu i jak narazie nie istnieje moduł który by to realizował więc pozwole sobie je pominąć.

Zmiana danych w bazie.

Pniam ma również funkcję do zmiany danych przechowywanych w bazach, jednak nie jest ona jeszcze do końca zdefiniowana.

Struktura danych PNIAM'a.

Pisalem powyżej o strukturze wykożysywaniej przez PNIAM. Oto ona:

typedef struct pniam_request {
        pniam_item_list_t *input;
        pniam_item_list_t *ok_replies;
        pniam_item_list_t *prompts;
    pniam_item_list_t *fail_replies;
        void  **priv_data[0];
} pniam_request_t;

Pierwszy element rekordu (input) to lista danych, które aplikacja może dostarczyć. Drugi (ok_replies), zawiera listę danych o które aplikacja pyta, wtedy wartośc pola value (patrz niżej) ma mieć wartość NULL, lub które zostały dostarczone przez moduły, wtedy pole value zawiera odpowiednią wartość. fail_replies zawiera te, których nie udało się modułom znaleśc w swoich bazach danych. Pole prompts zawiera dane które aplikacja powinna jeszcze dostarczyc. (to w przypadku kiedy funkcja pniam_authenticte() lub pniam_authorize() zwróciły PNIAM_AGAIN).

Dane w wyżej wymienionych polach są przechowywanie w formie "typu słownikowego", jako dynamiczna lista (właściwie dwukierunkowa kolejka) następujących rekordów:

typedef struct pniam_item {
                const char *name; /* zero-terminated string*/
                unsigned char *data;
                int len; /*data length */
                unsigned int flags;
                /* Supplementary data */
                struct pniam_item *prev;
                struct pniam_item *next;
} pniam_item_t;

Odpowieni zestaw funkcji pozwala na dodawanie i usuwanie elementów tej listy oraz jej przeszukiwanie. Przeszukiwanie odbywa się przy pomocy nazwy danej - klucza przechowywanego w polu name. Funkcjonalnie jest to "tablica indeksowana stringiem" czyli "typ słownikowy". Konstrukcja taka umożliwia przekazywanie aplikacji danych, o których istnieniu moduł nie musi nawet rozpoznawać. Na przykład doduł RADIUS, jeśli powstanie, będzie mógł przekazywać do struktury wszysktie dane, które otrzyma od serwera radiusa, po prostu przepisując je z jednej struktury danych do drugiej. Z drugiej strony aplikacja może żądać od biblioteki dowolnych danych, więc docelowo, może nie być już potrzeby posiadania i przetwarzania przez nią własnych plikópw konfiguracyjnych. Należy tylko zapewnić moduł który bedzie wstanie je dostarczyć. Ten sam modół będzie mógł oczywiście serwować dane dla wielu aplikacji, co w rezultacie powinno usprawnić i ujednolicić zarządzanie serwerem.

Prototypy funkcji:

void          pniam_item_list_push(pniam_item_list_t **,pniam_item_t *);
void          pniam_item_list_pop(pniam_item_list_t **,pniam_item_t *);
pniam_item_t  *pniam_item_list_getfirst(pniam_item_list_t *);
pniam_result_t pniam_item_add2list(pniam_item_list_t **,const char *,
                                   unsigned char *, int, unsigned int);

pniam_item_t  *pniam_item_list_getnext(pniam_item_list_t *,pniam_item_t *);
/*                 return NULL if data is unavailable                   */
pniam_item_t  *pniam_item_list_find(pniam_item_list_t *,const char *key);
/* key is the name field of pniam_item struct                           */
void pniam_item_list_delitem(pniam_item_list_t **,pniam_item_t *);
/* Remove item from list and free item memory                           */

pniam_result_t pniam_start(const char *appname, struct pniam_handle **);
pniam_result_t pniam_create_request(struct pniam_handle *,pniam_request_t **);
void           pniam_destroy_request(struct pniam_handle *,pniam_request_t *,
		   unsigned int flags);
pniam_result_t pniam_end(struct pniam_handle *);
pniam_result_t pniam_authenticate(struct pniam_handle *, struct pniam_request *);
pniam_result_t pniam_authorize(struct pniam_handle *, struct pniam_request *);
pniam_result_t pniam_account_start(struct pniam_handle *, struct pniam_request *);
pniam_result_t pniam_account_end(struct pniam_handle *, struct pniam_request *);
pniam_result_t pniam_change(struct pniam_handle *, struct pniam_request *);

Skąd ściągnąć.

Primary site: ftp://ftp.nc.orc.ru/pub/Linux/pniam/pniam-0.02.tgz

Nieoficjaly "mirror" oraz mój patch: ftp://stardust.open.net.pl/pub/stangrze/pniam/ (moduł pniam_rootok oraz funkcja pniam_dump_request):

Dokumentacja: http://www.msu.ru/pniam/pniam.html


©1998, 1999 PLD Team :-)

Jeśli masz jakieś uwagi, propozycje lub pytania dotyczące tych stron - napisz pod adres pagaj@shadow.eu.org