- předchozí článek - následující článek - obsah - úvodní stránka -

Linuxové noviny 01-02/2001

Programování s tcp_wrappers

David Haring, 10. prosince 2000

Balíček tcp_wrappers umožňuje sledovat a filtrovat příchozí spojení řady běžně používaných síťových služeb. Kromě utility tcpd, která se používá pro kontrolu přístupu ke službám ve spojení s inetd obsahuje tcp_wrappers i knihovnu s jednoduchým rozhraním, s jejíž pomocí lze podporu tcp_wrappers zabudovat i do jiných aplikací, které nejsou spouštěny přes "super-server" inetd.

Tcp_wrappers povolují či zamítají přístup ke službám na základě informace o jménu a IP adrese vzdáleného systému, případně i informace o vlastníkovi příchozího spojení na vzdáleném systému. Konfigurace je uložena v souborech /etc/hosts.allow a /etc/hosts.deny a je podrobně popsána v článku o konfiguraci inetd a tcp_wrappers; dále se jí zde nebudeme zabývat. Předmětem tohoto článku bude ukázka použití knihovny libwrap, tedy zabudování podpory tcp_wrappers do "stand-alone" síťových aplikací.

Pro potřeby knihovny libwrap je informace o spojení uložena ve struktuře request_info, kterou je potřeba naplnit potřebnými údaji. Následně aplikace zavolá některou z funkcí pro kontrolu přístupu. Ve struktuře request_info je zejména potřeba naplnit následující položky:

  • RQ_DAEMON: jméno procesu (serveru), který běží na hostitelském počítači
  • RQ_CLIENT_NAME: jméno klientského počítače
  • RQ_CLIENT_ADDR: IP adresa klientského počítače
  • RQ_SERVER_SIN: odkaz na strukturu sockaddr_in, která obsahuje použitou síťovou adresu a port hostitelského počítače (nutné zadat pro automatické zjištění vlastníka příchozího spojení podle RFC 1413)
  • RQ_CLIENT_SIN: odkaz na strukturu sockaddr_in, která obsahuje použitou síťovou adresu a port klientského počítače (nutné zadat pro automatické zjištění vlastníka příchozího spojení podle RFC 1413)

Knihovna libwrap poskytuje pro manipulaci s request_info následující funkce:

  • request_init() pro inicializaci a naplnění struktury request_info, se kterou pracuje hosts_access()
  • request_set() aktualizace obsahu již inicializované struktury request_info.

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <syslog.h>

/* hlavičkový soubor tcp_wrappers */ #include <tcpd.h>

/* číslo portu serveru */ #define PORT_NUM 5000 /* syslog facility */ #define LOG_FACILITY LOG_AUTHPRIV

/* tyto proměnné jsou vyžadovány tcp_wrappers */ int allow_severity=4; int deny_severity=4;

/* deskriptory socketů apod. */ int fd,fd2; struct sockaddr_in serv_addr, client_addr; int client_addr_len; struct hostent *client_info;

/* request_info pro tcp_wrappers */ struct request_info request;

char client_name[80];

/* do_exit() zapíše chybu do logu a ukončí aplikaci */ void do_exit(char *s) { syslog(LOG_WARNING,"%s, exiting.",s); exit(1); }

int main (int argc, char ** argv) { /* argv[0] může být absolutní cesta, potřebujeme jen jméno programu */ if (strrchr(argv[0],'/')) argv[0] = strrchr(argv[0],'/')+1;

/* nastavíme způsob logování */ openlog(argv[0],LOG_PID,LOG_FACILITY);

/* otevíráme socket (TCP) */ if ((fd=socket(AF_INET,SOCK_STREAM,0))<0) do_exit("error in socket()"); /* navážeme se na port */ bzero((char *)&serv_addr,sizeof(serv_addr)); serv_addr.sin_family=AF_INET; serv_addr.sin_port=htons(PORT_NUM); serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); if (bind(fd,&serv_addr,sizeof(serv_addr))<0) do_exit("error in bind()"); if (listen(fd,5)<0) do_exit("error in listen()");

