/** Steal one VP * * @param[in] ctx to move VALUE_PAIR into * @param[in] vp VALUE_PAIR to move into the new context. */ void pairsteal(TALLOC_CTX *ctx, VALUE_PAIR *vp) { (void) talloc_steal(ctx, vp); /* * The DA may be unknown. If we're stealing the VPs to a * different context, copy the unknown DA. We use the VP * as a context here instead of "ctx", so that when the * VP is freed, so is the DA. * * Since we have no introspection into OTHER VPs using * the same DA, we can't have multiple VPs use the same * DA. So we might as well tie it to this VP. */ if (vp->da->flags.is_unknown) { DICT_ATTR *da; char *p; size_t size; size = talloc_get_size(vp->da); p = talloc_zero_array(vp, char, size); da = (DICT_ATTR *) p; talloc_set_type(p, DICT_ATTR); memcpy(da, vp->da, size); vp->da = da; }
/* * Convert a buffer to a CSV entry */ static rlm_csv_entry_t *file2csv(CONF_SECTION *conf, rlm_csv_t *inst, int lineno, char *buffer) { rlm_csv_entry_t *e; int i; char *p, *q; MEM(e = (rlm_csv_entry_t *)talloc_zero_array(inst->tree, uint8_t, sizeof(*e) + inst->used_fields + sizeof(e->data[0]))); talloc_set_type(e, rlm_csv_entry_t); for (p = buffer, i = 0; p != NULL; p = q, i++) { if (!buf2entry(inst, p, &q)) { cf_log_err(conf, "Malformed entry in file %s line %d", inst->filename, lineno); return NULL; } if (q) *(q++) = '\0'; if (i >= inst->num_fields) { cf_log_err(conf, "Too many fields at file %s line %d", inst->filename, lineno); return NULL; } /* * This is the key field. */ if (i == inst->key_field) { e->key = talloc_typed_strdup(e, p); continue; } /* * This field is unused. Ignore it. */ if (inst->field_offsets[i] < 0) continue; MEM(e->data[inst->field_offsets[i]] = talloc_typed_strdup(e, p)); } if (i < inst->num_fields) { cf_log_err(conf, "Too few fields at file %s line %d (%d < %d)", inst->filename, lineno, i, inst->num_fields); return NULL; } /* * FIXME: Allow duplicate keys later. */ if (!rbtree_insert(inst->tree, e)) { cf_log_err(conf, "Failed inserting entry for filename %s line %d: duplicate entry", inst->filename, lineno); return NULL; } return e; }
/** Create a fifo queue * * The first element enqueued will be the first to be dequeued. * * @note The created fifo does not provide any thread synchronisation functionality * such as mutexes. If multiple threads are enqueueing and dequeueing data * the callers must synchronise their access. * * @param[in] ctx to allocate fifo array in. * @param[in] type Talloc type of elements (may be NULL). * @param[in] max The maximum number of elements allowed. * @param[in] free_node Function to use to free node data if the fifo is freed. * @return * - A new fifo queue. * - NULL on error. */ fr_fifo_t *_fr_fifo_create(TALLOC_CTX *ctx, char const *type, int max, fr_fifo_free_t free_node) { fr_fifo_t *fi; if ((max < 2) || (max > (1024 * 1024))) return NULL; fi = talloc_zero_size(ctx, (sizeof(*fi) + (sizeof(fi->data[0])*max))); if (!fi) return NULL; talloc_set_type(fi, fr_fifo_t); talloc_set_destructor(fi, _fifo_free); fi->max = max; fi->type = type; fi->free_node = free_node; return fi; }
/** Instantiate thread data for the submodule. * */ static int mod_thread_instantiate(UNUSED CONF_SECTION const *cs, void *instance, UNUSED fr_event_list_t *el, void *thread) { rlm_stats_t *inst = talloc_get_type_abort(instance, rlm_stats_t); rlm_stats_thread_t *t = thread; (void) talloc_set_type(t, rlm_stats_thread_t); t->inst = inst; t->src = rbtree_talloc_create(t, data_cmp, rlm_stats_data_t, NULL, RBTREE_FLAG_LOCK); t->dst = rbtree_talloc_create(t, data_cmp, rlm_stats_data_t, NULL, RBTREE_FLAG_LOCK); pthread_mutex_lock(&inst->mutex); fr_dlist_insert_head(&inst->list, t); pthread_mutex_unlock(&inst->mutex); return 0; }
static int unmap_eapsim_types(RADIUS_PACKET *r) { VALUE_PAIR *esvp; uint8_t *eap_data; int rcode_unmap; esvp = pairfind(r->vps, ATTRIBUTE_EAP_BASE+PW_EAP_SIM, 0, TAG_ANY); if (!esvp) { ERROR("eap: EAP-Sim attribute not found"); return 0; } eap_data = talloc_memdup(esvp, esvp->vp_octets, esvp->length); talloc_set_type(eap_data, uint8_t); rcode_unmap = unmap_eapsim_basictypes(r, eap_data, esvp->length); talloc_free(eap_data); return rcode_unmap; }
int main(int argc, char **argv) { RADIUS_PACKET *req; char *p; int c; uint16_t port = 0; char *filename = NULL; FILE *fp; int id; int force_af = AF_UNSPEC; static fr_log_t radclient_log = { .colourise = true, .fd = STDOUT_FILENO, .dst = L_DST_STDOUT, .file = NULL, .debug_file = NULL, }; radlog_init(&radclient_log, false); /* * We probably don't want to free the talloc autofree context * directly, so we'll allocate a new context beneath it, and * free that before any leak reports. */ TALLOC_CTX *autofree = talloc_init("main"); id = ((int)getpid() & 0xff); fr_debug_flag = 0; set_radius_dir(autofree, RADIUS_DIR); while ((c = getopt(argc, argv, "46c:d:D:f:hi:qst:r:S:xXv")) != EOF) { switch(c) { case '4': force_af = AF_INET; break; case '6': force_af = AF_INET6; break; case 'd': set_radius_dir(autofree, optarg); break; case 'D': main_config.dictionary_dir = talloc_typed_strdup(NULL, optarg); break; case 'f': filename = optarg; break; case 'q': do_output = 0; break; case 'x': debug_flag++; fr_debug_flag++; break; case 'X': #if 0 sha1_data_problems = 1; /* for debugging only */ #endif break; case 'r': if (!isdigit((int) *optarg)) usage(); retries = atoi(optarg); break; case 'i': if (!isdigit((int) *optarg)) usage(); id = atoi(optarg); if ((id < 0) || (id > 255)) { usage(); } break; case 's': do_summary = 1; break; case 't': if (!isdigit((int) *optarg)) usage(); timeout = atof(optarg); break; case 'v': printf("radeapclient: $Id: 69643e042c5a7c9053977756a5623e3c07598f2e $ built on " __DATE__ " at " __TIME__ "\n"); exit(0); break; case 'S': fp = fopen(optarg, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", optarg, fr_syserror(errno)); exit(1); } if (fgets(filesecret, sizeof(filesecret), fp) == NULL) { fprintf(stderr, "radclient: Error reading %s: %s\n", optarg, fr_syserror(errno)); exit(1); } fclose(fp); /* truncate newline */ p = filesecret + strlen(filesecret) - 1; while ((p >= filesecret) && (*p < ' ')) { *p = '\0'; --p; } if (strlen(filesecret) < 2) { fprintf(stderr, "radclient: Secret in %s is too short\n", optarg); exit(1); } secret = filesecret; break; case 'h': default: usage(); break; } } argc -= (optind - 1); argv += (optind - 1); if ((argc < 3) || ((!secret) && (argc < 4))) { usage(); } if (!main_config.dictionary_dir) { main_config.dictionary_dir = DICTDIR; } /* * Read the distribution dictionaries first, then * the ones in raddb. */ DEBUG2("including dictionary file %s/%s", main_config.dictionary_dir, RADIUS_DICTIONARY); if (dict_init(main_config.dictionary_dir, RADIUS_DICTIONARY) != 0) { ERROR("Errors reading dictionary: %s", fr_strerror()); exit(1); } /* * It's OK if this one doesn't exist. */ int rcode = dict_read(radius_dir, RADIUS_DICTIONARY); if (rcode == -1) { ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY, fr_strerror()); exit(1); } /* * We print this after reading it. That way if * it doesn't exist, it's OK, and we don't print * anything. */ if (rcode == 0) { DEBUG2("including dictionary file %s/%s", radius_dir, RADIUS_DICTIONARY); } req = rad_alloc(NULL, 1); if (!req) { fr_perror("radclient"); exit(1); } #if 0 { FILE *randinit; if((randinit = fopen("/dev/urandom", "r")) == NULL) { perror("/dev/urandom"); } else { fread(randctx.randrsl, 256, 1, randinit); fclose(randinit); } } fr_randinit(&randctx, 1); #endif req->id = id; /* * Resolve hostname. */ if (force_af == AF_UNSPEC) force_af = AF_INET; req->dst_ipaddr.af = force_af; if (strcmp(argv[1], "-") != 0) { char const *hostname = argv[1]; char const *portname = argv[1]; char buffer[256]; if (*argv[1] == '[') { /* IPv6 URL encoded */ p = strchr(argv[1], ']'); if ((size_t) (p - argv[1]) >= sizeof(buffer)) { usage(); } memcpy(buffer, argv[1] + 1, p - argv[1] - 1); buffer[p - argv[1] - 1] = '\0'; hostname = buffer; portname = p + 1; } p = strchr(portname, ':'); if (p && (strchr(p + 1, ':') == NULL)) { *p = '\0'; portname = p + 1; } else { portname = NULL; } if (ip_hton(&req->dst_ipaddr, force_af, hostname, false) < 0) { fprintf(stderr, "radclient: Failed to find IP address for host %s: %s\n", hostname, fr_syserror(errno)); exit(1); } /* * Strip port from hostname if needed. */ if (portname) port = atoi(portname); } /* * See what kind of request we want to send. */ if (strcmp(argv[2], "auth") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_AUTHENTICATION_REQUEST; } else if (strcmp(argv[2], "acct") == 0) { if (port == 0) port = getport("radacct"); if (port == 0) port = PW_ACCT_UDP_PORT; req->code = PW_CODE_ACCOUNTING_REQUEST; do_summary = 0; } else if (strcmp(argv[2], "status") == 0) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = PW_CODE_STATUS_SERVER; } else if (strcmp(argv[2], "disconnect") == 0) { if (port == 0) port = PW_POD_UDP_PORT; req->code = PW_CODE_DISCONNECT_REQUEST; } else if (isdigit((int) argv[2][0])) { if (port == 0) port = getport("radius"); if (port == 0) port = PW_AUTH_UDP_PORT; req->code = atoi(argv[2]); } else { usage(); } req->dst_port = port; /* * Add the secret. */ if (argv[3]) secret = argv[3]; /* * Read valuepairs. * Maybe read them, from stdin, if there's no * filename, or if the filename is '-'. */ if (filename && (strcmp(filename, "-") != 0)) { fp = fopen(filename, "r"); if (!fp) { fprintf(stderr, "radclient: Error opening %s: %s\n", filename, fr_syserror(errno)); exit(1); } } else { fp = stdin; } /* * Send request. */ if ((req->sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("radclient: socket: "); exit(1); } while(!filedone) { if (req->vps) pairfree(&req->vps); if (readvp2(&req->vps, NULL, fp, &filedone) < 0) { fr_perror("radeapclient"); break; } sendrecv_eap(req); } if(do_summary) { printf("\n\t Total approved auths: %d\n", totalapp); printf("\t Total denied auths: %d\n", totaldeny); } talloc_free(autofree); return 0; } /* * given a radius request with some attributes in the EAP range, build * them all into a single EAP-Message body. * * Note that this function will build multiple EAP-Message bodies * if there are multiple eligible EAP-types. This is incorrect, as the * recipient will in fact concatenate them. * * XXX - we could break the loop once we process one type. Maybe this * just deserves an assert? * */ static void map_eap_methods(RADIUS_PACKET *req) { VALUE_PAIR *vp, *vpnext; int id, eapcode; int eap_method; eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t); vp = pairfind(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY); if(!vp) { id = ((int)getpid() & 0xff); } else { id = vp->vp_integer; } vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE, 0, TAG_ANY); if(!vp) { eapcode = PW_EAP_REQUEST; } else { eapcode = vp->vp_integer; } for(vp = req->vps; vp != NULL; vp = vpnext) { /* save it in case it changes! */ vpnext = vp->next; if(vp->da->attr >= ATTRIBUTE_EAP_BASE && vp->da->attr < ATTRIBUTE_EAP_BASE+256) { break; } } if(!vp) { return; } eap_method = vp->da->attr - ATTRIBUTE_EAP_BASE; switch(eap_method) { case PW_EAP_IDENTITY: case PW_EAP_NOTIFICATION: case PW_EAP_NAK: case PW_EAP_MD5: case PW_EAP_OTP: case PW_EAP_GTC: case PW_EAP_TLS: case PW_EAP_LEAP: case PW_EAP_TTLS: case PW_EAP_PEAP: default: /* * no known special handling, it is just encoded as an * EAP-message with the given type. */ /* nuke any existing EAP-Messages */ pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY); pt_ep->code = eapcode; pt_ep->id = id; pt_ep->type.num = eap_method; pt_ep->type.length = vp->length; pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->length); talloc_set_type(pt_ep->type.data, uint8_t); eap_basic_compose(req, pt_ep); } }
/* * given a radius request with some attributes in the EAP range, build * them all into a single EAP-Message body. * * Note that this function will build multiple EAP-Message bodies * if there are multiple eligible EAP-types. This is incorrect, as the * recipient will in fact concatenate them. * * XXX - we could break the loop once we process one type. Maybe this * just deserves an assert? * */ static void map_eap_methods(RADIUS_PACKET *req) { VALUE_PAIR *vp, *vpnext; int id, eapcode; int eap_method; eap_packet_t *pt_ep = talloc_zero(req, eap_packet_t); vp = pairfind(req->vps, ATTRIBUTE_EAP_ID, 0, TAG_ANY); if(!vp) { id = ((int)getpid() & 0xff); } else { id = vp->vp_integer; } vp = pairfind(req->vps, ATTRIBUTE_EAP_CODE, 0, TAG_ANY); if(!vp) { eapcode = PW_EAP_REQUEST; } else { eapcode = vp->vp_integer; } for(vp = req->vps; vp != NULL; vp = vpnext) { /* save it in case it changes! */ vpnext = vp->next; if(vp->da->attr >= ATTRIBUTE_EAP_BASE && vp->da->attr < ATTRIBUTE_EAP_BASE+256) { break; } } if(!vp) { return; } eap_method = vp->da->attr - ATTRIBUTE_EAP_BASE; switch(eap_method) { case PW_EAP_IDENTITY: case PW_EAP_NOTIFICATION: case PW_EAP_NAK: case PW_EAP_MD5: case PW_EAP_OTP: case PW_EAP_GTC: case PW_EAP_TLS: case PW_EAP_LEAP: case PW_EAP_TTLS: case PW_EAP_PEAP: default: /* * no known special handling, it is just encoded as an * EAP-message with the given type. */ /* nuke any existing EAP-Messages */ pairdelete(&req->vps, PW_EAP_MESSAGE, 0, TAG_ANY); pt_ep->code = eapcode; pt_ep->id = id; pt_ep->type.num = eap_method; pt_ep->type.length = vp->length; pt_ep->type.data = talloc_memdup(vp, vp->vp_octets, vp->length); talloc_set_type(pt_ep->type.data, uint8_t); eap_basic_compose(req, pt_ep); } }
/** Parse socket configuration * * @param[in] cs specifying the listener configuration. * @param[in] listen structure encapsulating the libldap socket. * @return * - 0 on success. * - -1 on error. */ static int proto_ldap_socket_parse(CONF_SECTION *cs, rad_listen_t *listen) { proto_ldap_inst_t *inst = listen->data; CONF_SECTION *sync_cs; size_t i; int ret; /* * Always cache the CONF_SECTION of the server. */ listen->server_cs = virtual_server_find(listen->server); if (!listen->server_cs) { cf_log_err(cs, "Failed to find virtual server '%s'", listen->server); return -1; } if (cf_section_rules_push(cs, module_config) < 0) return -1; ret = cf_section_parse(inst, inst, cs); if (ret < 0) return ret; talloc_set_type(inst, proto_ldap_inst_t); rad_assert(inst->handle_config.server_str[0]); inst->handle_config.name = talloc_typed_asprintf(inst, "proto_ldap_conn (%s)", listen->server); memcpy(&inst->handle_config.server, &inst->handle_config.server_str[0], sizeof(inst->handle_config.server)); /* * Convert scope strings to enumerated constants */ for (sync_cs = cf_section_find(cs, "sync", NULL), i = 0; sync_cs; sync_cs = cf_section_find_next(cs, sync_cs, "sync", NULL), i++) { int scope; void **tmp; CONF_SECTION *map_cs; talloc_set_type(inst->sync_config[i], sync_config_t); scope = fr_str2int(fr_ldap_scope, inst->sync_config[i]->scope_str, -1); if (scope < 0) { #ifdef LDAP_SCOPE_CHILDREN cf_log_err(cs, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'" ", 'base' or 'children'", inst->sync_config[i]->scope_str); #else cf_log_err(cs, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'" " or 'base'", inst->sync_config[i]->scope_str) #endif return -1; } inst->sync_config[i]->scope = scope; /* * Needs to be NULL terminated as that's what libldap needs */ if (inst->sync_config[i]->attrs) { memcpy(&tmp, &inst->sync_config[i]->attrs, sizeof(tmp)); tmp = talloc_array_null_terminate(tmp); memcpy(&inst->sync_config[i]->attrs, tmp, sizeof(inst->sync_config[i]->attrs)); } inst->sync_config[i]->persist = true; inst->sync_config[i]->user_ctx = listen; inst->sync_config[i]->cookie = _proto_ldap_cookie_store; inst->sync_config[i]->entry = _proto_ldap_entry; inst->sync_config[i]->refresh_required = _proto_ldap_refresh_required; inst->sync_config[i]->present = _proto_ldap_present; /* * Parse and validate any maps */ map_cs = cf_section_find(sync_cs, "update", NULL); if (map_cs && map_afrom_cs(inst, &inst->sync_config[i]->entry_map, map_cs, NULL, NULL, fr_ldap_map_verify, NULL, LDAP_MAX_ATTRMAP) < 0) { return -1; } }
/* * Check if an incoming request is "ok" * * It takes packets, not requests. It sees if the packet looks * OK. If so, it does a number of sanity checks on it. */ static int arp_socket_recv(rad_listen_t *listener) { int ret; arp_socket_t *sock = listener->data; pcap_t *handle = sock->lsock.pcap->handle; const uint8_t *data; struct pcap_pkthdr *header; ssize_t link_len; arp_over_ether_t const *arp; RADIUS_PACKET *packet; ret = pcap_next_ex(handle, &header, &data); if (ret == 0) { DEBUG("No packet retrieved from pcap."); return 0; /* no packet */ } if (ret < 0) { ERROR("Error requesting next packet, got (%i): %s", ret, pcap_geterr(handle)); return 0; } link_len = fr_pcap_link_layer_offset(data, header->caplen, sock->lsock.pcap->link_layer); if (link_len < 0) { PERROR("Failed determining link layer header offset"); return 0; } /* * Silently ignore it if it's too small to be ARP. * * This can happen when pcap gets overloaded and starts truncating packets. */ if (header->caplen < (link_len + sizeof(*arp))) { ERROR("Packet too small, we require at least %zu bytes, got %i bytes", link_len + sizeof(*arp), header->caplen); return 0; } data += link_len; arp = (arp_over_ether_t const *) data; if (ntohs(arp->htype) != ARPHRD_ETHER) return 0; if (ntohs(arp->ptype) != 0x0800) return 0; if (arp->hlen != ETHER_ADDR_LEN) return 0; /* FIXME: malformed error */ if (arp->plen != 4) return 0; /* FIXME: malformed packet error */ packet = talloc_zero(NULL, RADIUS_PACKET); if (!packet) return 0; packet->dst_port = 1; /* so it's not a "fake" request */ packet->data_len = header->caplen - link_len; packet->data = talloc_memdup(packet, arp, packet->data_len); talloc_set_type(packet->data, uint8_t); DEBUG("ARP received on interface %s", sock->lsock.interface); if (!request_receive(NULL, listener, packet, &sock->client, arp_process)) { fr_radius_packet_free(&packet); return 0; } return 1; }