📄 mod_wrap2.c
字号:
/* * ProFTPD: mod_wrap2 -- tcpwrappers-like access control * * Copyright (c) 2000-2007 TJ Saunders * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * As a special exemption, TJ Saunders gives permission to link this program * with OpenSSL, and distribute the resulting executable, without including * the source code for OpenSSL in the source distribution. */#include "mod_wrap2.h"typedef struct regtab_obj { struct regtab_obj *next; /* Table source type name */ const char *regtab_name; /* Initialization function for this type of table source */ wrap2_table_t *(*regtab_open)(pool *, char *);} wrap2_regtab_t;module wrap2_module;/* Wrap tables for the current session */static char *wrap2_allow_table = NULL;static char *wrap2_deny_table = NULL;/* Memory pool for this module */static pool *wrap2_pool = NULL;/* List of registered quotatab sources */static wrap2_regtab_t *wrap2_regtab_list = NULL;/* Logging data */static int wrap2_logfd = -1;static char *wrap2_logname = NULL;static unsigned char wrap2_engine = FALSE;static char *wrap2_service_name = WRAP2_DEFAULT_SERVICE_NAME;static char *wrap2_client_name = NULL;static config_rec *wrap2_ctxt = NULL;/* Access check variables */#define WRAP2_UNKNOWN "unknown"#define WRAP2_PARANOID "paranoid"#define WRAP2_IS_KNOWN_HOSTNAME(s) \ (strcasecmp((s), WRAP2_UNKNOWN) != 0 && strcasecmp((s), WRAP2_PARANOID))#define WRAP2_IS_NOT_INADDR(s) \ (s[strspn(s,"01234567890./")] != 0)#define WRAP2_GET_DAEMON(r) \ ((r)->daemon)/* Data structures */typedef struct host_info { char name[WRAP2_BUFFER_SIZE]; char addr[WRAP2_BUFFER_SIZE]; struct sockaddr_in *sin; /* socket address or 0 */ struct t_unitdata *unit; /* TLI transport address or 0 */ struct conn_info *connection; /* for shared information */} wrap2_host_t;typedef struct conn_info { int sock_fd; /* socket handle */ char user[WRAP2_BUFFER_SIZE]; /* access via eval_user(request) */ char daemon[WRAP2_BUFFER_SIZE]; /* access via eval_daemon(request) */ struct host_info client[1]; /* client endpoint info */ struct host_info server[1]; /* server endpoint info */ void (*sink) (int); /* datagram sink function or 0 */ void (*hostname) (struct host_info *); /* address to printable hostname */ void (*hostaddr) (struct host_info *); /* address to printable address */ void (*cleanup) (struct conn_info *); /* cleanup function or 0 */ struct netconfig *config; /* netdir handle */} wrap2_conn_t;#define WRAP2_CONN_SOCK_FD 1 /* socket descriptor */#define WRAP2_CONN_DAEMON 2 /* server process (argv[0]) */#ifndef INADDR_NONE#define INADDR_NONE 0xffffffff#endif/* Logging routines */static int wrap2_closelog(void) { if (wrap2_logfd != -1) { close(wrap2_logfd); wrap2_logfd = -1; wrap2_logname = NULL; } return 0;}int wrap2_log(const char *fmt, ...) { char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'}; time_t timestamp = time(NULL); struct tm *t = NULL; va_list msg; if (!wrap2_logname) return 0; t = pr_localtime(NULL, ×tamp); /* Prepend the timestamp. */ strftime(buf, sizeof(buf), "%b %d %H:%M:%S ", t); buf[sizeof(buf) - 1] = '\0'; /* Prepend a small header */ snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), MOD_WRAP2_VERSION "[%u]: ", (unsigned int) getpid()); buf[sizeof(buf) - 1] = '\0'; /* Affix the message. */ va_start(msg, fmt); vsnprintf(buf + strlen(buf), sizeof(buf) - strlen(buf), fmt, msg); va_end(msg); buf[strlen(buf)] = '\n'; buf[sizeof(buf) - 1] = '\0'; while (write(wrap2_logfd, buf, strlen(buf)) < 0) { if (errno == EINTR) { pr_signals_handle(); continue; } return -1; } return 0;}static int wrap2_openlog(void) { int res = 0; /* Sanity check */ wrap2_logname = get_param_ptr(main_server->conf, "WrapLog", FALSE); if (wrap2_logname == NULL) return 0; /* Check for "none" */ if (strcasecmp(wrap2_logname, "none") == 0) { wrap2_logname = NULL; return 0; } pr_signals_block(); PRIVS_ROOT res = pr_log_openfile(wrap2_logname, &wrap2_logfd, 0640); PRIVS_RELINQUISH pr_signals_unblock(); return res;}/* Table routines */static int wrap2_close_table(wrap2_table_t *tab) { return tab->tab_close(tab);}static wrap2_table_t *wrap2_open_table(char *name) { char *info = NULL; unsigned char have_type = FALSE; register wrap2_regtab_t *regtab = NULL; wrap2_table_t *tab = NULL; info = strchr(name, ':'); *info++ = '\0'; /* Look up the table source open routine by name, and invoke it */ for (regtab = wrap2_regtab_list; regtab; regtab = regtab->next) { if (strcmp(regtab->regtab_name, name) == 0) { tab = regtab->regtab_open(wrap2_pool, info); if (tab == NULL) return NULL; have_type = TRUE; break; } } if (!have_type) { wrap2_log("unsupported table source: '%s'", name); errno = EINVAL; return NULL; } return tab;}/* Information structure routines */static wrap2_conn_t *wrap2_conn_update(wrap2_conn_t *conn, va_list ap) { int key = 0; char *val = NULL; while ((key = va_arg(ap, int)) > 0) { switch (key) { default: wrap2_log("invalid key: %d", key); return conn; case WRAP2_CONN_SOCK_FD: conn->sock_fd = va_arg(ap, int); continue;#if 0 case WRAP2_CONN_CLIENT_SIN: conn->client->sin = va_arg(ap, struct sockaddr_in *); continue; case WRAP2_CONN_SERVER_SIN: conn->server->sin = va_arg(ap, struct sockaddr_in *); continue;#endif case WRAP2_CONN_DAEMON: val = conn->daemon; break;#if 0 case WRAP2_CONN_USER: val = conn->user; break; case WRAP2_CONN_CLIENT_NAME: val = conn->client->name; break; case WRAP2_CONN_CLIENT_ADDR: val = conn->client->addr; break; case WRAP2_CONN_SERVER_NAME: val = conn->server->name; break; case WRAP2_CONN_SERVER_ADDR: val = conn->server->addr; break;#endif } /* Copy in the string */ sstrncpy(val, va_arg(ap, char *), WRAP2_BUFFER_SIZE); } return conn;}static wrap2_conn_t *wrap2_conn_set(wrap2_conn_t *conn, ...) { static wrap2_conn_t default_conn; wrap2_conn_t *c = NULL; va_list ap; /* Initialize the data members. We do not assign default callbacks, * to avoid pulling in the whole socket module when it is not really * needed. */ va_start(ap, conn); *conn = default_conn; conn->sock_fd = -1; sstrncpy(conn->daemon, WRAP2_UNKNOWN, sizeof(conn->daemon)); conn->client->connection = conn; conn->server->connection = conn; c = wrap2_conn_update(conn, ap); va_end(ap); return c;}static char *wrap2_get_user(wrap2_conn_t *conn) { if (*conn->user == '\0') { /* RFC931 lookups may have already been done by the daemon. Check * to see what session.ident_lookups is. If TRUE, use the RFC931 name * in session.ident_user. Otherwise, use the user name issued by the * client, C_USER. */ if (session.ident_lookups) { sstrncpy(conn->user, session.ident_user, sizeof(conn->user)); } else { char *user = get_param_ptr(main_server->conf, C_USER, FALSE); sstrncpy(conn->user, user, sizeof(conn->user)); } } return conn->user;}static char *wrap2_get_hostaddr(wrap2_host_t *host) { if (*host->addr == '\0') sstrncpy(host->addr, pr_netaddr_get_ipstr(session.c->remote_addr), sizeof(host->addr)); return host->addr;}static char *wrap2_get_hostname(wrap2_host_t *host) { if (*host->name == '\0') sstrncpy(host->name, session.c->remote_name, sizeof(host->name)); return host->name;}static char *wrap2_get_hostinfo(wrap2_host_t *host) { char *hostname = wrap2_get_hostname(host); if (WRAP2_IS_KNOWN_HOSTNAME(hostname)) return host->name; return wrap2_get_hostaddr(host);}static char *wrap2_get_client(wrap2_conn_t *conn) { static char both[WRAP2_BUFFER_SIZE] = {'\0'}; char *hostinfo = wrap2_get_hostinfo(conn->client); if (strcasecmp(wrap2_get_user(conn), WRAP2_UNKNOWN) != 0) { snprintf(both, sizeof(both), "%s@%s", conn->user, hostinfo); both[sizeof(both)-1] = '\0'; return both; } return hostinfo;}/* Access checking routines */char *wrap2_strsplit(char *str, int delim) { char *tmp = NULL; tmp = strchr(str, delim); if (tmp != NULL) *tmp++ = '\0'; return tmp;}static char *wrap2_skip_whitespace(char *str) { register unsigned int i; char *tmp; /* Skip any leading whitespace. */ tmp = str; for (i = 0; str[i]; i++) { if (isspace((int) str[i])) { tmp = &str[i+1]; continue; } break; } return tmp;}static unsigned char wrap2_match_string(const char *tok, const char *str) { size_t len = 0; if (tok[0] == '.') { /* Suffix */ len = strlen(str) - strlen(tok); return (len > 0 && (strcasecmp(tok, str + len) == 0)); } else if (strcasecmp(tok, "ALL") == 0) { /* ALL: match any */ return TRUE; } else if (strcasecmp(tok, "KNOWN") == 0) { /* Not unknown */ return (strcasecmp(str, WRAP2_UNKNOWN) != 0); } else if (tok[(len = strlen(tok)) - 1] == '.') { /* Prefix */ return (strncasecmp(tok, str, len) == 0); } else { /* Exact match */ return (strcasecmp(tok, str) == 0); } return FALSE;}static unsigned long wrap2_addr_a2n(const char *str) { unsigned char within_run = FALSE; unsigned int nruns = 0; const char *cp = str; /* Count the number of runs of non-dot characters. */ while (*cp) { if (*cp == '.') { within_run = FALSE; } else if (within_run == FALSE) { within_run = TRUE; nruns++; } cp++; } return (nruns == 4 ? inet_addr(str) : INADDR_NONE);}static unsigned char wrap2_match_netmask(const char *net_tok, const char *mask_tok, const char *str) { unsigned long net = 0UL; unsigned long mask = 0UL; unsigned long addr = 0UL; /* Disallow forms other than dotted quad: the treatment that inet_addr() * gives to forms with less than four components is inconsistent with the * access control language. John P. Rouillard <rouilj@cs.umb.edu>. */ if ((addr = wrap2_addr_a2n(str)) == INADDR_NONE) return FALSE; if ((net = wrap2_addr_a2n(net_tok)) == INADDR_NONE || (mask = wrap2_addr_a2n(mask_tok)) == INADDR_NONE) { wrap2_log("warning: bad net/mask expression: '%s/%s'", net_tok, mask_tok); return FALSE; } return ((addr & mask) == net);}static unsigned char wrap2_match_host(char *tok, wrap2_host_t *host) { char *mask = NULL; tok = wrap2_skip_whitespace(tok); /* This code looks a little hairy because we want to avoid unnecessary * hostname lookups. * * The KNOWN pattern requires that both address AND name be known; some * patterns are specific to host names or to host addresses; all other * patterns are satisfied when either the address OR the name match. */ if (tok[0] == '@') {#ifdef WRAP2_USE_NIS /* netgroup: look it up. */ static char *mydomain = NULL; if (mydomain == NULL) yp_get_default_domain(&mydomain); return (innetgr(tok + 1, wrap2_get_hostname(host), NULL, mydomain));#else wrap2_log("warning: '%s': NIS support is not enabled", tok); return FALSE;#endif } else if (strcasecmp(tok, "KNOWN") == 0) { /* Check address and name. */ char *name = wrap2_get_hostname(host); return ((strcasecmp(wrap2_get_hostaddr(host), WRAP2_UNKNOWN) != 0) && WRAP2_IS_KNOWN_HOSTNAME(name)); } else if (strcasecmp(tok, "LOCAL") == 0) { /* Local: no dots in name. */ char *name = wrap2_get_hostname(host); return (strchr(name, '.') == NULL && WRAP2_IS_KNOWN_HOSTNAME(name));#ifdef PR_USE_IPV6 } else if (pr_netaddr_use_ipv6() && *tok == '[') { char *cp, *tmp; pr_netaddr_t *acl_addr; unsigned int nmaskbits; /* IPv6 address */ if (pr_netaddr_get_family(session.c->remote_addr) == AF_INET) /* No need to try to match an IPv6 address against an IPv4 client. */ return FALSE; /* Find the terminating ']'. */ cp = strchr(tok, ']'); if (cp) *cp = '\0'; /* Lookup a netaddr for the IPv6 address. */ acl_addr = pr_netaddr_get_addr(wrap2_pool, tok, NULL); if (!acl_addr) { wrap2_log("unable to resolve IPv6 address '%s'", tok); return FALSE; } /* Determine the number of mask bits. */ nmaskbits = strtol(cp + 1, &tmp, 10); if (tmp && *tmp) { wrap2_log("bad mask syntax: '%s'", tmp); return FALSE; } return (pr_netaddr_ncmp(session.c->remote_addr, acl_addr, nmaskbits) == 0);#endif /* PR_USE_IPV6 */ } else if ((mask = wrap2_strsplit(tok, '/')) != 0) { /* Net/mask */ return (wrap2_match_netmask(tok, mask, wrap2_get_hostaddr(host))); } else { /* Anything else */ return (wrap2_match_string(tok, wrap2_get_hostaddr(host)) || (WRAP2_IS_NOT_INADDR(tok) && wrap2_match_string(tok, wrap2_get_hostname(host)))); } return FALSE;}static unsigned char wrap2_match_client(char *tok, wrap2_conn_t *conn) { unsigned char match = FALSE; char *host = NULL; host = wrap2_strsplit(tok + 1, '@'); if (host == 0) { /* Plain host */ match = wrap2_match_host(tok, conn->client); if (match) wrap2_log("client matches '%s'", tok);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -