diff options
Diffstat (limited to 'source3/lib/access.c')
-rw-r--r-- | source3/lib/access.c | 601 |
1 files changed, 267 insertions, 334 deletions
diff --git a/source3/lib/access.c b/source3/lib/access.c index 14a84b2fb4..f12ee92799 100644 --- a/source3/lib/access.c +++ b/source3/lib/access.c @@ -1,147 +1,162 @@ /* -This module is an adaption of code from the tcpd-1.4 package written -by Wietse Venema, Eindhoven University of Technology, The Netherlands. + This module is an adaption of code from the tcpd-1.4 package written + by Wietse Venema, Eindhoven University of Technology, The Netherlands. -The code is used here with permission. + The code is used here with permission. -The code has been considerably changed from the original. Bug reports -should be sent to Andrew.Tridgell@anu.edu.au + The code has been considerably changed from the original. Bug reports + should be sent to samba@samba.org */ #include "includes.h" -#include "loadparm.h" -#define ALLOW_PURE_ADDRESSES - -extern int DEBUGLEVEL; - -#ifndef INADDR_NONE -#define INADDR_NONE ((unsigned long)~0) -#endif - - -#define FROM_ADDRLEN (4*3+3+1) -#define Good True -#define Bad False - -#define CLIENT_MATCH client_match - -/* Delimiters for lists of daemons or clients. */ - -static char sep[] = ", \t"; - -/* Constants to be used in assignments only, not in comparisons... */ - -#define YES 1 -#define NO 0 #define FAIL (-1) -/* Forward declarations. */ -BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client); -static int list_match(char *list,char *item, int (*match_fn)()); -static int client_match(char *tok,char *item); -static int string_match(char *tok,char *s); -static int masked_match(char *tok, char *slash, char *s); -static int matchname(char *remotehost,struct in_addr addr); -BOOL fromhost(int sock,struct from_host *f); - - -/* Size of logical line buffer. */ -#define BUFLEN 2048 +#define ALLONES ((uint32)0xFFFFFFFF) +/* masked_match - match address against netnumber/netmask */ +static int masked_match(char *tok, char *slash, char *s) +{ + uint32 net; + uint32 mask; + uint32 addr; + + if ((addr = interpret_addr(s)) == INADDR_NONE) + return (False); + *slash = 0; + net = interpret_addr(tok); + *slash = '/'; + + if (strlen(slash + 1) > 2) { + mask = interpret_addr(slash + 1); + } else { + mask = (uint32)((ALLONES >> atoi(slash + 1)) ^ ALLONES); + } + + if (net == INADDR_NONE || mask == INADDR_NONE) { + DEBUG(0,("access: bad net/mask access control: %s\n", tok)); + return (False); + } + return ((addr & mask) == net); +} -/* return true if access should be allowed to a service*/ -BOOL check_access(int snum) +/* string_match - match string against token */ +static int string_match(char *tok,char *s, char *invalid_char) { - extern int Client; - extern struct from_host Client_info; - char *denyl,*allowl; - BOOL ret = False; + size_t tok_len; + size_t str_len; + char *cut; + + *invalid_char = '\0'; + + /* Return True if a token has the magic value "ALL". Return + * FAIL if the token is "FAIL". If the token starts with a "." + * (domain name), return True if it matches the last fields of + * the string. If the token has the magic value "LOCAL", + * return True if the string does not contain a "." + * character. If the token ends on a "." (network number), + * return True if it matches the first fields of the + * string. If the token begins with a "@" (netgroup name), + * return True if the string is a (host) member of the + * netgroup. Return True if the token fully matches the + * string. If the token is a netnumber/netmask pair, return + * True if the address is a member of the specified subnet. + */ + + if (tok[0] == '.') { /* domain: match last fields */ + if ((str_len = strlen(s)) > (tok_len = strlen(tok)) + && strcasecmp(tok, s + str_len - tok_len) == 0) + return (True); + } else if (tok[0] == '@') { /* netgroup: look it up */ +#ifdef HAVE_NETGROUP + static char *mydomain = NULL; + char *hostname = NULL; + BOOL netgroup_ok = False; + + if (!mydomain) yp_get_default_domain(&mydomain); + + if (!mydomain) { + DEBUG(0,("Unable to get default yp domain.\n")); + return False; + } + if (!(hostname = strdup(s))) { + DEBUG(1,("out of memory for strdup!\n")); + return False; + } + + netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); + + DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n", + hostname, + mydomain, + tok+1, + BOOLSTR(netgroup_ok))); + + SAFE_FREE(hostname); + + if (netgroup_ok) return(True); +#else + DEBUG(0,("access: netgroup support is not configured\n")); + return (False); +#endif + } else if (strcasecmp(tok, "ALL") == 0) { /* all: match any */ + return (True); + } else if (strcasecmp(tok, "FAIL") == 0) { /* fail: match any */ + return (FAIL); + } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */ + if (strchr_m(s, '.') == 0 && strcasecmp(s, "unknown") != 0) + return (True); + } else if (!strcasecmp(tok, s)) { /* match host name or address */ + return (True); + } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */ + if (strncmp(tok, s, tok_len) == 0) + return (True); + } else if ((cut = strchr_m(tok, '/')) != 0) { /* netnumber/netmask */ + if (isdigit((int)s[0]) && masked_match(tok, cut, s)) + return (True); + } else if (strchr_m(tok, '*') != 0) { + *invalid_char = '*'; + } else if (strchr_m(tok, '?') != 0) { + *invalid_char = '?'; + } + return (False); +} - denyl = lp_hostsdeny(snum); - if (denyl) denyl = strdup(denyl); - allowl = lp_hostsallow(snum); - if (allowl) allowl = strdup(allowl); +/* client_match - match host name and address against token */ +static int client_match(char *tok,char *item) +{ + char **client = (char **)item; + int match; + char invalid_char = '\0'; + /* + * Try to match the address first. If that fails, try to match the host + * name if available. + */ - fromhost(Client,&Client_info); + if ((match = string_match(tok, client[1], &invalid_char)) == 0) { + if(invalid_char) + DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \ +token '%s' in an allow/deny hosts line.\n", invalid_char, tok )); - if ((!denyl || *denyl==0) && (!allowl || *allowl==0)) - ret = True; + if (client[0][0] != 0) + match = string_match(tok, client[0], &invalid_char); - if (!ret) - { - if (!fromhost(Client,&Client_info)) - DEBUG(0,("ERROR: Can't get from_host info\n")); - else - { - if (allow_access(denyl,allowl,&Client_info)) - { - if (snum >= 0) - DEBUG(2,("Allowed connection from %s (%s) to %s\n", - Client_info.name,Client_info.addr, - lp_servicename(snum))); - ret = True; - } - else - if (snum >= 0) - DEBUG(0,("Denied connection from %s (%s) to %s\n", - Client_info.name,Client_info.addr, - lp_servicename(snum))); + if(invalid_char) + DEBUG(0,("client_match: address match failing due to invalid character '%c' found in \ +token '%s' in an allow/deny hosts line.\n", invalid_char, tok )); } - } - - if (denyl) free(denyl); - if (allowl) free(allowl); - return(ret); -} - -/* return true if access should be allowed */ -BOOL allow_access(char *deny_list,char *allow_list,struct from_host *client) -{ - /* if theres no deny list and no allow list then allow access */ - if ((!deny_list || *deny_list == 0) && (!allow_list || *allow_list == 0)) - return(True); - - /* if there is an allow list but no deny list then allow only hosts - on the allow list */ - if (!deny_list || *deny_list == 0) - return(list_match(allow_list,(char *)client,CLIENT_MATCH)); - - /* if theres a deny list but no allow list then allow - all hosts not on the deny list */ - if (!allow_list || *allow_list == 0) - return(!list_match(deny_list,(char *)client,CLIENT_MATCH)); - - /* if there are both type of list then allow all hosts on the allow list */ - if (list_match(allow_list,(char *)client,CLIENT_MATCH)) - return (True); - - /* if there are both type of list and it's not on the allow then - allow it if its not on the deny */ - if (list_match(deny_list,(char *)client,CLIENT_MATCH)) - return (False); - - return (True); + return (match); } /* list_match - match an item against a list of tokens with exceptions */ -/* (All modifications are marked with the initials "jkf") */ -static int list_match(char *list,char *item, int (*match_fn)()) +static int list_match(char **list,char *item, int (*match_fn)(char *, char *)) { - char *tok; - char *listcopy; /* jkf */ - int match = NO; - - /* - * jkf@soton.ac.uk -- 31 August 1994 -- Stop list_match() - * overwriting the list given as its first parameter. - */ + int match = False; - /* jkf -- can get called recursively with NULL list */ - listcopy = (list == 0) ? (char *)0 : strdup(list); + if (!list) return False; /* * Process tokens one at a time. We have exhausted all possible matches @@ -150,240 +165,158 @@ static int list_match(char *list,char *item, int (*match_fn)()) * the match is affected by any exceptions. */ - for (tok = strtok(listcopy, sep); tok ; tok = strtok(NULL, sep)) { - if (strcasecmp(tok, "EXCEPT") == 0) /* EXCEPT: give up */ + for (; *list ; list++) { + if (strcasecmp(*list, "EXCEPT") == 0) /* EXCEPT: give up */ break; - if ((match = (*match_fn) (tok, item))) /* YES or FAIL */ + if ((match = (*match_fn) (*list, item))) /* True or FAIL */ break; } - /* Process exceptions to YES or FAIL matches. */ - - if (match != NO) { - while ((tok = strtok((char *) 0, sep)) && strcasecmp(tok, "EXCEPT")) - /* VOID */ ; - if (tok == 0 || list_match((char *) 0, item, match_fn) == NO) { - if (listcopy != 0) free(listcopy); /* jkf */ - return (match); - } - } - - if (listcopy != 0) free(listcopy); /* jkf */ - return (NO); -} + /* Process exceptions to True or FAIL matches. */ + if (match != False) { + while (*list && strcasecmp(*list, "EXCEPT")) + list++; -/* client_match - match host name and address against token */ -static int client_match(char *tok,char *item) -{ - struct from_host *client = (struct from_host *) item; - int match; - - /* - * Try to match the address first. If that fails, try to match the host - * name if available. - */ + for (; *list; list++) { + if ((*match_fn) (*list, item)) /* Exception Found */ + return False; + } + } - if ((match = string_match(tok, client->addr)) == 0) - if (client->name[0] != 0) - match = string_match(tok, client->name); return (match); } -/* string_match - match string against token */ -static int string_match(char *tok,char *s) -{ - int tok_len; - int str_len; - char *cut; - - /* - * Return YES if a token has the magic value "ALL". Return FAIL if the - * token is "FAIL". If the token starts with a "." (domain name), return - * YES if it matches the last fields of the string. If the token has the - * magic value "LOCAL", return YES if the string does not contain a "." - * character. If the token ends on a "." (network number), return YES if - * it matches the first fields of the string. If the token begins with a - * "@" (netgroup name), return YES if the string is a (host) member of - * the netgroup. Return YES if the token fully matches the string. If the - * token is a netnumber/netmask pair, return YES if the address is a - * member of the specified subnet. - */ - if (tok[0] == '.') { /* domain: match last fields */ - if ((str_len = strlen(s)) > (tok_len = strlen(tok)) - && strcasecmp(tok, s + str_len - tok_len) == 0) - return (YES); - } else if (tok[0] == '@') { /* netgroup: look it up */ -#ifdef NETGROUP - static char *mydomain = NULL; - char *hostname = NULL; - BOOL netgroup_ok = False; - - if (!mydomain) yp_get_default_domain(&mydomain); - - if (!(hostname = strdup(s))) { - DEBUG(1,("out of memory for strdup!\n")); - return NO; - } - - netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); - - DEBUG(5,("looking for %s of domain %s in netgroup %s gave %s\n", - hostname, - mydomain, - tok+1, - BOOLSTR(netgroup_ok))); - -#ifdef NETGROUP_INSECURE - /* if you really want netgroups that match non qualified names - then define NETGROUP_INSECURE. It can, however, be a big - security hole */ - { - char *clnt_domain; - if (!netgroup_ok && (clnt_domain=strchr(hostname,'.'))) { - *clnt_domain++ = '\0'; - netgroup_ok = innetgr(tok + 1, hostname, (char *) 0, mydomain); +/* return true if access should be allowed */ +BOOL allow_access(char **deny_list,char **allow_list, + char *cname,char *caddr) +{ + char *client[2]; + + client[0] = cname; + client[1] = caddr; + + /* if it is loopback then always allow unless specifically denied */ + if (strcmp(caddr, "127.0.0.1") == 0) { + /* + * If 127.0.0.1 matches both allow and deny then allow. + * Patch from Steve Langasek vorlon@netexpress.net. + */ + if (deny_list && + list_match(deny_list,(char *)client,client_match) && + (!allow_list || + !list_match(allow_list,(char *)client, client_match))) { + return False; + } + return True; } - } -#endif - free(hostname); - - if (netgroup_ok) return(YES); -#else - DEBUG(0,("access: netgroup support is not configured")); - return (NO); -#endif - } else if (strcasecmp(tok, "ALL") == 0) { /* all: match any */ - return (YES); - } else if (strcasecmp(tok, "FAIL") == 0) { /* fail: match any */ - return (FAIL); - } else if (strcasecmp(tok, "LOCAL") == 0) { /* local: no dots */ - if (strchr(s, '.') == 0 && strcasecmp(s, "unknown") != 0) - return (YES); - } else if (!strcasecmp(tok, s)) { /* match host name or address */ - return (YES); - } else if (tok[(tok_len = strlen(tok)) - 1] == '.') { /* network */ - if (strncmp(tok, s, tok_len) == 0) - return (YES); - } else if ((cut = strchr(tok, '/')) != 0) { /* netnumber/netmask */ - if (isdigit(s[0]) && masked_match(tok, cut, s)) - return (YES); - } - return (NO); -} + /* if theres no deny list and no allow list then allow access */ + if ((!deny_list || *deny_list == 0) && + (!allow_list || *allow_list == 0)) { + return(True); + } -/* masked_match - match address against netnumber/netmask */ -static int masked_match(char *tok, char *slash, char *s) -{ - unsigned long net; - unsigned long mask; - unsigned long addr; - - if ((addr = interpret_addr(s)) == INADDR_NONE) - return (NO); - *slash = 0; - net = interpret_addr(tok); - *slash = '/'; - if (net == INADDR_NONE || (mask = interpret_addr(slash + 1)) == INADDR_NONE) { - DEBUG(0,("access: bad net/mask access control: %s", tok)); - return (NO); - } - return ((addr & mask) == net); + /* if there is an allow list but no deny list then allow only hosts + on the allow list */ + if (!deny_list || *deny_list == 0) + return(list_match(allow_list,(char *)client,client_match)); + + /* if theres a deny list but no allow list then allow + all hosts not on the deny list */ + if (!allow_list || *allow_list == 0) + return(!list_match(deny_list,(char *)client,client_match)); + + /* if there are both types of list then allow all hosts on the + allow list */ + if (list_match(allow_list,(char *)client,client_match)) + return (True); + + /* if there are both types of list and it's not on the allow then + allow it if its not on the deny */ + if (list_match(deny_list,(char *)client,client_match)) + return (False); + + return (True); } - -/* fromhost - find out what is at the other end of a socket */ -BOOL fromhost(int sock,struct from_host *f) +/* return true if the char* contains ip addrs only. Used to avoid +gethostbyaddr() calls */ +static BOOL only_ipaddrs_in_list(char** list) { - static struct sockaddr sa; - struct sockaddr_in *sockin = (struct sockaddr_in *) (&sa); - struct hostent *hp; - int length = sizeof(sa); - static char addr_buf[FROM_ADDRLEN]; - static char name_buf[MAXHOSTNAMELEN]; - BOOL takeAddressAsHostname = False; - - if (getpeername(sock, &sa, &length) < 0) - { - DEBUG(0,("getpeername failed\n")); - return(False); - } - - f->sin = sockin; - f->addr = strcpy(addr_buf,(char *)inet_ntoa(sockin->sin_addr)); - - /* Look up the remote host name. */ - if ((hp = gethostbyaddr((char *) &sockin->sin_addr, - sizeof(sockin->sin_addr), - AF_INET)) == 0) { - DEBUG(1,("Gethostbyaddr failed for %s\n",addr_buf)); -#ifdef ALLOW_PURE_ADDRESSES - takeAddressAsHostname = True; -#else - return(False); -#endif - } - - /* Save the host name. A later gethostbyxxx() call may clobber it. */ - f->name = StrnCpy(name_buf, - takeAddressAsHostname? f->addr : hp->h_name, - sizeof(name_buf) - 1); - - /* - * Verify that the host name does not belong to someone else. If host - * name verification fails, pretend that the host name lookup failed. - */ - if (!takeAddressAsHostname && !matchname(f->name, sockin->sin_addr)) - { - DEBUG(0,("Matchname failed\n")); - return(False); - } - - return(True); + BOOL only_ip = True; + + if (!list) return True; + + for (; *list ; list++) + { + /* factor out the special strings */ + if (!strcasecmp(*list, "ALL") || !strcasecmp(*list, "FAIL") || + !strcasecmp(*list, "EXCEPT")) + { + continue; + } + + if (!is_ipaddress(*list)) + { + char *p; + /* + * if we failed, make sure that it was not because the token + * was a network/netmask pair. Only network/netmask pairs + * have a '/' in them + */ + if ((p=strchr_m(*list, '/')) == NULL) + { + only_ip = False; + DEBUG(3,("only_ipaddrs_in_list: list has non-ip address (%s)\n", *list)); + break; + } + } + } + + return only_ip; } -/* matchname - determine if host name matches IP address */ -static int matchname(char *remotehost,struct in_addr addr) +/* return true if access should be allowed to a service for a socket */ +BOOL check_access(int sock, char **allow_list, char **deny_list) { - struct hostent *hp; - int i; - - if ((hp = Get_Hostbyname(remotehost)) == 0) { - DEBUG(0,("Get_Hostbyname(%s): lookup failure", remotehost)); - return (Bad); - } - - /* - * Make sure that gethostbyname() returns the "correct" host name. - * Unfortunately, gethostbyname("localhost") sometimes yields - * "localhost.domain". Since the latter host name comes from the - * local DNS, we just have to trust it (all bets are off if the local - * DNS is perverted). We always check the address list, though. - */ - - if (strcasecmp(remotehost, hp->h_name) - && strcasecmp(remotehost, "localhost")) { - DEBUG(0,("host name/name mismatch: %s != %s", - remotehost, hp->h_name)); - return (Bad); - } + BOOL ret = False; + BOOL only_ip = False; - /* Look up the host address in the address list we just got. */ - for (i = 0; hp->h_addr_list[i]; i++) { - if (memcmp(hp->h_addr_list[i], (caddr_t) & addr, sizeof(addr)) == 0) - return (Good); - } - - /* - * The host name does not map to the original host address. Perhaps - * someone has compromised a name server. More likely someone botched - * it, but that could be dangerous, too. - */ - - DEBUG(0,("host name/address mismatch: %s != %s", - inet_ntoa(addr), hp->h_name)); - return (Bad); -} + if ((!deny_list || *deny_list==0) && (!allow_list || *allow_list==0)) + { + ret = True; + } + if (!ret) + { + /* bypass gethostbyaddr() calls if the lists only contain IP addrs */ + if (only_ipaddrs_in_list(allow_list) && only_ipaddrs_in_list(deny_list)) + { + only_ip = True; + DEBUG (3, ("check_access: no hostnames in host allow/deny list.\n")); + ret = allow_access(deny_list,allow_list, "", get_socket_addr(sock)); + } + else + { + DEBUG (3, ("check_access: hostnames in host allow/deny list.\n")); + ret = allow_access(deny_list,allow_list, get_socket_name(sock), + get_socket_addr(sock)); + } + + if (ret) + { + DEBUG(2,("Allowed connection from %s (%s)\n", + only_ip ? "" : get_socket_name(sock), + get_socket_addr(sock))); + } + else + { + DEBUG(0,("Denied connection from %s (%s)\n", + only_ip ? "" : get_socket_name(sock), + get_socket_addr(sock))); + } + } + return(ret); +} |