RADCLIENT *client_from_request(RADCLIENT_LIST *clients, REQUEST *request) { int i, *pi; char **p; RADCLIENT *c; char buffer[128]; if (!clients || !request) return NULL; c = talloc_zero(clients, RADCLIENT); c->cs = request->client->cs; c->ipaddr.af = AF_UNSPEC; c->src_ipaddr.af = AF_UNSPEC; for (i = 0; dynamic_config[i].name != NULL; i++) { DICT_ATTR const *da; VALUE_PAIR *vp; da = dict_attrbyname(dynamic_config[i].name); if (!da) { DEBUG("- Cannot add client %s: attribute \"%s\"is not in the dictionary", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)), dynamic_config[i].name); error: client_free(c); return NULL; } vp = pairfind(request->config_items, da->attr, da->vendor, TAG_ANY); if (!vp) { /* * Not required. Skip it. */ if (!dynamic_config[i].dflt) continue; DEBUG("- Cannot add client %s: Required attribute \"%s\" is missing.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)), dynamic_config[i].name); goto error; } switch (dynamic_config[i].type) { case PW_TYPE_IPV4_ADDR: if (da->attr == PW_FREERADIUS_CLIENT_IP_ADDRESS) { c->ipaddr.af = AF_INET; c->ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; c->ipaddr.prefix = 32; } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IP_ADDRESS) { #ifdef WITH_UDPFROMTO c->src_ipaddr.af = AF_INET; c->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr; #else WARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IP-Address"); #endif } break; case PW_TYPE_IPV6_ADDR: if (da->attr == PW_FREERADIUS_CLIENT_IPV6_ADDRESS) { c->ipaddr.af = AF_INET6; c->ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; c->ipaddr.prefix = 128; } else if (da->attr == PW_FREERADIUS_CLIENT_SRC_IPV6_ADDRESS) { #ifdef WITH_UDPFROMTO c->src_ipaddr.af = AF_INET6; c->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr; #else WARN("Server not built with udpfromto, ignoring FreeRADIUS-Client-Src-IPv6-Address"); #endif } break; case PW_TYPE_IPV4_PREFIX: if (da->attr == PW_FREERADIUS_CLIENT_IP_PREFIX) { c->ipaddr.af = AF_INET; memcpy(&c->ipaddr.ipaddr.ip4addr.s_addr, &(vp->vp_ipv4prefix[2]), sizeof(c->ipaddr.ipaddr.ip4addr.s_addr)); fr_ipaddr_mask(&c->ipaddr, (vp->vp_ipv4prefix[1] & 0x3f)); } break; case PW_TYPE_IPV6_PREFIX: if (da->attr == PW_FREERADIUS_CLIENT_IPV6_PREFIX) { c->ipaddr.af = AF_INET6; memcpy(&c->ipaddr.ipaddr.ip6addr, &(vp->vp_ipv6prefix[2]), sizeof(c->ipaddr.ipaddr.ip6addr)); fr_ipaddr_mask(&c->ipaddr, vp->vp_ipv6prefix[1]); } break; case PW_TYPE_STRING: p = (char **) ((char *) c + dynamic_config[i].offset); if (*p) talloc_free(*p); if (vp->vp_strvalue[0]) { *p = talloc_typed_strdup(c->cs, vp->vp_strvalue); } else { *p = NULL; } break; case PW_TYPE_BOOLEAN: pi = (int *) ((bool *) ((char *) c + dynamic_config[i].offset)); *pi = vp->vp_integer; break; default: goto error; } } if (c->ipaddr.af == AF_UNSPEC) { DEBUG("- Cannot add client %s: No IP address was specified.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer))); goto error; } { fr_ipaddr_t addr; /* * Need to apply the same mask as we set for the client * else clients created with FreeRADIUS-Client-IPv6-Prefix * or FreeRADIUS-Client-IPv4-Prefix will fail this check. */ addr = request->packet->src_ipaddr; fr_ipaddr_mask(&addr, c->ipaddr.prefix); if (fr_ipaddr_cmp(&addr, &c->ipaddr) != 0) { char buf2[128]; DEBUG("- Cannot add client %s: IP address %s do not match", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer)), ip_ntoh(&c->ipaddr, buf2, sizeof(buf2))); goto error; } } if (!c->secret || !*c->secret) { DEBUG("- Cannot add client %s: No secret was specified.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer))); goto error; } if (!client_validate(clients, request->client, c)) { return NULL; } if ((c->src_ipaddr.af != AF_UNSPEC) && (c->src_ipaddr.af != c->ipaddr.af)) { DEBUG("- Cannot add client %s: Client IP and src address are different IP version.", ip_ntoh(&request->packet->src_ipaddr, buffer, sizeof(buffer))); goto error; } return c; }
/* * Find a per-socket client. */ RADCLIENT *client_listener_find(const rad_listen_t *listener, const fr_ipaddr_t *ipaddr, int src_port) { #ifdef WITH_DYNAMIC_CLIENTS int rcode; REQUEST *request; RADCLIENT *created; #endif time_t now; RADCLIENT *client; RADCLIENT_LIST *clients; listen_socket_t *sock; rad_assert(listener != NULL); rad_assert(ipaddr != NULL); sock = listener->data; clients = sock->clients; /* * This HAS to have been initialized previously. */ rad_assert(clients != NULL); client = client_find(clients, ipaddr,sock->proto); if (!client) { char name[256], buffer[128]; #ifdef WITH_DYNAMIC_CLIENTS unknown: /* used only for dynamic clients */ #endif /* * DoS attack quenching, but only in daemon mode. * If they're running in debug mode, show them * every packet. */ if (debug_flag == 0) { static time_t last_printed = 0; now = time(NULL); if (last_printed == now) return NULL; last_printed = now; } listener->print(listener, name, sizeof(name)); radlog(L_ERR, "Ignoring request to %s from unknown client %s port %d" #ifdef WITH_TCP " proto %s" #endif , name, inet_ntop(ipaddr->af, &ipaddr->ipaddr, buffer, sizeof(buffer)), src_port #ifdef WITH_TCP , (sock->proto == IPPROTO_UDP) ? "udp" : "tcp" #endif ); return NULL; } #ifndef WITH_DYNAMIC_CLIENTS return client; /* return the found client. */ #else /* * No server defined, and it's not dynamic. Return it. */ if (!client->client_server && !client->dynamic) return client; now = time(NULL); /* * It's a dynamically generated client, check it. */ if (client->dynamic && (src_port != 0)) { /* * Lives forever. Return it. */ if (client->lifetime == 0) return client; /* * Rate-limit the deletion of known clients. * This makes them last a little longer, but * prevents the server from melting down if (say) * 10k clients all expire at once. */ if (now == client->last_new_client) return client; /* * It's not dead yet. Return it. */ if ((client->created + client->lifetime) > now) return client; /* * This really puts them onto a queue for later * deletion. */ client_delete(clients, client); /* * Go find the enclosing network again. */ client = client_find(clients, ipaddr, sock->proto); /* * WTF? */ if (!client) goto unknown; if (!client->client_server) goto unknown; /* * At this point, 'client' is the enclosing * network that configures where dynamic clients * can be defined. */ rad_assert(client->dynamic == 0); } else { /* * The IP is unknown, so we've found an enclosing * network. Enable DoS protection. We only * allow one new client per second. Known * clients aren't subject to this restriction. */ if (now == client->last_new_client) goto unknown; } client->last_new_client = now; request = request_alloc(); if (!request) goto unknown; request->listener = listener; request->client = client; request->packet = rad_recv(listener->fd, 0x02); /* MSG_PEEK */ if (!request->packet) { /* badly formed, etc */ request_free(&request); goto unknown; } request->reply = rad_alloc_reply(request->packet); if (!request->reply) { request_free(&request); goto unknown; } request->packet->timestamp = request->timestamp; request->number = 0; request->priority = listener->type; request->server = client->client_server; request->root = &mainconfig; /* * Run a fake request through the given virtual server. * Look for FreeRADIUS-Client-IP-Address * FreeRADIUS-Client-Secret * ... * * and create the RADCLIENT structure from that. */ DEBUG("server %s {", request->server); rcode = module_authorize(0, request); DEBUG("} # server %s", request->server); if (rcode != RLM_MODULE_OK) { request_free(&request); goto unknown; } /* * If the client was updated by rlm_dynamic_clients, * don't create the client from attribute-value pairs. */ if (request->client == client) { created = client_create(clients, request); } else { created = request->client; /* * This frees the client if it isn't valid. */ if (!client_validate(clients, client, created)) goto unknown; } request_free(&request); if (!created) goto unknown; return created; #endif }
RADCLIENT_LIST *clients_parse_section(CONF_SECTION *section, UNUSED bool tls_required) #endif { bool global = false, in_server = false; CONF_SECTION *cs; RADCLIENT *c; RADCLIENT_LIST *clients; /* * Be forgiving. If there's already a clients, return * it. Otherwise create a new one. */ clients = cf_data_find(section, "clients"); if (clients) return clients; clients = clients_init(section); if (!clients) return NULL; if (cf_top_section(section) == section) global = true; if (strcmp("server", cf_section_name1(section)) == 0) in_server = true; /* * Associate the clients structure with the section. */ if (cf_data_add(section, "clients", clients, NULL) < 0) { cf_log_err_cs(section, "Failed to associate clients with section %s", cf_section_name1(section)); clients_free(clients); return NULL; } for (cs = cf_subsection_find_next(section, NULL, "client"); cs != NULL; cs = cf_subsection_find_next(section, cs, "client")) { c = client_parse(cs, in_server); if (!c) { return NULL; } #ifdef WITH_TLS /* * TLS clients CANNOT use non-TLS listeners. * non-TLS clients CANNOT use TLS listeners. */ if (tls_required != c->tls_required) { cf_log_err_cs(cs, "Client does not have the same TLS configuration as the listener"); client_free(c); clients_free(clients); return NULL; } #endif /* * FIXME: Add the client as data via cf_data_add, * for migration issues. */ #ifdef WITH_DYNAMIC_CLIENTS #ifdef HAVE_DIRENT_H if (c->client_server) { char const *value; CONF_PAIR *cp; DIR *dir; struct dirent *dp; struct stat stat_buf; char buf2[2048]; /* * Find the directory where individual * client definitions are stored. */ cp = cf_pair_find(cs, "directory"); if (!cp) goto add_client; value = cf_pair_value(cp); if (!value) { cf_log_err_cs(cs, "The \"directory\" entry must not be empty"); client_free(c); return NULL; } DEBUG("including dynamic clients in %s", value); dir = opendir(value); if (!dir) { cf_log_err_cs(cs, "Error reading directory %s: %s", value, fr_syserror(errno)); client_free(c); return NULL; } /* * Read the directory, ignoring "." files. */ while ((dp = readdir(dir)) != NULL) { char const *p; RADCLIENT *dc; if (dp->d_name[0] == '.') continue; /* * Check for valid characters */ for (p = dp->d_name; *p != '\0'; p++) { if (isalpha((int)*p) || isdigit((int)*p) || (*p == ':') || (*p == '.')) continue; break; } if (*p != '\0') continue; snprintf(buf2, sizeof(buf2), "%s/%s", value, dp->d_name); if ((stat(buf2, &stat_buf) != 0) || S_ISDIR(stat_buf.st_mode)) continue; dc = client_read(buf2, in_server, true); if (!dc) { cf_log_err_cs(cs, "Failed reading client file \"%s\"", buf2); client_free(c); closedir(dir); return NULL; } /* * Validate, and add to the list. */ if (!client_validate(clients, c, dc)) { client_free(c); closedir(dir); return NULL; } } /* loop over the directory */ closedir(dir); } #endif /* HAVE_DIRENT_H */ add_client: #endif /* WITH_DYNAMIC_CLIENTS */ if (!client_add(clients, c)) { cf_log_err_cs(cs, "Failed to add client %s", cf_section_name2(cs)); client_free(c); return NULL; } } /* * Replace the global list of clients with the new one. * The old one is still referenced from the original * configuration, and will be freed when that is freed. */ if (global) { root_clients = clients; } return clients; }