while(1) { client_addr_len=sizeof(client_addr);

/* čekáme na spojení klienta */

if ((fd2=accept(fd,&client_addr,&client_addr_len))<0) do_exit("error in accept()");

/* zjistíme a uložíme jméno klientského počítače */ if (client_info=gethostbyaddr((char *)&(client_addr.sin_addr),\ sizeof(client_addr.sin_addr),AF_INET)) snprintf(client_name, sizeof(client_name)-1,"%s",client_info->h_name); else client_name[0]='\0';

/* naplníme request_info */ /* pokud zadáme i RQ_SERVER_SIN a RQ_CLIENT_SIN, tcp_wrappers */ /* se pokusí identifikovat vlastníka příchozího spojení */

request_init(&request, RQ_DAEMON, argv[0],\ RQ_SERVER_SIN, &serv_addr, RQ_CLIENT_SIN, &client_addr,\ RQ_CLIENT_ADDR, inet_ntoa(client_addr.sin_addr),\ RQ_CLIENT_NAME, client_name, 0);

/* ověříme, zda je přístup povolen */

if (!hosts_access(&request)) { /* spojení zamítnuto tcp_wrappers, zapíšeme do logu */ /* jméno vlastníka příchozího spojení je dostupné přes eval_user() */ syslog(deny_severity, "rejected connection from %s (%s@%s)",\ inet_ntoa(client_addr.sin_addr),eval_user(&request),client_name); /* spojení ukončíme */ close(fd2); } else { /* spojení povoleno tcp_wrappers, zapíšeme do logu */ syslog(allow_severity, "accepted connection from %s (%s@%s)", inet_ntoa(client_addr.sin_addr),eval_user(&request),client_name); /* tady bychom zpracovali požadavek klienta */ /* spojení ukončíme */ close(fd2); } } }

/* překlad: gcc server.c -lwrap -lnsl */

server.c: ukázka použití knihovny libwrap

Pro povolení přístupu ke službě jsou k dispozici následující dvě funkce:

  • hosts_access() povolí nebo zamítne přístup na základě informací

    v request_info, které konfrontuje s konfigurací v souborech hosts.allow a hosts.deny. Návratová hodnota 0 znamená zamítnutí přístupu. Před voláním hosts_access() je třeba inicializovat strukturu request_info.

  • hosts_ctl() povolí nebo zamítne přístup k službě na základě zadaných parametrů. Ve skutečnosti volá request_init() a následně hosts_access(). Na rozdíl od přímého použití request_init() a {hosts_access() neumožňuje automatickou identifikaci vlastníka příchozího spojení a je na nás abychom tuto položku doplnili - pokud ji nechceme použít, doplníme STRING_UNKNOWN. Návratová hodnota 0 znamená zamítnutí přístupu.

Rozhraní knihovny libwrap je popsáno v manuálové stránce hosts_access(3), konfigurace přístupu k službám tcp_wrappers v hosts.access(5). Rovněž zdrojové kódy tcp_wrappers jsou velmi přehledné a komentované.

Uveďme příklad jednoduchého iterativního serveru, komunikujícího přes TCP. Příklad uvádí použití hosts_access() s využitím automatické identifikace vlastníka příchozího spojení knihovnou libwrap.

Shrnutí

S pomocí tcp_wrappers můžeme jednoduchým způsobem kontrolovat a omezit přístup k síťovým službám. Tento způsob je běžně používán nejen v souvislosti se službami spouštěnými přes inetd, ale je využíván i v řadě jiných síťových služeb jako je např. ssh či portmap (NFS apod.). Knihovna libwrap umožňuje jednoduchým způsobem tuto funkčnost začlenit do libovolných síťových aplikací. *


- předchozí článek - následující článek - obsah - úvodní stránka -