X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_conf.c;h=7db9ff2e4722e68136e3532d6cc7cc5446b475df;hb=refs%2Fheads%2Fupstream;hp=43c382862ee89d9bef518c95ea897b10f154c252;hpb=87adb627f71f9157a179c5f0c2f59a8e0ef8db1c;p=ircu2.10.12-pk.git diff --git a/ircd/s_conf.c b/ircd/s_conf.c index 43c3828..7db9ff2 100644 --- a/ircd/s_conf.c +++ b/ircd/s_conf.c @@ -34,7 +34,6 @@ #include "hash.h" #include "ircd.h" #include "ircd_alloc.h" -#include "ircd_auth.h" #include "ircd_chattr.h" #include "ircd_log.h" #include "ircd_reply.h" @@ -49,6 +48,7 @@ #include "opercmds.h" #include "parse.h" #include "res.h" +#include "s_auth.h" #include "s_bsd.h" #include "s_debug.h" #include "s_misc.h" @@ -56,8 +56,7 @@ #include "struct.h" #include "sys.h" -#include -#include +/* #include -- Now using assert in ircd_log.h */ #include #include #include @@ -69,7 +68,7 @@ /** Global list of all ConfItem structures. */ struct ConfItem *GlobalConfList; -/** Count of items in #GlobalConfLis. */ +/** Count of items in #GlobalConfList. */ int GlobalConfCount; /** Global list of service mappings. */ struct s_map *GlobalServiceMapList; @@ -95,7 +94,6 @@ static void killcomment(struct Client* sptr, const char* filename) FBFILE* file = 0; char line[80]; struct stat sb; - struct tm* tm; if (NULL == (file = fbopen(filename, "r"))) { send_reply(sptr, ERR_NOMOTD); @@ -104,7 +102,6 @@ static void killcomment(struct Client* sptr, const char* filename) return; } fbstat(&sb, file); - tm = localtime((time_t*) &sb.st_mtime); /* NetBSD needs cast */ while (fbgets(line, sizeof(line) - 1, file)) { char* end = line + strlen(line); while (end > line) { @@ -130,9 +127,7 @@ struct ConfItem* make_conf(int type) aconf = (struct ConfItem*) MyMalloc(sizeof(struct ConfItem)); assert(0 != aconf); -#ifdef DEBUGMODE ++GlobalConfCount; -#endif memset(aconf, 0, sizeof(struct ConfItem)); aconf->status = type; aconf->next = GlobalConfList; @@ -151,15 +146,16 @@ void free_conf(struct ConfItem *aconf) aconf->address.port)); if (aconf->dns_pending) delete_resolver_queries(aconf); + MyFree(aconf->username); MyFree(aconf->host); + MyFree(aconf->origin_name); if (aconf->passwd) memset(aconf->passwd, 0, strlen(aconf->passwd)); MyFree(aconf->passwd); MyFree(aconf->name); + MyFree(aconf->hub_limit); MyFree(aconf); -#ifdef DEBUGMODE --GlobalConfCount; -#endif } /** Disassociate configuration from the client. @@ -194,19 +190,48 @@ static void detach_conf(struct Client* cptr, struct ConfItem* aconf) } } +/** Parse a user\@host mask into username and host or IP parts. + * If \a host contains no username part, set \a aconf->username to + * NULL. If the host part of \a host looks like an IP mask, set \a + * aconf->addrbits and \a aconf->address to match. Otherwise, set + * \a aconf->host, and set \a aconf->addrbits to -1. + * @param[in,out] aconf Configuration item to set. + * @param[in] host user\@host mask to parse. + */ +void conf_parse_userhost(struct ConfItem *aconf, char *host) +{ + char *host_part; + unsigned char addrbits; + + MyFree(aconf->username); + MyFree(aconf->host); + host_part = strchr(host, '@'); + if (host_part) { + *host_part = '\0'; + DupString(aconf->username, host); + host_part++; + } else { + aconf->username = NULL; + host_part = host; + } + DupString(aconf->host, host_part); + if (ipmask_parse(aconf->host, &aconf->address.addr, &addrbits)) + aconf->addrbits = addrbits; + else + aconf->addrbits = -1; +} + /** Copies a completed DNS query into its ConfItem. * @param vptr Pointer to struct ConfItem for the block. * @param hp DNS reply, or NULL if the lookup failed. */ -static void conf_dns_callback(void* vptr, struct DNSReply* hp) +static void conf_dns_callback(void* vptr, const struct irc_in_addr *addr, const char *h_name) { struct ConfItem* aconf = (struct ConfItem*) vptr; assert(aconf); aconf->dns_pending = 0; - if (hp) { - memcpy(&aconf->address.addr, &hp->addr, sizeof(aconf->address.addr)); - MyFree(hp); - } + if (addr) + memcpy(&aconf->address.addr, addr, sizeof(aconf->address.addr)); } /** Start a nameserver lookup of the conf host. If the conf entry is @@ -217,13 +242,9 @@ static void conf_dns_lookup(struct ConfItem* aconf) { if (!aconf->dns_pending) { char buf[HOSTLEN + 1]; - struct DNSQuery query; - query.vptr = aconf; - query.callback = conf_dns_callback; - host_from_uh(buf, aconf->host, HOSTLEN); - buf[HOSTLEN] = '\0'; - gethost_byname(buf, &query); + host_from_uh(buf, aconf->host, HOSTLEN); + gethost_byname(buf, conf_dns_callback, aconf); aconf->dns_pending = 1; } } @@ -321,88 +342,176 @@ void det_confs_butmask(struct Client* cptr, int mask) } } -/** Check client limits and attach Client block. - * If the password field consists of one or two digits, use that - * as the per-IP connection limit; otherwise use 255. - * @param cptr Client getting \a aconf. - * @param aconf Configuration item to attach. - * @return Authorization check result. - */ -static enum AuthorizationCheckResult -check_limit_and_attach(struct Client* cptr, struct ConfItem* aconf) -{ - int number = 255; - - if (aconf->passwd) { - if (IsDigit(*aconf->passwd) && !aconf->passwd[1]) - number = *aconf->passwd-'0'; - else if (IsDigit(*aconf->passwd) && IsDigit(aconf->passwd[1]) && - !aconf->passwd[2]) - number = (*aconf->passwd-'0')*10+(aconf->passwd[1]-'0'); - } - if (IPcheck_nr(cptr) > number) - return ACR_TOO_MANY_FROM_IP; - return attach_conf(cptr, aconf); -} - /** Find the first (best) Client block to attach. * @param cptr Client for whom to check rules. * @return Authorization check result. */ -enum AuthorizationCheckResult attach_iline(struct Client* cptr) +enum AuthorizationCheckResult attach_iline(struct Client* cptr) { struct ConfItem* aconf; - static char uhost[HOSTLEN + USERLEN + 3]; - static char fullname[HOSTLEN + 1]; - struct DNSReply* hp; assert(0 != cptr); - hp = cli_dns_reply(cptr); for (aconf = GlobalConfList; aconf; aconf = aconf->next) { if (aconf->status != CONF_CLIENT) continue; + /* If you change any of this logic, please make corresponding + * changes in conf_debug_iline() below. + */ if (aconf->address.port && aconf->address.port != cli_listener(cptr)->addr.port) continue; - if (!aconf->host || !aconf->name) + if (aconf->username && match(aconf->username, cli_username(cptr))) + continue; + if (aconf->host && match(aconf->host, cli_sockhost(cptr))) + continue; + if ((aconf->addrbits >= 0) + && !ipmask_check(&cli_ip(cptr), &aconf->address.addr, aconf->addrbits)) continue; - if (hp) { - ircd_strncpy(fullname, hp->h_name, HOSTLEN); - fullname[HOSTLEN] = '\0'; + if (IPcheck_nr(cptr) > aconf->maximum) + return ACR_TOO_MANY_FROM_IP; + if (aconf->username) + SetFlag(cptr, FLAG_DOID); + return attach_conf(cptr, aconf); + } + return ACR_NO_AUTHORIZATION; +} - Debug((DEBUG_DNS, "a_il: %s->%s", cli_sockhost(cptr), fullname)); +/** Interpret \a client as a client specifier and show which Client + * block(s) match that client. + * + * The client specifier may contain an IP address, hostname, listener + * port, or a combination of those separated by commas. IP addresses + * and hostnamese may be preceded by "username@"; the last given + * username will be used for the match. + * + * @param[in] client Client specifier. + * @return Matching Client block structure. + */ +struct ConfItem *conf_debug_iline(const char *client) +{ + struct irc_in_addr address; + struct ConfItem *aconf; + struct DenyConf *deny; + char *sep; + unsigned short listener; + char username[USERLEN+1], hostname[HOSTLEN+1], realname[REALLEN+1]; + + /* Initialize variables. */ + listener = 0; + memset(&address, 0, sizeof(address)); + memset(&username, 0, sizeof(username)); + memset(&hostname, 0, sizeof(hostname)); + memset(&realname, 0, sizeof(realname)); + + /* Parse client specifier. */ + while (*client) { + struct irc_in_addr tmpaddr; + long tmp; + + /* Try to parse as listener port number first. */ + tmp = strtol(client, &sep, 10); + if (tmp && (*sep == '\0' || *sep == ',')) { + listener = tmp; + client = sep + (*sep != '\0'); + continue; + } - if (strchr(aconf->name, '@')) { - strcpy(uhost, cli_username(cptr)); - strcat(uhost, "@"); - } - else - *uhost = '\0'; - strncat(uhost, fullname, sizeof(uhost) - 1 - strlen(uhost)); - uhost[sizeof(uhost) - 1] = 0; - if (0 == match(aconf->name, uhost)) { - if (strchr(uhost, '@')) - SetFlag(cptr, FLAG_DOID); - return check_limit_and_attach(cptr, aconf); + /* Maybe username@ before an IP address or hostname? */ + tmp = strcspn(client, ",@"); + if (client[tmp] == '@') { + if (tmp > USERLEN) + tmp = USERLEN; + ircd_strncpy(username, client, tmp); + /* and fall through */ + client += tmp + 1; + } + + /* Looks like an IP address? */ + tmp = ircd_aton(&tmpaddr, client); + if (tmp && (client[tmp] == '\0' || client[tmp] == ',')) { + memcpy(&address, &tmpaddr, sizeof(address)); + client += tmp + (client[tmp] != '\0'); + continue; + } + + /* Realname? */ + if (client[0] == '$' && client[1] == 'R') { + client += 2; + for (tmp = 0; *client != '\0' && *client != ',' && tmp < REALLEN; ++client, ++tmp) { + if (*client == '\\') + realname[tmp] = *++client; + else + realname[tmp] = *client; } + continue; } - if (strchr(aconf->host, '@')) { - ircd_strncpy(uhost, cli_username(cptr), sizeof(uhost) - 2); - uhost[sizeof(uhost) - 2] = 0; - strcat(uhost, "@"); + + /* Else must be a hostname. */ + tmp = strcspn(client, ","); + if (tmp > HOSTLEN) + tmp = HOSTLEN; + ircd_strncpy(hostname, client, tmp); + client += tmp + (client[tmp] != '\0'); + } + + /* Walk configuration to find matching Client block. */ + for (aconf = GlobalConfList; aconf; aconf = aconf->next) { + if (aconf->status != CONF_CLIENT) + continue; + if (aconf->address.port && aconf->address.port != listener) { + fprintf(stdout, "Listener port mismatch: %u != %u\n", aconf->address.port, listener); + continue; } - else - *uhost = '\0'; - strncat(uhost, cli_sock_ip(cptr), sizeof(uhost) - 1 - strlen(uhost)); - uhost[sizeof(uhost) - 1] = 0; - if (match(aconf->host, uhost)) + if (aconf->username && match(aconf->username, username)) { + fprintf(stdout, "Username mismatch: %s != %s\n", aconf->username, username); continue; - if (strchr(uhost, '@')) - SetFlag(cptr, FLAG_DOID); + } + if (aconf->host && match(aconf->host, hostname)) { + fprintf(stdout, "Hostname mismatch: %s != %s\n", aconf->host, hostname); + continue; + } + if ((aconf->addrbits >= 0) + && !ipmask_check(&address, &aconf->address.addr, aconf->addrbits)) { + fprintf(stdout, "IP address mismatch: %s != %s\n", aconf->name, ircd_ntoa(&address)); + continue; + } + fprintf(stdout, "Match! username=%s host=%s ip=%s class=%s maxlinks=%u password=%s\n", + (aconf->username ? aconf->username : "(null)"), + (aconf->host ? aconf->host : "(null)"), + (aconf->name ? aconf->name : "(null)"), + ConfClass(aconf), aconf->maximum, + (aconf->passwd ? aconf->passwd : "(null)")); + break; + } - return check_limit_and_attach(cptr, aconf); + /* If no authorization, say so and exit. */ + if (!aconf) + { + fprintf(stdout, "No authorization found.\n"); + return NULL; } - return ACR_NO_AUTHORIZATION; + + /* Look for a Kill block with the user's name on it. */ + for (deny = denyConfList; deny; deny = deny->next) { + if (deny->usermask && match(deny->usermask, username)) + continue; + if (deny->realmask && match(deny->realmask, realname)) + continue; + if (deny->bits > 0) { + if (!ipmask_check(&address, &deny->address, deny->bits)) + continue; + } else if (deny->hostmask && match(deny->hostmask, hostname)) + continue; + + /* Looks like a match; report it. */ + fprintf(stdout, "Denied! usermask=%s realmask=\"%s\" hostmask=%s (bits=%u)\n", + deny->usermask ? deny->usermask : "(null)", + deny->realmask ? deny->realmask : "(null)", + deny->hostmask ? deny->hostmask : "(null)", + deny->bits); + } + + return aconf; } /** Check whether a particular ConfItem is already attached to a @@ -424,7 +533,7 @@ static int is_attached(struct ConfItem *aconf, struct Client *cptr) /** Associate a specific configuration entry to a *local* client (this * is the one which used in accepting the connection). Note, that this - * automaticly changes the attachment if there was an old one... + * automatically changes the attachment if there was an old one... * @param cptr Client to attach \a aconf to * @param aconf ConfItem to attach * @return Authorization check result. @@ -520,41 +629,34 @@ struct ConfItem* attach_confs_byhost(struct Client* cptr, const char* host, /** Find a ConfItem that has the same name and user+host fields as * specified. Requires an exact match for \a name. * @param name Name to match - * @param user User part of match (or NULL) - * @param host Hostname part of match + * @param cptr Client to match against * @param statmask Filter for ConfItem::status * @return First found matching ConfItem. */ -struct ConfItem* find_conf_exact(const char* name, const char* user, - const char* host, int statmask) +struct ConfItem* find_conf_exact(const char* name, struct Client *cptr, int statmask) { struct ConfItem *tmp; - char userhost[USERLEN + HOSTLEN + 3]; - - if (user) - ircd_snprintf(0, userhost, sizeof(userhost), "%s@%s", user, host); - else - ircd_strncpy(userhost, host, sizeof(userhost) - 1); for (tmp = GlobalConfList; tmp; tmp = tmp->next) { if (!(tmp->status & statmask) || !tmp->name || !tmp->host || 0 != ircd_strcmp(tmp->name, name)) continue; - /* - * Accept if the *real* hostname (usually sockecthost) - * socket host) matches *either* host or name field - * of the configuration. - */ - if (match(tmp->host, userhost)) + if (tmp->username + && (EmptyString(cli_username(cptr)) + || match(tmp->username, cli_username(cptr)))) continue; - if (tmp->status & CONF_OPERATOR) { - if (tmp->clients < MaxLinks(tmp->conn_class)) - return tmp; - else + if (tmp->addrbits < 0) + { + if (match(tmp->host, cli_sockhost(cptr))) continue; } - else - return tmp; + else if (!ipmask_check(&cli_ip(cptr), &tmp->address.addr, tmp->addrbits)) + continue; + if ((tmp->status & CONF_OPERATOR) + && (MaxLinks(tmp->conn_class) > 0) + && (tmp->clients >= MaxLinks(tmp->conn_class))) + continue; + return tmp; } return 0; } @@ -666,6 +768,7 @@ void conf_erase_deny_list(void) MyFree(p->hostmask); MyFree(p->usermask); MyFree(p->message); + MyFree(p->realmask); MyFree(p); } denyConfList = 0; @@ -711,9 +814,9 @@ void clear_quarantines(void) static int conf_error; /** When non-zero, indicates that the configuration file was loaded at least once. */ static int conf_already_read; -extern FILE *yyin; extern void yyparse(void); -extern void init_lexer(void); +extern int init_lexer(void); +extern void deinit_lexer(void); /** Read configuration file. * @return Zero on failure, non-zero on success. */ @@ -721,11 +824,11 @@ int read_configuration_file(void) { conf_error = 0; feature_unmark(); /* unmark all features for resetting later */ - /* Now just open an fd. The buffering isn't really needed... */ - init_lexer(); + clear_nameservers(); /* clear previous list of DNS servers */ + if (!init_lexer()) + return 0; yyparse(); - fclose(yyin); - yyin = NULL; + deinit_lexer(); feature_mark(); /* reset unmarked features */ conf_already_read = 1; return 1; @@ -746,6 +849,47 @@ yyerror(const char *msg) conf_error = 1; } +/** Attach CONF_UWORLD items to a server and everything attached to it. */ +static void +attach_conf_uworld(struct Client *cptr) +{ + struct DLink *lp; + + attach_confs_byhost(cptr, cli_name(cptr), CONF_UWORLD); + for (lp = cli_serv(cptr)->down; lp; lp = lp->next) + attach_conf_uworld(lp->value.cptr); +} + +/** Free all memory associated with service mapping \a smap. + * @param smap[in] The mapping to free. + */ +void free_mapping(struct s_map *smap) +{ + struct nick_host *nh, *next; + for (nh = smap->services; nh; nh = next) + { + next = nh->next; + MyFree(nh); + } + MyFree(smap->name); + MyFree(smap->command); + MyFree(smap->prepend); + MyFree(smap); +} + +/** Unregister and free all current service mappings. */ +static void close_mappings(void) +{ + struct s_map *map, *next; + + for (map = GlobalServiceMapList; map; map = next) { + next = map->next; + unregister_mapping(map); + free_mapping(map); + } + GlobalServiceMapList = NULL; +} + /** Reload the configuration file. * @param cptr Client that requested rehash (if a signal, &me). * @param sig Type of rehash (0 = oper-requested, 1 = signal, 2 = @@ -798,18 +942,19 @@ int rehash(struct Client *cptr, int sig) clear_quarantines(); - if (sig != 2) - restart_resolver(); - class_mark_delete(); mark_listeners_closing(); - iauth_mark_closing(); + auth_mark_closing(); + close_mappings(); read_configuration_file(); + if (sig != 2) + restart_resolver(); + log_reopen(); /* reopen log files */ - iauth_close_unused(); + auth_close_unused(); close_listeners(); class_delete_marked(); /* unless it fails */ @@ -830,10 +975,8 @@ int rehash(struct Client *cptr, int sig) for (i = 0; i <= HighestFd; i++) { if ((acptr = LocalClientArray[i])) { assert(!IsMe(acptr)); - if (IsServer(acptr)) { + if (IsServer(acptr)) det_confs_butmask(acptr, ~(CONF_UWORLD | CONF_ILLEGAL)); - attach_confs_byname(acptr, cli_name(acptr), CONF_UWORLD); - } /* Because admin's are getting so uppity about people managing to * get past K/G's etc, we'll "fix" the bug by actually explaining * whats going on. @@ -851,6 +994,8 @@ int rehash(struct Client *cptr, int sig) } } + attach_conf_uworld(&me); + return ret; } @@ -915,28 +1060,16 @@ int find_kill(struct Client *cptr) * -- Isomer */ for (deny = denyConfList; deny; deny = deny->next) { - if (0 != match(deny->usermask, name)) + if (deny->usermask && match(deny->usermask, name)) + continue; + if (deny->realmask && match(deny->realmask, realname)) + continue; + if (deny->bits > 0) { + if (!ipmask_check(&cli_ip(cptr), &deny->address, deny->bits)) + continue; + } else if (deny->hostmask && match(deny->hostmask, host)) continue; - if (EmptyString(deny->hostmask)) - break; - - if (deny->flags & DENY_FLAGS_REALNAME) { /* K: by real name */ - if (0 == match(deny->hostmask + 2, realname)) - break; - } else if (deny->flags & DENY_FLAGS_IP) { /* k: by IP */ -#ifdef DEBUGMODE - char tbuf1[SOCKIPLEN], tbuf2[SOCKIPLEN]; - Debug((DEBUG_DEBUG, "ip: %s network: %s/%u", - ircd_ntoa_r(tbuf1, &cli_ip(cptr)), ircd_ntoa_r(tbuf2, &deny->address), deny->bits)); -#endif - if (ipmask_check(&cli_ip(cptr), &deny->address, deny->bits)) - break; - } - else if (0 == match(deny->hostmask, host)) - break; - } - if (deny) { if (EmptyString(deny->message)) send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":Connection from your host is refused on this server."); @@ -946,19 +1079,17 @@ int find_kill(struct Client *cptr) else send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", deny->message); } + return -1; } - else if ((agline = gline_lookup(cptr, 0))) { + + if (!feature_bool(FEAT_DISABLE_GLINES) && (agline = gline_lookup(cptr, 0))) { /* * find active glines * added a check against the user's IP address to find_gline() -Kev */ send_reply(cptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s.", GlineReason(agline)); - } - - if (deny) - return -1; - if (agline) return -2; + } return 0; } @@ -972,8 +1103,6 @@ enum AuthorizationCheckResult conf_check_client(struct Client *cptr) { enum AuthorizationCheckResult acr = ACR_OK; - ClearAccess(cptr); - if ((acr = attach_iline(cptr))) { Debug((DEBUG_DNS, "ch_cl: access denied: %s[%s]", cli_name(cptr), cli_sockhost(cptr))); @@ -1011,37 +1140,18 @@ int conf_check_server(struct Client *cptr) if (IsConnecting(cptr) || IsHandshake(cptr)) { c_conf = find_conf_byname(lp, cli_name(cptr), CONF_SERVER); if (!c_conf) { - sendto_opmask_butone(0, SNO_OLDSNO, "Connect Error: lost C:line for %s", + sendto_opmask_butone(0, SNO_OLDSNO, + "Connect Error: lost Connect block for %s", cli_name(cptr)); det_confs_butmask(cptr, 0); return -1; } } - ClearAccess(cptr); + /* Try finding the Connect block by DNS name and IP next. */ + if (!c_conf && !(c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER))) + c_conf = find_conf_byip(lp, &cli_ip(cptr), CONF_SERVER); - if (!c_conf) { - if (cli_dns_reply(cptr)) { - struct DNSReply* hp = cli_dns_reply(cptr); - const char* name = hp->h_name; - /* - * If we are missing a C or N line from above, search for - * it under all known hostnames we have for this ip#. - */ - if ((c_conf = find_conf_byhost(lp, hp->h_name, CONF_SERVER))) - ircd_strncpy(cli_sockhost(cptr), name, HOSTLEN); - else - c_conf = find_conf_byip(lp, &hp->addr, CONF_SERVER); - } - else { - /* - * Check for C lines with the hostname portion the ip number - * of the host the server runs on. This also checks the case where - * there is a server connecting from 'localhost'. - */ - c_conf = find_conf_byhost(lp, cli_sockhost(cptr), CONF_SERVER); - } - } /* * Attach by IP# only if all other checks have failed. * It is quite possible to get here with the strange things that can @@ -1065,7 +1175,6 @@ int conf_check_server(struct Client *cptr) * attach the Connect block to the client structure for later use. */ attach_conf(cptr, c_conf); - attach_confs_byname(cptr, cli_name(cptr), CONF_UWORLD); if (!irc_in_addr_valid(&c_conf->address.addr)) memcpy(&c_conf->address.addr, &cli_ip(cptr), sizeof(c_conf->address.addr));