/*
 *	Do any per-module initialization that is separate to each
 *	configured instance of the module.  e.g. set up connections
 *	to external databases, read configuration files, set up
 *	dictionary entries, etc.
 *
 *	If configuration information is given in the config section
 *	that must be referenced in later calls, store a handle to it
 *	in *instance otherwise put a null pointer there.
 */
static int checkval_instantiate(CONF_SECTION *conf, void **instance)
{
	rlm_checkval_t *data;
	DICT_ATTR *dattr;
	ATTR_FLAGS flags;

	static const FR_NAME_NUMBER names[] = {
		{ "string", PW_TYPE_STRING },
		{ "integer", PW_TYPE_INTEGER },
		{ "ipaddr", PW_TYPE_IPADDR },
		{ "date", PW_TYPE_DATE },
		{ "abinary", PW_TYPE_OCTETS },
		{ "octets", PW_TYPE_OCTETS },
		{ "binary", PW_TYPE_OCTETS },
		{ NULL, 0 }
	};

	/*
	 *	Set up a storage area for instance data
	 */
	data = rad_malloc(sizeof(*data));
	if (!data) {
		return -1;
	}
	memset(data, 0, sizeof(*data));

	/*
	 *	If the configuration parameters can't be parsed, then
	 *	fail.
	 */
	if (cf_section_parse(conf, data, module_config) < 0) {
		checkval_detach(data);
		return -1;
	}

	/*
	 * Check if data_type exists
	 */
	if (!data->data_type || !*data->data_type){
		radlog(L_ERR, "rlm_checkval: Data type not defined");
		checkval_detach(data);
		return -1;
	}
	if (!data->item_name || !*data->item_name){
		radlog(L_ERR, "rlm_checkval: Item name not defined");
		checkval_detach(data);
		return -1;
	}
	if (!data->check_name || !*data->check_name){
		radlog(L_ERR, "rlm_checkval: Check item name not defined");
		checkval_detach(data);
		return -1;
	}

	/*
	 *	Discover the attribute number of the item name
	 */
	dattr = dict_attrbyname(data->item_name);
	if (!dattr) {
		radlog(L_ERR, "rlm_checkval: No such attribute %s",
		       data->item_name);
		checkval_detach(data);
		return -1;
	}
	data->item_attr = dattr;

	/*
	 *	Add the check attribute name to the dictionary
	 *	if it does not already exists. dict_addattr() handles that
	 */

	memset(&flags, 0, sizeof(flags));
	dict_addattr(data->check_name, -1, 0, PW_TYPE_STRING, flags);
	dattr = dict_attrbyname(data->check_name);
	if (!dattr){
		radlog(L_ERR, "rlm_checkval: No such attribute %s",
		       data->check_name);
		checkval_detach(data);
		return -1;
	}
	data->chk_attr = dattr;
	DEBUG2("rlm_checkval: Registered name %s for attribute %d",
		dattr->name,dattr->attr);

	/*
	 *	Convert the string type to an integer type,
	 *	so we don't have to do string comparisons on each
	 *	packet.
	 */
	data->dat_type = fr_str2int(names, data->data_type, -1);
	if (data->dat_type < 0) {
		radlog(L_ERR, "rlm_checkval: Data type %s in not known",data->data_type);
		checkval_detach(data);
		return -1;
	}

	*instance = data;

	return 0;
}
int main(int argc, char **argv)
{
	int c;
	char const *radius_dir = RADDBDIR;
	char const *dict_dir = DICTDIR;
	char filesecret[256];
	FILE *fp;
	int do_summary = false;
	int persec = 0;
	int parallel = 1;
	rc_request_t	*this;
	int force_af = AF_UNSPEC;

	/*
	 *	It's easier having two sets of flags to set the
	 *	verbosity of library calls and the verbosity of
	 *	radclient.
	 */
	fr_debug_flag = 0;
	fr_log_fp = stdout;

#ifndef NDEBUG
	if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) {
		fr_perror("radclient");
		exit(EXIT_FAILURE);
	}
#endif

	talloc_set_log_stderr();

	filename_tree = rbtree_create(NULL, filename_cmp, NULL, 0);
	if (!filename_tree) {
	oom:
		ERROR("Out of memory");
		exit(1);
	}

	while ((c = getopt(argc, argv, "46c:d:D:f:Fhi:n:p:qr:sS:t:vx"
#ifdef WITH_TCP
		"P:"
#endif
			   )) != EOF) switch (c) {
		case '4':
			force_af = AF_INET;
			break;

		case '6':
			force_af = AF_INET6;
			break;

		case 'c':
			if (!isdigit((int) *optarg))
				usage();
			resend_count = atoi(optarg);
			break;

		case 'D':
			dict_dir = optarg;
			break;

		case 'd':
			radius_dir = optarg;
			break;

		case 'f':
		{
			char const *p;
			rc_file_pair_t *files;

			files = talloc(talloc_autofree_context(), rc_file_pair_t);
			if (!files) goto oom;

			p = strchr(optarg, ':');
			if (p) {
				files->packets = talloc_strndup(files, optarg, p - optarg);
				if (!files->packets) goto oom;
				files->filters = p + 1;
			} else {
				files->packets = optarg;
				files->filters = NULL;
			}
			rbtree_insert(filename_tree, (void *) files);
		}
			break;

		case 'F':
			print_filename = true;
			break;

		case 'i':	/* currently broken */
			if (!isdigit((int) *optarg))
				usage();
			last_used_id = atoi(optarg);
			if ((last_used_id < 0) || (last_used_id > 255)) {
				usage();
			}
			break;

		case 'n':
			persec = atoi(optarg);
			if (persec <= 0) usage();
			break;

			/*
			 *	Note that sending MANY requests in
			 *	parallel can over-run the kernel
			 *	queues, and Linux will happily discard
			 *	packets.  So even if the server responds,
			 *	the client may not see the reply.
			 */
		case 'p':
			parallel = atoi(optarg);
			if (parallel <= 0) usage();
			break;

#ifdef WITH_TCP
		case 'P':
			proto = optarg;
			if (strcmp(proto, "tcp") != 0) {
				if (strcmp(proto, "udp") == 0) {
					proto = NULL;
				} else {
					usage();
				}
			} else {
				ipproto = IPPROTO_TCP;
			}
			break;

#endif

		case 'q':
			do_output = false;
			fr_log_fp = NULL; /* no output from you, either! */
			break;

		case 'r':
			if (!isdigit((int) *optarg)) usage();
			retries = atoi(optarg);
			if ((retries == 0) || (retries > 1000)) usage();
			break;

		case 's':
			do_summary = true;
			break;

		case 'S':
		{
			char *p;
			fp = fopen(optarg, "r");
			if (!fp) {
			       ERROR("Error opening %s: %s", optarg, fr_syserror(errno));
			       exit(1);
			}
			if (fgets(filesecret, sizeof(filesecret), fp) == NULL) {
			       ERROR("Error reading %s: %s", 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) {
			       ERROR("Secret in %s is too short", optarg);
			       exit(1);
			}
			secret = filesecret;
		}
		       break;

		case 't':
			if (!isdigit((int) *optarg))
				usage();
			timeout = atof(optarg);
			break;

		case 'v':
			DEBUG("%s", radclient_version);
			exit(0);

		case 'x':
			fr_debug_flag++;
			break;

		case 'h':
		default:
			usage();
	}
	argc -= (optind - 1);
	argv += (optind - 1);

	if ((argc < 3)  || ((secret == NULL) && (argc < 4))) {
		ERROR("Insufficient arguments");
		usage();
	}
	/*
	 *	Mismatch between the binary and the libraries it depends on
	 */
	if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) {
		fr_perror("radclient");
		return 1;
	}

	if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) {
		fr_perror("radclient");
		return 1;
	}

	if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) {
		fr_perror("radclient");
		return 1;
	}
	fr_strerror();	/* Clear the error buffer */

	/*
	 *	Get the request type
	 */
	if (!isdigit((int) argv[2][0])) {
		packet_code = fr_str2int(request_types, argv[2], -2);
		if (packet_code == -2) {
			ERROR("Unrecognised request type \"%s\"", argv[2]);
			usage();
		}
	} else {
		packet_code = atoi(argv[2]);
	}

	/*
	 *	Resolve hostname.
	 */
	if (force_af == AF_UNSPEC) force_af = AF_INET;
	server_ipaddr.af = force_af;
	if (strcmp(argv[1], "-") != 0) {
		char *p;
		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(&server_ipaddr, force_af, hostname, false) < 0) {
			ERROR("Failed to find IP address for host %s: %s", hostname, strerror(errno));
			exit(1);
		}

		/*
		 *	Strip port from hostname if needed.
		 */
		if (portname) server_port = atoi(portname);

		/*
		 *	Work backwards from the port to determine the packet type
		 */
		if (packet_code == PW_CODE_UNDEFINED) packet_code = radclient_get_code(server_port);
	}
	radclient_get_port(packet_code, &server_port);

	/*
	 *	Add the secret.
	 */
	if (argv[3]) secret = argv[3];

	/*
	 *	If no '-f' is specified, we're reading from stdin.
	 */
	if (rbtree_num_elements(filename_tree) == 0) {
		rc_file_pair_t *files;

		files = talloc_zero(talloc_autofree_context(), rc_file_pair_t);
		files->packets = "-";
		if (!radclient_init(files, files)) {
			exit(1);
		}
	}

	/*
	 *	Walk over the list of filenames, creating the requests.
	 */
	if (rbtree_walk(filename_tree, RBTREE_IN_ORDER, filename_walk, NULL) != 0) {
		ERROR("Failed parsing input files");
		exit(1);
	}

	/*
	 *	No packets read.  Die.
	 */
	if (!request_head) {
		ERROR("Nothing to send");
		exit(1);
	}

	/*
	 *	Bind to the first specified IP address and port.
	 *	This means we ignore later ones.
	 */
	if (request_head->packet->src_ipaddr.af == AF_UNSPEC) {
		memset(&client_ipaddr, 0, sizeof(client_ipaddr));
		client_ipaddr.af = server_ipaddr.af;
	} else {
		client_ipaddr = request_head->packet->src_ipaddr;
	}

	client_port = request_head->packet->src_port;

#ifdef WITH_TCP
	if (proto) {
		sockfd = fr_socket_client_tcp(NULL, &server_ipaddr, server_port, false);
	} else
#endif
	sockfd = fr_socket(&client_ipaddr, client_port);
	if (sockfd < 0) {
		ERROR("Error opening socket");
		exit(1);
	}

	pl = fr_packet_list_create(1);
	if (!pl) {
		ERROR("Out of memory");
		exit(1);
	}

	if (!fr_packet_list_socket_add(pl, sockfd, ipproto, &server_ipaddr,
				       server_port, NULL)) {
		ERROR("Out of memory");
		exit(1);
	}

	/*
	 *	Walk over the list of packets, sanity checking
	 *	everything.
	 */
	for (this = request_head; this != NULL; this = this->next) {
		this->packet->src_ipaddr = client_ipaddr;
		this->packet->src_port = client_port;
		if (radclient_sane(this) != 0) {
			exit(1);
		}
	}

	/*
	 *	Walk over the packets to send, until
	 *	we're all done.
	 *
	 *	FIXME: This currently busy-loops until it receives
	 *	all of the packets.  It should really have some sort of
	 *	send packet, get time to wait, select for time, etc.
	 *	loop.
	 */
	do {
		int n = parallel;
		rc_request_t *next;
		char const *filename = NULL;

		done = true;
		sleep_time = -1;

		/*
		 *	Walk over the packets, sending them.
		 */

		for (this = request_head; this != NULL; this = next) {
			next = this->next;

			/*
			 *	If there's a packet to receive,
			 *	receive it, but don't wait for a
			 *	packet.
			 */
			recv_one_packet(0);

			/*
			 *	This packet is done.  Delete it.
			 */
			if (this->done) {
				talloc_free(this);
				continue;
			}

			/*
			 *	Packets from multiple '-f' are sent
			 *	in parallel.
			 *
			 *	Packets from one file are sent in
			 *	series, unless '-p' is specified, in
			 *	which case N packets from each file
			 *	are sent in parallel.
			 */
			if (this->files->packets != filename) {
				filename = this->files->packets;
				n = parallel;
			}

			if (n > 0) {
				n--;

				/*
				 *	Send the current packet.
				 */
				send_one_packet(this);

				/*
				 *	Wait a little before sending
				 *	the next packet, if told to.
				 */
				if (persec) {
					struct timeval tv;

					/*
					 *	Don't sleep elsewhere.
					 */
					sleep_time = 0;

					if (persec == 1) {
						tv.tv_sec = 1;
						tv.tv_usec = 0;
					} else {
						tv.tv_sec = 0;
						tv.tv_usec = 1000000/persec;
					}

					/*
					 *	Sleep for milliseconds,
					 *	portably.
					 *
					 *	If we get an error or
					 *	a signal, treat it like
					 *	a normal timeout.
					 */
					select(0, NULL, NULL, NULL, &tv);
				}

				/*
				 *	If we haven't sent this packet
				 *	often enough, we're not done,
				 *	and we shouldn't sleep.
				 */
				if (this->resend < resend_count) {
					done = false;
					sleep_time = 0;
				}
			} else { /* haven't sent this packet, we're not done */
				assert(this->done == false);
				assert(this->reply == NULL);
				done = false;
			}
		}

		/*
		 *	Still have outstanding requests.
		 */
		if (fr_packet_list_num_elements(pl) > 0) {
			done = false;
		} else {
			sleep_time = 0;
		}

		/*
		 *	Nothing to do until we receive a request, so
		 *	sleep until then.  Once we receive one packet,
		 *	we go back, and walk through the whole list again,
		 *	sending more packets (if necessary), and updating
		 *	the sleep time.
		 */
		if (!done && (sleep_time > 0)) {
			recv_one_packet(sleep_time);
		}
	} while (!done);

	rbtree_free(filename_tree);
	fr_packet_list_free(pl);
	while (request_head) TALLOC_FREE(request_head);
	dict_free();

	if (do_summary) {
		DEBUG("Packet summary:\n"
		      "\tAccepted      : %" PRIu64 "\n"
		      "\tRejected      : %" PRIu64 "\n"
		      "\tLost          : %" PRIu64 "\n"
		      "\tPassed filter : %" PRIu64 "\n"
		      "\tFailed filter : %" PRIu64,
		      stats.accepted,
		      stats.rejected,
		      stats.lost,
		      stats.passed,
		      stats.failed
		);
	}

	if ((stats.lost > 0) || (stats.failed > 0)) {
		exit(1);
	}
	exit(0);
}
Exemple #3
0
/*
 *	if (...) {...}
 *	if (...) {...} else {...}
 *	if (...) {...} else if ...
 */
static int parse_if(policy_lex_file_t *lexer, policy_item_t **tail)
{
	int rcode;
	policy_lex_t token;
	char mystring[256];
	policy_if_t *this;

	debug_tokens("[IF] ");

	this = rad_malloc(sizeof(*this));
	memset(this, 0, sizeof(*this));

	this->item.type = POLICY_TYPE_IF;
	this->item.lineno = lexer->lineno;

	rcode = parse_condition(lexer, &(this->condition));
	if (!rcode) {
		rlm_policy_free_item((policy_item_t *) this);
		return rcode;
	}

	rcode = parse_block(lexer, &(this->if_true));
	if (!rcode) {
		rlm_policy_free_item((policy_item_t *) this);
		return rcode;
	}

	token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
				mystring, sizeof(mystring));
	if ((token == POLICY_LEX_BARE_WORD) &&
	    (fr_str2int(policy_reserved_words, mystring,
			  POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_ELSE)) {
		debug_tokens("[ELSE] ");
		token = policy_lex_file(lexer, 0, mystring, sizeof(mystring));
		rad_assert(token == POLICY_LEX_BARE_WORD);

		token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK,
					mystring, sizeof(mystring));
		if ((token == POLICY_LEX_BARE_WORD) &&
		    (fr_str2int(policy_reserved_words, mystring,
				  POLICY_RESERVED_UNKNOWN) == POLICY_RESERVED_IF)) {
			token = policy_lex_file(lexer, 0,
						mystring, sizeof(mystring));
			rad_assert(token == POLICY_LEX_BARE_WORD);
			rcode = parse_if(lexer, &(this->if_false));
		} else {
			rcode = parse_block(lexer, &(this->if_false));
		}
		if (!rcode) {
			rlm_policy_free_item((policy_item_t *) this);
			return rcode;
		}
	}

	debug_tokens("\n");

	/*
	 *	Empty "if" condition, don't even bother remembering
	 *	it.
	 */
	if (!this->if_true && !this->if_false) {
		debug_tokens("Discarding empty \"if\" statement at line %d\n",
			     this->item.lineno);
		rlm_policy_free_item((policy_item_t *) this);
		return 1;
	}

	*tail = (policy_item_t *) this;

	return 1;
}
/*
 *	Read config files.
 *
 *	This function can ONLY be called from the main server process.
 */
int mainconfig_init(void)
{
	int rcode;
	char const *p = NULL;
	CONF_SECTION *cs;
	struct stat statbuf;
	cached_config_t *cc;
	char buffer[1024];

	if (stat(radius_dir, &statbuf) < 0) {
		ERROR("Errors reading %s: %s",
		       radius_dir, fr_syserror(errno));
		return -1;
	}

#ifdef S_IWOTH
	if ((statbuf.st_mode & S_IWOTH) != 0) {
		ERROR("Configuration directory %s is globally writable.  Refusing to start due to insecure configuration.",
		       radius_dir);
	  return -1;
	}
#endif

#ifdef S_IROTH
	if (0 && (statbuf.st_mode & S_IROTH) != 0) {
		ERROR("Configuration directory %s is globally readable.  Refusing to start due to insecure configuration.",
		       radius_dir);
		return -1;
	}
#endif
	INFO("Starting - reading configuration files ...");

	/*
	 *	We need to load the dictionaries before reading the
	 *	configuration files.  This is because of the
	 *	pre-compilation in conffile.c.  That should probably
	 *	be fixed to be done as a second stage.
	 */
	if (!mainconfig.dictionary_dir) {
		mainconfig.dictionary_dir = talloc_typed_strdup(NULL, DICTDIR);
	}

	/*
	 *	Read the distribution dictionaries first, then
	 *	the ones in raddb.
	 */
	DEBUG2("including dictionary file %s/%s", mainconfig.dictionary_dir, RADIUS_DICTIONARY);
	if (dict_init(mainconfig.dictionary_dir, RADIUS_DICTIONARY) != 0) {
		ERROR("Errors reading dictionary: %s",
		      fr_strerror());
		return -1;
	}

	/*
	 *	Try to load protocol-specific dictionaries.  It's OK
	 *	if they don't exist.
	 */
#ifdef WITH_DHCP
	dict_read(mainconfig.dictionary_dir, "dictionary.dhcp");
#endif

#ifdef WITH_VMPS
	dict_read(mainconfig.dictionary_dir, "dictionary.vqp");
#endif

	/*
	 *	It's OK if this one doesn't exist.
	 */
	rcode = dict_read(radius_dir, RADIUS_DICTIONARY);
	if (rcode == -1) {
		ERROR("Errors reading %s/%s: %s", radius_dir, RADIUS_DICTIONARY,
		      fr_strerror());
		return -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);
	}

	/* Read the configuration file */
	snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf",
		 radius_dir, mainconfig.name);
	if ((cs = cf_file_read(buffer)) == NULL) {
		ERROR("Errors reading or parsing %s", buffer);
		return -1;
	}

	/*
	 *	If there was no log destination set on the command line,
	 *	set it now.
	 */
	if (default_log.dst == L_DST_NULL) {
		if (cf_section_parse(cs, NULL, serverdest_config) < 0) {
			fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n");
			cf_file_free(cs);
			return -1;
		}

		if (!radlog_dest) {
			fprintf(stderr, "radiusd: Error: No log destination specified.\n");
			cf_file_free(cs);
			return -1;
		}

		default_log.dst = fr_str2int(log_str2dst, radlog_dest,
					      L_DST_NUM_DEST);
		if (default_log.dst == L_DST_NUM_DEST) {
			fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n",
				radlog_dest);
			cf_file_free(cs);
			return -1;
		}

		if (default_log.dst == L_DST_SYSLOG) {
			/*
			 *	Make sure syslog_facility isn't NULL
			 *	before using it
			 */
			if (!syslog_facility) {
				fprintf(stderr, "radiusd: Error: Syslog chosen but no facility was specified\n");
				cf_file_free(cs);
				return -1;
			}
			mainconfig.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1);
			if (mainconfig.syslog_facility < 0) {
				fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n",
					syslog_facility);
				cf_file_free(cs);
				return -1;
			}

#ifdef HAVE_SYSLOG_H
			/*
			 *	Call openlog only once, when the
			 *	program starts.
			 */
			openlog(progname, LOG_PID, mainconfig.syslog_facility);
#endif

		} else if (default_log.dst == L_DST_FILES) {
			if (!mainconfig.log_file) {
				fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n");
				cf_file_free(cs);
				return -1;
			}
		}
	}

#ifdef HAVE_SETUID
	/*
	 *	Switch users as early as possible.
	 */
	if (!switch_users(cs)) fr_exit(1);
#endif

	/*
	 *	Open the log file AFTER switching uid / gid.  If we
	 *	did switch uid/gid, then the code in switch_users()
	 *	took care of setting the file permissions correctly.
	 */
	if ((default_log.dst == L_DST_FILES) &&
	    (default_log.fd < 0)) {
		default_log.fd = open(mainconfig.log_file,
					    O_WRONLY | O_APPEND | O_CREAT, 0640);
		if (default_log.fd < 0) {
			fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, fr_syserror(errno));
			cf_file_free(cs);
			return -1;
		}
	}

	/*
	 *	This allows us to figure out where, relative to
	 *	radiusd.conf, the other configuration files exist.
	 */
	if (cf_section_parse(cs, NULL, server_config) < 0) {
		return -1;
	}

	/*
	 *	We ignore colourization of output until after the
	 *	configuration files have been parsed.
	 */
	p = getenv("TERM");
	if (do_colourise && p && isatty(default_log.fd) && strstr(p, "xterm")) {
		default_log.colourise = true;
	} else {
		default_log.colourise = false;
	}

	if (mainconfig.max_request_time == 0) mainconfig.max_request_time = 100;
	if (mainconfig.reject_delay > 5) mainconfig.reject_delay = 5;
	if (mainconfig.cleanup_delay > 5) mainconfig.cleanup_delay =5;

	/*
	 *	Free the old configuration items, and replace them
	 *	with the new ones.
	 *
	 *	Note that where possible, we do atomic switch-overs,
	 *	to ensure that the pointers are always valid.
	 */
	rad_assert(mainconfig.config == NULL);
	root_config = mainconfig.config = cs;

	DEBUG2("%s: #### Loading Realms and Home Servers ####", mainconfig.name);
	if (!realms_init(cs)) {
		return -1;
	}

	DEBUG2("%s: #### Loading Clients ####", mainconfig.name);
	if (!clients_parse_section(cs, false)) {
		return -1;
	}

	/*
	 *  Register the %{config:section.subsection} xlat function.
	 */
	xlat_register("config", xlat_config, NULL, NULL);
	xlat_register("client", xlat_client, NULL, NULL);
	xlat_register("getclient", xlat_getclient, NULL, NULL);

	/*
	 *	Starting the server, WITHOUT "-x" on the
	 *	command-line: use whatever is in the config
	 *	file.
	 */
	if (debug_flag == 0) {
		debug_flag = mainconfig.debug_level;
	}
	fr_debug_flag = debug_flag;

	/*
	 *  Go update our behaviour, based on the configuration
	 *  changes.
	 */

	/*
	 *	Sanity check the configuration for internal
	 *	consistency.
	 */
	if (mainconfig.reject_delay > mainconfig.cleanup_delay) {
		mainconfig.reject_delay = mainconfig.cleanup_delay;
	}
	if (mainconfig.reject_delay < 0) mainconfig.reject_delay = 0;

	if (chroot_dir) {
		if (chdir(radlog_dir) < 0) {
			ERROR("Failed to 'chdir %s' after chroot: %s",
			       radlog_dir, fr_syserror(errno));
			return -1;
		}
	}

	cc = talloc_zero(NULL, cached_config_t);
	if (!cc) return -1;

	cc->cs = talloc_steal(cc ,cs);
	rad_assert(cs_cache == NULL);
	cs_cache = cc;

	/* Clear any unprocessed configuration errors */
	(void) fr_strerror();

	return 0;
}
Exemple #5
0
/*
 *	Parse one statement.  'foo = bar', or 'if (...) {...}', or '{...}',
 *	and so on.
 */
static int parse_statement(policy_lex_file_t *lexer, policy_item_t **tail)
{
	int rcode;
	policy_reserved_word_t reserved;
	policy_lex_t token, assign;
	char lhs[256], rhs[256];
	policy_assignment_t *this;

	/*
	 *	See what kind of token we have.
	 */
	token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
	switch (token) {
	case POLICY_LEX_LC_BRACKET:
		rcode = parse_block(lexer, tail);
		if (!rcode) {
			return 0;
		}
		break;

	case POLICY_LEX_BARE_WORD:
		reserved = fr_str2int(policy_reserved_words,
					lhs,
					POLICY_RESERVED_UNKNOWN);
		switch (reserved) {
		case POLICY_RESERVED_IF:
			if (parse_if(lexer, tail)) {
				return 1;
			}
			return 0;
			break;

		case POLICY_RESERVED_CONTROL:
		case POLICY_RESERVED_REQUEST:
		case POLICY_RESERVED_REPLY:
		case POLICY_RESERVED_PROXY_REQUEST:
		case POLICY_RESERVED_PROXY_REPLY:
			if (parse_attribute_block(lexer, tail,
						  reserved))
				return 1;
			return 0;
			break;

		case POLICY_RESERVED_PRINT:
			if (parse_print(lexer, tail)) {
				return 1;
			}
			return 0;
			break;

		case POLICY_RESERVED_RETURN:
			if (parse_return(lexer, tail)) {
				return 1;
			}
			return 0;
			break;

		case POLICY_RESERVED_MODULE:
			if (parse_module(lexer, tail)) {
				return 1;
			}
			return 0;
			break;

		case POLICY_RESERVED_UNKNOWN: /* wasn't a reserved word */
			/*
			 *	Is a named policy, parse the reference to it.
			 */
			if (rlm_policy_find(lexer->policies, lhs) != NULL) {
				if (!parse_call(lexer, tail, lhs)) {
					return 0;
				}
				return 1;
			}

			{
				const DICT_ATTR *dattr;

				/*
				 *	Bare words MUST be dictionary attributes
				 */

				dattr = dict_attrbyname(lhs);
				if (!dattr) {
					fprintf(stderr, "%s[%d]: Expected attribute name, got \"%s\"\n",
						lexer->filename, lexer->lineno, lhs);
					return 0;
				}
				debug_tokens("%s[%d]: Got attribute %s\n",
					     lexer->filename, lexer->lineno,
					     lhs);
			}
			break;

		default:
			fprintf(stderr, "%s[%d]: Unexpected reserved word \"%s\"\n",
				lexer->filename, lexer->lineno, lhs);
			return 0;
		} /* switch over reserved words */
		break;

		/*
		 *	Return from nested blocks.
		 */
	case POLICY_LEX_RC_BRACKET:
		policy_lex_push_token(lexer, token);
		return 2;	/* magic */

	case POLICY_LEX_EOF:	/* nothing more to do */
		return 3;

	default:
		fprintf(stderr, "%s[%d]: Unexpected %s\n",
			lexer->filename, lexer->lineno,
			fr_int2str(policy_explanations,
				     token, "string"));
		break;
	}

	/*
	 *	Parse a bare statement.
	 */
	assign = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
	switch (assign) {
	case POLICY_LEX_ASSIGN:
	case POLICY_LEX_SET_EQUALS:
	case POLICY_LEX_AND_EQUALS:
	case POLICY_LEX_OR_EQUALS:
	case POLICY_LEX_PLUS_EQUALS:
		break;

	default:
		fprintf(stderr, "%s[%d]: Unexpected assign %s\n",
			lexer->filename, lexer->lineno,
			fr_int2str(policy_explanations,
				     assign, "string"));
		return 0;
	}

	this = rad_malloc(sizeof(*this));
	memset(this, 0, sizeof(*this));

	this->item.type = POLICY_TYPE_ASSIGNMENT;
	this->item.lineno = lexer->lineno;

	token = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
	if ((token != POLICY_LEX_BARE_WORD) &&
	    (token != POLICY_LEX_DOUBLE_QUOTED_STRING)) {
		fprintf(stderr, "%s[%d]: Unexpected rhs %s\n",
			lexer->filename, lexer->lineno,
			fr_int2str(policy_explanations,
				     token, "string"));
		rlm_policy_free_item((policy_item_t *) this);
		return 0;
	}
	this->rhs_type = token;
	this->rhs = strdup(rhs);

	token = policy_lex_file(lexer, POLICY_LEX_FLAG_RETURN_EOL,
				rhs, sizeof(rhs));
	if (token != POLICY_LEX_EOL) {
		fprintf(stderr, "%s[%d]: Expected EOL\n",
			lexer->filename, lexer->lineno);
		rlm_policy_free_item((policy_item_t *) this);
		return 0;
	}
	debug_tokens("[ASSIGN %s %s %s]\n",
	       lhs, fr_int2str(rlm_policy_tokens, assign, "?"), rhs);

	/*
	 *	Fill in the assignment struct
	 */
	this->lhs = strdup(lhs);
	this->assign = assign;

	*tail = (policy_item_t *) this;

	return 1;
}
Exemple #6
0
/*
 *	Parse data from a file into a policy language.
 */
int rlm_policy_parse(rbtree_t *policies, const char *filename)
{
	FILE *fp;
	policy_lex_t token;
	policy_lex_file_t mylexer, *lexer = NULL;
	char buffer[32];

	fp = fopen(filename, "r");
	if (!fp) {
		fprintf(stderr, "Failed to open %s: %s\n",
			filename, strerror(errno));
		return 0;
	}

	lexer = &mylexer;
	memset(lexer, 0, sizeof(*lexer));
	lexer->filename = filename;
	lexer->fp = fp;
	lexer->token = POLICY_LEX_BAD;
	lexer->parse = NULL;	/* initial input */
	lexer->policies = policies;

	do {
		int reserved;

		token = policy_lex_file(lexer, 0, buffer, sizeof(buffer));
		switch (token) {
		case POLICY_LEX_BARE_WORD:
			reserved = fr_str2int(policy_reserved_words,
						buffer,
						POLICY_RESERVED_UNKNOWN);
			switch (reserved) {
			case POLICY_RESERVED_POLICY:
				if (!parse_named_policy(lexer)) {
					return 0;
				}
				break;

			case POLICY_RESERVED_INCLUDE:
				if (!parse_include(lexer)) {
					return 0;
				}
				break;

			case POLICY_RESERVED_DEBUG:
				if (!parse_debug(lexer)) {
					return 0;
				}
				break;

			default:
				fprintf(stderr, "%s[%d]: Unexpected word \"%s\"\n",
					lexer->filename, lexer->lineno,
					buffer);
				return 0;
				break;
			} /* switch over reserved words */

		case POLICY_LEX_EOF:
			break;

		default:
			fprintf(stderr, "%s[%d]: Illegal input\n",
				lexer->filename, lexer->lineno);
			return 0;
		}
	} while (token != POLICY_LEX_EOF);

	if (((lexer->debug & POLICY_DEBUG_PRINT_POLICY) != 0) && fr_log_fp) {
		fprintf(fr_log_fp, "# rlm_policy \n");
	}

	debug_tokens("--------------------------------------------------\n");

	return 1;
}
Exemple #7
0
/** Build value pairs from the passed JSON object and add to the request
 *
 * Parse the passed JSON object and create value pairs that will be injected into
 * the given request for authorization.
 *
 * Example JSON document structure:
 * @code{.json}
 * {
 *   "docType": "raduser",
 *   "userName": "******",
 *   "config": {
 *     "SHA-Password": {
 *       "value": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3",
 *       "op": ":="
 *     }
 *   },
 *   "reply": {
 *     "Reply-Message": {
 *       "value": "Hidey Ho!",
 *       "op": "="
 *     }
 *   }
 * }
 * @endcode
 *
 * @param  json    The JSON object representation of the user documnent.
 * @param  section The pair section ("config" or "reply").
 * @param  request The request to which the generated pairs should be added.
 */
void *mod_json_object_to_value_pairs(json_object *json, const char *section, REQUEST *request)
{
	json_object *jobj, *jval, *jop;     /* json object pointers */
	TALLOC_CTX *ctx;                    /* talloc context for fr_pair_make */
	VALUE_PAIR *vp, **ptr;              /* value pair and value pair pointer for fr_pair_make */

	/* assign ctx and vps for fr_pair_make based on section */
	if (strcmp(section, "config") == 0) {
		ctx = request;
		ptr = &(request->config);
	} else if (strcmp(section, "reply") == 0) {
		ctx = request->reply;
		ptr = &(request->reply->vps);
	} else {
		/* log error - this shouldn't happen */
		RERROR("invalid section passed for fr_pair_make");
		/* return */
		return NULL;
	}

	/* get config payload */
	if (json_object_object_get_ex(json, section, &jobj)) {
		/* make sure we have the correct type */
		if (!fr_json_object_is_type(jobj, json_type_object)) {
			/* log error */
			RERROR("invalid json type for '%s' section - sections must be json objects", section);
			/* reuturn */
			return NULL;
		}
		/* loop through object */
		json_object_object_foreach(jobj, attribute, json_vp) {
			/* check for appropriate type in value and op */
			if (!fr_json_object_is_type(json_vp, json_type_object)) {
				/* log error */
				RERROR("invalid json type for '%s' attribute - attributes must be json objects",
				       attribute);
				/* return */
				return NULL;
			}
			/* debugging */
			RDEBUG("parsing '%s' attribute: %s => %s", section, attribute,
			       json_object_to_json_string(json_vp));
			/* create pair from json object */
			if (json_object_object_get_ex(json_vp, "value", &jval) &&
				json_object_object_get_ex(json_vp, "op", &jop)) {
				/* make correct pairs based on json object type */
				switch (fr_json_object_get_type(jval)) {
				case json_type_double:
				case json_type_int:
				case json_type_string:
					/* debugging */
					RDEBUG("adding '%s' attribute to '%s' section", attribute, section);
					/* add pair */
					vp = fr_pair_make(ctx, ptr, attribute, json_object_get_string(jval),
						fr_str2int(fr_tokens, json_object_get_string(jop), 0));
					/* check pair */
					if (!vp) {
						RERROR("could not build value pair for '%s' attribute (%s)",
						       attribute, fr_strerror());
						/* return */
						return NULL;
					}
					break;

				case json_type_object:
				case json_type_array:
					/* log error - we want to handle these eventually */
					RERROR("skipping unhandled nested json object or array value pair object");
					break;

				default:
					/* log error - this shouldn't ever happen */
					RERROR("skipping unhandled json type in value pair object");
					break;
				}
			} else {
				/* log error */
				RERROR("failed to get 'value' or 'op' element for '%s' attribute", attribute);
			}
		}
		/* return NULL */
		return NULL;
	}

	/* debugging */
	RDEBUG("couldn't find '%s' section in json object - not adding value pairs for this section", section);

	/* return NULL */
	return NULL;
}
Exemple #8
0
/*
 *	Parse a module statement.
 */
static int parse_module(policy_lex_file_t *lexer, policy_item_t **tail)
{
	int component;
	policy_lex_t token;
	policy_module_t *this;
	char *p;
	const char *section_name;
	char filename[1024];
	char buffer[2048];
	CONF_SECTION *cs, *subcs;
	modcallable *mc;

	/*
	 *	And the filename
	 */
	token = policy_lex_file(lexer, 0, filename, sizeof(filename));
	if (token != POLICY_LEX_DOUBLE_QUOTED_STRING) {
		fprintf(stderr, "%s[%d]: Expected filename, got \"%s\"\n",
			lexer->filename, lexer->lineno,
			fr_int2str(rlm_policy_tokens, token, "?"));
		return 0;
	}

	/*
	 *	See if we're including all of the files in a subdirectory.
	 */
	strlcpy(buffer, lexer->filename, sizeof(buffer));
	p = strrchr(buffer, '/');
	if (p) {
		strlcpy(p + 1, filename, sizeof(buffer) - 1 - (p - buffer));
	} else {
		snprintf(buffer, sizeof(buffer), "%s/%s",
			 radius_dir, filename);
	}

	/*
	 *	Include section calling a module.
	 */
	debug_tokens("including module section from file %s\n", buffer);
	cs = cf_file_read(buffer);
	if (!cs) {
		return 0;	/* it prints out error messages */
	}

	/*
	 *	The outer section is called "main", and can be ignored.
	 *	It should be a section, so there should be a subsection.
	 */
	subcs = cf_subsection_find_next(cs, NULL, NULL);
	if (!subcs) {
		fprintf(stderr, "%s[%d]: Expected section containing modules\n",
			lexer->filename, lexer->lineno);
		cf_section_free(&cs);
		return 0;
	}

	section_name = cf_section_name1(subcs);
	rad_assert(section_name != NULL);
	component = fr_str2int(policy_component_names, section_name,
				 RLM_COMPONENT_COUNT);
	if (component == RLM_COMPONENT_COUNT) {
		fprintf(stderr, "%s[%d]: Invalid section name \"%s\"\n",
			lexer->filename, lexer->lineno, section_name);
		cf_section_free(&cs);
		return 0;
	}

	/*
	 *	Compile the module entry.
	 */
	mc = compile_modgroup(NULL, component, subcs);
	if (!mc) {
		cf_section_free(&cs);
		return 0;	/* more often results in calling exit... */
	}

	this = rad_malloc(sizeof(*this));
	memset(this, 0, sizeof(*this));

	this->item.type = POLICY_TYPE_MODULE;
	this->item.lineno = lexer->lineno;
	this->component = component;
	this->cs = cs;
	this->mc = mc;

	*tail = (policy_item_t *) this;

	return 1;
}
Exemple #9
0
/*
 * filterBinary:
 *
 * This routine will call routines to parse entries from an ASCII format
 * to a binary format recognized by the Ascend boxes.
 *
 *	pair:			Pointer to value_pair to place return.
 *
 *	valstr:			The string to parse
 *
 *	return:			-1 for error or 0.
 */
int
ascend_parse_filter(VALUE_PAIR *pair)
{
	int		token, type;
	int		rcode;
	int		argc;
	char		*argv[32];
	ascend_filter_t filter;
	char		*p;

	rcode = -1;

	/*
	 *	Rather than printing specific error messages, we create
	 *	a general one here, which won't be used if the function
	 *	returns OK.
	 */
	fr_strerror_printf("Text is not in proper format");

	/*
	 *	Tokenize the input string in the VP.
	 *
	 *	Once the filter is *completelty* parsed, then we will
	 *	over-write it with the final binary filter.
	 */
	p = talloc_strdup(pair, pair->vp_strvalue);
	argc = str2argv(p, argv, 32);
	if (argc < 3) {
		talloc_free(p);
		return -1;
	}

	/*
	 *	Decide which filter type it is: ip, ipx, or generic
	 */
	type = fr_str2int(filterType, argv[0], -1);
	memset(&filter, 0, sizeof(filter));

	/*
	 *	Validate the filter type.
	 */
	switch (type) {
	case RAD_FILTER_GENERIC:
	case RAD_FILTER_IP:
	case RAD_FILTER_IPX:
		filter.type = type;
		break;

	default:
		fr_strerror_printf("Unknown Ascend filter type \"%s\"", argv[0]);
		talloc_free(p);
		return -1;
		break;
	}

	/*
	 *	Parse direction
	 */
	token = fr_str2int(filterKeywords, argv[1], -1);
	switch (token) {
	case FILTER_IN:
		filter.direction = 1;
		break;

	case FILTER_OUT:
		filter.direction = 0;
		break;

	default:
		fr_strerror_printf("Unknown Ascend filter direction \"%s\"", argv[1]);
		talloc_free(p);
		return -1;
		break;
	}

	/*
	 *	Parse action
	 */
	token = fr_str2int(filterKeywords, argv[2], -1);
	switch (token) {
	case FILTER_FORWARD:
		filter.forward = 1;
		break;

	case FILTER_DROP:
		filter.forward = 0;
		break;

	default:
		fr_strerror_printf("Unknown Ascend filter action \"%s\"", argv[2]);
		talloc_free(p);
		return -1;
		break;
	}


	switch (type) {
	case RAD_FILTER_GENERIC:
		rcode = ascend_parse_generic(argc - 3, &argv[3],
					  &filter.u.generic);
		break;

	case RAD_FILTER_IP:
		rcode = ascend_parse_ip(argc - 3, &argv[3], &filter.u.ip);
		break;

	case RAD_FILTER_IPX:
		rcode = ascend_parse_ipx(argc - 3, &argv[3], &filter.u.ipx);
		break;
	}

	/*
	 *	Touch the VP only if everything was OK.
	 */
	if (rcode == 0) {
		pair->length = sizeof(filter);
		memcpy(pair->vp_filter, &filter, sizeof(filter));
	}

	talloc_free(p);
	return rcode;

#if 0
    /*
     * if 'more' is set then this new entry must exist, be a
     * FILTER_GENERIC_TYPE, direction and disposition must match for
     * the previous 'more' to be valid. If any should fail then TURN OFF
     * previous 'more'
     */
    if( prevRadPair ) {
	filt = ( RadFilter * )prevRadPair->vp_strvalue;
	if(( tok != FILTER_GENERIC_TYPE ) || (rc == -1 ) ||
	   ( prevRadPair->attribute != pair->attribute ) ||
	   ( filt->indirection != radFil.indirection ) ||
	   ( filt->forward != radFil.forward ) ) {
	    gen = &filt->u.generic;
	    gen->more = false;
	    fr_strerror_printf("filterBinary:  'more' for previous entry doesn't match: %s.\n",
		     valstr);
	}
    }
    prevRadPair = NULL;
    if( rc != -1 && tok == FILTER_GENERIC_TYPE ) {
	if( radFil.u.generic.more ) {
	    prevRadPair = pair;
	}
    }

    if( rc != -1 ) {
	    pairmemcpy(pair, &radFil, pair->length );
    }
    return(rc);

#endif
}
Exemple #10
0
/*
 *	Expand the variables in an input string.
 */
char const *cf_expand_variables(char const *cf, int *lineno,
				CONF_SECTION *outer_cs,
				char *output, size_t outsize,
				char const *input, bool *soft_fail)
{
	char *p;
	char const *end, *ptr;
	CONF_SECTION const *parent_cs;
	char name[8192];

	if (soft_fail) *soft_fail = false;

	/*
	 *	Find the master parent conf section.
	 *	We can't use main_config->root_cs, because we're in the
	 *	process of re-building it, and it isn't set up yet...
	 */
	parent_cs = cf_root(outer_cs);

	p = output;
	ptr = input;
	while (*ptr) {
		/*
		 *	Ignore anything other than "${"
		 */
		if ((*ptr == '$') && (ptr[1] == '{')) {
			CONF_ITEM *ci;
			CONF_PAIR *cp;
			char *q;

			/*
			 *	FIXME: Add support for ${foo:-bar},
			 *	like in xlat.c
			 */

			/*
			 *	Look for trailing '}', and log a
			 *	warning for anything that doesn't match,
			 *	and exit with a fatal error.
			 */
			end = strchr(ptr, '}');
			if (end == NULL) {
				*p = '\0';
				INFO("%s[%d]: Variable expansion missing }",
				     cf, *lineno);
				return NULL;
			}

			ptr += 2;

			/*
			 *	Can't really happen because input lines are
			 *	capped at 8k, which is sizeof(name)
			 */
			if ((size_t) (end - ptr) >= sizeof(name)) {
				ERROR("%s[%d]: Reference string is too large",
				      cf, *lineno);
				return NULL;
			}

			memcpy(name, ptr, end - ptr);
			name[end - ptr] = '\0';

			q = strchr(name, ':');
			if (q) {
				*(q++) = '\0';
			}

			ci = cf_reference_item(parent_cs, outer_cs, name);
			if (!ci) {
				if (soft_fail) *soft_fail = true;
				ERROR("%s[%d]: Reference \"${%s}\" not found", cf, *lineno, name);
				return NULL;
			}

			/*
			 *	The expansion doesn't refer to another item or section
			 *	it's the property of a section.
			 */
			if (q) {
				CONF_SECTION *find = cf_item_to_section(ci);

				if (ci->type != CONF_ITEM_SECTION) {
					ERROR("%s[%d]: Can only reference properties of sections", cf, *lineno);
					return NULL;
				}

				switch (fr_str2int(conf_property_name, q, CONF_PROPERTY_INVALID)) {
				case CONF_PROPERTY_NAME:
					strcpy(p, find->name1);
					break;

				case CONF_PROPERTY_INSTANCE:
					strcpy(p, find->name2 ? find->name2 : find->name1);
					break;

				default:
					ERROR("%s[%d]: Invalid property '%s'", cf, *lineno, q);
					return NULL;
				}
				p += strlen(p);
				ptr = end + 1;

			} else if (ci->type == CONF_ITEM_PAIR) {
				/*
				 *  Substitute the value of the variable.
				 */
				cp = cf_item_to_pair(ci);

				/*
				 *	If the thing we reference is
				 *	marked up as being expanded in
				 *	pass2, don't expand it now.
				 *	Let it be expanded in pass2.
				 */
				if (cp->pass2) {
					if (soft_fail) *soft_fail = true;

					ERROR("%s[%d]: Reference \"%s\" points to a variable which has not been expanded.",
					      cf, *lineno, input);
					return NULL;
				}

				if (!cp->value) {
					ERROR("%s[%d]: Reference \"%s\" has no value",
					      cf, *lineno, input);
					return NULL;
				}

				if (p + strlen(cp->value) >= output + outsize) {
					ERROR("%s[%d]: Reference \"%s\" is too long",
					      cf, *lineno, input);
					return NULL;
				}

				strcpy(p, cp->value);
				p += strlen(p);
				ptr = end + 1;

			} else if (ci->type == CONF_ITEM_SECTION) {
				CONF_SECTION *subcs;

				/*
				 *	Adding an entry again to a
				 *	section is wrong.  We don't
				 *	want an infinite loop.
				 */
				if (cf_item_to_section(ci->parent) == outer_cs) {
					ERROR("%s[%d]: Cannot reference different item in same section", cf, *lineno);
					return NULL;
				}

				/*
				 *	Copy the section instead of
				 *	referencing it.
				 */
				subcs = cf_item_to_section(ci);
				subcs = cf_section_dup(outer_cs, outer_cs, subcs,
						       cf_section_name1(subcs), cf_section_name2(subcs),
						       false);
				if (!subcs) {
					ERROR("%s[%d]: Failed copying reference %s", cf, *lineno, name);
					return NULL;
				}

				subcs->item.filename = ci->filename;
				subcs->item.lineno = ci->lineno;
				cf_item_add(outer_cs, &(subcs->item));

				ptr = end + 1;

			} else {
				ERROR("%s[%d]: Reference \"%s\" type is invalid", cf, *lineno, input);
				return NULL;
			}
		} else if (strncmp(ptr, "$ENV{", 5) == 0) {
			char *env;

			ptr += 5;

			/*
			 *	Look for trailing '}', and log a
			 *	warning for anything that doesn't match,
			 *	and exit with a fatal error.
			 */
			end = strchr(ptr, '}');
			if (end == NULL) {
				*p = '\0';
				INFO("%s[%d]: Environment variable expansion missing }",
				     cf, *lineno);
				return NULL;
			}

			/*
			 *	Can't really happen because input lines are
			 *	capped at 8k, which is sizeof(name)
			 */
			if ((size_t) (end - ptr) >= sizeof(name)) {
				ERROR("%s[%d]: Environment variable name is too large",
				      cf, *lineno);
				return NULL;
			}

			memcpy(name, ptr, end - ptr);
			name[end - ptr] = '\0';

			/*
			 *	Get the environment variable.
			 *	If none exists, then make it an empty string.
			 */
			env = getenv(name);
			if (env == NULL) {
				*name = '\0';
				env = name;
			}

			if (p + strlen(env) >= output + outsize) {
				ERROR("%s[%d]: Reference \"%s\" is too long",
				      cf, *lineno, input);
				return NULL;
			}

			strcpy(p, env);
			p += strlen(p);
			ptr = end + 1;

		} else {
			/*
			 *	Copy it over verbatim.
			 */
			*(p++) = *(ptr++);
		}


		if (p >= (output + outsize)) {
			ERROR("%s[%d]: Reference \"%s\" is too long",
			      cf, *lineno, input);
			return NULL;
		}
	} /* loop over all of the input string. */

	*p = '\0';

	return output;
}
Exemple #11
0
/*
 *	ascend_parse_generic
 *
 *	This routine parses a Generic filter string from a RADIUS
 *	reply. The format of the string is:
 *
 *	generic dir action offset mask value [== or != ] [more]
 *
 *	Fields in [...] are optional.
 *
 *	offset:		A Number. Specifies an offset into a frame
 *			to start comparing.
 *
 *	mask:		A hexadecimal mask of bits to compare.
 *
 *	value:		A value to compare with the masked data.
 *
 *	compNeq:	Defines type of comparison. ( "==" or "!=")
 *			Default is "==".
 *
 *	more:		Optional keyword MORE, to represent the attachment
 *			to the next entry.
 */
static int ascend_parse_generic(int argc, char **argv,
				ascend_generic_filter_t *filter)
{
	int rcode;
	int token;
	int flags;

	/*
	 *	We may have nothing, in which case we simply return.
	 */
	if (argc == 0) return 0;

	/*
	 *	We need at least "offset mask value"
	 */
	if (argc < 3) return -1;

	/*
	 *	No more than optional comparison and "more"
	 */
	if (argc > 5) return -1;

	/*
	 *	Offset is a uint16_t number.
	 */
	if (strspn(argv[0], "0123456789") != strlen(argv[0])) return -1;

	rcode = atoi(argv[0]);
	if (rcode > 65535) return -1;

	filter->offset = rcode;
	filter->offset = htons(filter->offset);

	rcode = fr_hex2bin(argv[1], filter->mask, sizeof(filter->mask));
	if (rcode != sizeof(filter->mask)) return -1;

	token = fr_hex2bin(argv[2], filter->value, sizeof(filter->value));
	if (token != sizeof(filter->value)) return -1;

	/*
	 *	The mask and value MUST be the same length.
	 */
	if (rcode != token) return -1;

	filter->len = rcode;
	filter->len = htons(filter->len);

	/*
	 *	Nothing more.  Exit.
	 */
	if (argc == 3) return 0;

	argc -= 3;
	argv += 3;
	flags = 0;

	while (argc >= 1) {
		token = fr_str2int(filterKeywords, argv[0], -1);
		switch (token) {
		case FILTER_GENERIC_COMPNEQ:
			if (flags & 0x01) return -1;
			filter->compNeq = true;
			flags |= 0x01;
			break;
		case FILTER_GENERIC_COMPEQ:
			if (flags & 0x01) return -1;
			filter->compNeq = false;
			flags |= 0x01;
			break;

		case FILTER_MORE:
			if (flags & 0x02) return -1;
			filter->more = htons( 1 );
			flags |= 0x02;
			break;

		default:
			fr_strerror_printf("Invalid string \"%s\" in generic data filter",
				   argv[0]);
			return -1;
		}

		argc--;
		argv++;
	}

	return 0;
}
Exemple #12
0
/*
 *	ascend_parse_ip:
 *
 *	This routine parses an IP filter string from a RADIUS
 *	reply. The format of the string is:
 *
 *	ip dir action [ dstip n.n.n.n/nn ] [ srcip n.n.n.n/nn ]
 *	    [ proto [ dstport cmp value ] [ srcport cmd value ] [ est ] ]
 *
 *	Fields in [...] are optional.
 *
 *	dstip:		Keyword for destination IP address.
 *			n.n.n.n = IP address. /nn - netmask.
 *
 *	srcip:		Keyword for source IP address.
 *			n.n.n.n = IP address. /nn - netmask.
 *
 *	proto:		Optional protocol field. Either a name or
 *			number. Known names are in FilterProtoName[].
 *
 *	dstport:	Keyword for destination port. Only valid with tcp
 *			or udp. 'cmp' are in FilterPortType[]. 'value' can be
 *			a name or number.
 *
 *	srcport:	Keyword for source port. Only valid with tcp
 *			or udp. 'cmp' are in FilterPortType[]. 'value' can be
 *			a name or number.
 *
 *	est:		Keyword for TCP established. Valid only for tcp.
 *
 */
static int ascend_parse_ip(int argc, char **argv, ascend_ip_filter_t *filter)
{
	int rcode;
	int token;
	int flags;

	/*
	 *	We may have nothing, in which case we simply return.
	 */
	if (argc == 0) return 0;

	/*
	 *	There may, or may not, be src & dst IP's in the string.
	 */
	flags = 0;
	while ((argc > 0) && (flags != DONE_FLAGS)) {
		token = fr_str2int(filterKeywords, argv[0], -1);
		switch (token) {
		case FILTER_IP_SRC:
			if (flags & IP_SRC_ADDR_FLAG) return -1;
			if (argc < 2) return -1;

			rcode = ascend_parse_ipaddr(&filter->srcip, argv[1]);
			if (rcode < 0) return rcode;

			filter->srcmask = rcode;
			flags |= IP_SRC_ADDR_FLAG;
			argv += 2;
			argc -= 2;
			break;

		case FILTER_IP_DST:
			if (flags & IP_DEST_ADDR_FLAG) return -1;
			if (argc < 2) return -1;

			rcode = ascend_parse_ipaddr(&filter->dstip, argv[1]);
			if (rcode < 0) return rcode;

			filter->dstmask = rcode;
			flags |= IP_DEST_ADDR_FLAG;
			argv += 2;
			argc -= 2;
			break;

		case FILTER_IP_SRC_PORT:
			if (flags & IP_SRC_PORT_FLAG) return -1;
			if (argc < 3) return -1;

			rcode = ascend_parse_port(&filter->srcport,
						  argv[1], argv[2]);
			if (rcode < 0) return rcode;
			filter->srcPortComp = rcode;

			flags |= IP_SRC_PORT_FLAG;
			argv += 3;
			argc -= 3;
			break;

		case FILTER_IP_DST_PORT:
			if (flags & IP_DEST_PORT_FLAG) return -1;
			if (argc < 3) return -1;

			rcode = ascend_parse_port(&filter->dstport,
						  argv[1], argv[2]);
			if (rcode < 0) return rcode;
			filter->dstPortComp = rcode;

			flags |= IP_DEST_PORT_FLAG;
			argv += 3;
			argc -= 3;
			break;

		case FILTER_EST:
			if (flags & IP_EST_FLAG) return -1;
			filter->established = 1;
			argv++;
			argc--;
			flags |= IP_EST_FLAG;
			break;

		default:
			if (flags & IP_PROTO_FLAG) return -1;
			if (strspn(argv[0], "0123456789") == strlen(argv[0])) {
				token = atoi(argv[0]);
			} else {
				token = fr_str2int(filterProtoName, argv[0], -1);
				if (token == -1) {
					fr_strerror_printf("Unknown IP protocol \"%s\" in IP data filter",
						   argv[0]);
					return -1;
				}
			}
			filter->proto = token;
			flags |= IP_PROTO_FLAG;

			argv++;
			argc--;
			break;
		}
	}

	/*
	 *	We should have parsed everything by now.
	 */
	if (argc != 0) {
		fr_strerror_printf("Unknown extra string \"%s\" in IP data filter",
			   argv[0]);
		return -1;
	}

	return 0;
}
Exemple #13
0
/*
 *	ascend_parse_ipx_net
 *
 *	srcipxnet nnnn srcipxnode mmmmm [srcipxsoc cmd value ]
 */
static int ascend_parse_ipx_net(int argc, char **argv,
				ascend_ipx_net_t *net, uint8_t *comp)
{
	int		token;
	char const	*p;

	if (argc < 3) return -1;

	/*
	 *	Parse the net, which is a hex number.
	 */
	net->net = htonl(strtol(argv[0], NULL, 16));

	/*
	 *	Parse the node.
	 */
	token = fr_str2int(filterKeywords, argv[1], -1);
	switch (token) {
	case FILTER_IPX_SRC_IPXNODE:
	case FILTER_IPX_DST_IPXNODE:
		break;

	default:
		return -1;
	}

	/*
	 *	Can have a leading "0x" or "0X"
	 */
	p = argv[2];
	if ((memcmp(p, "0X", 2) == 0) ||
	    (memcmp(p, "0x", 2) == 0)) p += 2;

	/*
	 *	Node must be 6 octets long.
	 */
	token = fr_hex2bin(p, net->node, IPX_NODE_ADDR_LEN);
	if (token != IPX_NODE_ADDR_LEN) return -1;

	/*
	 *	Nothing more, die.
	 */
	if (argc == 3) return 3;

	/*
	 *	Can't be too little or too much.
	 */
	if (argc != 6) return -1;

	/*
	 *	Parse the socket.
	 */
	token = fr_str2int(filterKeywords, argv[3], -1);
	switch (token) {
	case FILTER_IPX_SRC_IPXSOCK:
	case FILTER_IPX_DST_IPXSOCK:
		break;

	default:
		return -1;
	}

	/*
	 *	Parse the command "<", ">", "=" or "!="
	 */
	token = fr_str2int(filterCompare, argv[4], -1);
	switch (token) {
	case RAD_COMPARE_LESS:
	case RAD_COMPARE_EQUAL:
	case RAD_COMPARE_GREATER:
	case RAD_COMPARE_NOT_EQUAL:
		*comp = token;
		break;

	default:
		return -1;
	}

	/*
	 *	Parse the value.
	 */
	token = strtoul(argv[5], NULL, 16);
	if (token > 65535) return -1;

	net->socket = token;
	net->socket = htons(net->socket);


	/*
	 *	Everything's OK, we parsed 6 entries.
	 */
	return 6;
}
Exemple #14
0
/** Convert an 'update' config section into an attribute map.
 *
 * Uses 'name2' of section to set default request and lists.
 * Copied from map_afrom_cs, except that list assignments can have the RHS
 * be a bare word.
 *
 * @param[in] cs the update section
 * @param[out] out Where to store the head of the map.
 * @param[in] dst_list_def The default destination list, usually dictated by
 * 	the section the module is being called in.
 * @param[in] src_list_def The default source list, usually dictated by the
 *	section the module is being called in.
 * @param[in] max number of mappings to process.
 * @return -1 on error, else 0.
 */
static int ldap_map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list_def, pair_lists_t src_list_def,
			     unsigned int max)
{
	char const *cs_list, *p;

	request_refs_t request_def = REQUEST_CURRENT;

	CONF_ITEM *ci;

	unsigned int total = 0;
	value_pair_map_t **tail, *map;
	TALLOC_CTX *ctx;

	*out = NULL;
	tail = out;

	if (!cs) return 0;

	/*
	 *	The first map has cs as the parent.
	 *	The rest have the previous map as the parent.
	 */
	ctx = cs;

	ci = cf_sectiontoitem(cs);

	cs_list = p = cf_section_name2(cs);
	if (cs_list) {
		request_def = radius_request_name(&p, REQUEST_CURRENT);
		if (request_def == REQUEST_UNKNOWN) {
			cf_log_err(ci, "Default request specified "
				   "in mapping section is invalid");
			return -1;
		}

		dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN);
		if (dst_list_def == PAIR_LIST_UNKNOWN) {
			cf_log_err(ci, "Default list \"%s\" specified "
				   "in mapping section is invalid", p);
			return -1;
		}
	}

	for (ci = cf_item_find_next(cs, NULL);
	     ci != NULL;
	     ci = cf_item_find_next(cs, ci)) {
		char const *attr;
		FR_TOKEN type;
		CONF_PAIR *cp;

		cp = cf_itemtopair(ci);

		type = cf_pair_value_type(cp);

		if (total++ == max) {
			cf_log_err(ci, "Map size exceeded");
			goto error;
		}

		if (!cf_item_is_pair(ci)) {
			cf_log_err(ci, "Entry is not in \"attribute = value\" format");
			goto error;
		}

		cp = cf_itemtopair(ci);

		/*
		 *	Look for "list: OP BARE_WORD".  If it exists,
		 *	we can make the RHS a bare word.  Otherwise,
		 *	just call map_afrom_cp()
		 *
		 *	Otherwise, the map functions check the RHS of
		 *	list assignments, and complain that the RHS
		 *	isn't another list.
		 */
		attr = cf_pair_attr(cp);

		p = strrchr(attr, ':');
		if (!p || (p[1] != '\0') || (type == T_DOUBLE_QUOTED_STRING)) {
			if (map_afrom_cp(ctx, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) {
				goto error;
			}
		} else {
			ssize_t slen;
			char const *value;

			map = talloc_zero(ctx, value_pair_map_t);
			map->op = cf_pair_operator(cp);
			map->ci = cf_pairtoitem(cp);

			slen = tmpl_afrom_attr_str(ctx, &map->lhs, attr, request_def, dst_list_def);
			if (slen <= 0) {
				char *spaces, *text;

				fr_canonicalize_error(ctx, &spaces, &text, slen, attr);
				
				cf_log_err(ci, "Failed parsing list reference");
				cf_log_err(ci, "%s", text);
				cf_log_err(ci, "%s^ %s", spaces, fr_strerror());

				talloc_free(spaces);
				talloc_free(text);
				goto error;
			}
		      
			if (map->lhs->type != TMPL_TYPE_LIST) {
				cf_log_err(map->ci, "Invalid list name");
				goto error;
			}
			
			if (map->op != T_OP_ADD) {
				cf_log_err(map->ci, "Only '+=' operator is permitted for valuepair to list mapping");
				goto error;
			}

			value = cf_pair_value(cp);
			if (!value) {
				cf_log_err(map->ci, "No value specified for list assignment");
				goto error;
			}

			/*
			 *	the RHS type is a bare word or single
			 *	quoted string.  We don't want it being
			 *	interpreted as a list or attribute
			 *	reference, so we force the RHS to be a
			 *	literal.
			 */
			slen = tmpl_afrom_str(ctx, &map->rhs, value, T_SINGLE_QUOTED_STRING, request_def, dst_list_def);
			if (slen <= 0) {
				char *spaces, *text;

				fr_canonicalize_error(ctx, &spaces, &text, slen, value);
				
				cf_log_err(ci, "Failed parsing string");
				cf_log_err(ci, "%s", text);
				cf_log_err(ci, "%s^ %s", spaces, fr_strerror());

				talloc_free(spaces);
				talloc_free(text);
				goto error;
			}

			/*
			 *	And unlike map_afrom_cp(), we do NOT
			 *	try to parse the RHS as a list
			 *	reference.  It's a literal, and we
			 *	leave it as a literal.
			 */
			rad_assert(map->rhs->type == TMPL_TYPE_LITERAL);
		}

		ctx = *tail = map;
		tail = &(map->next);
	}

	return 0;
error:
	TALLOC_FREE(*out);
	return -1;
}
Exemple #15
0
/*
 *	*presult is "did comparison match or not"
 */
static int radius_do_cmp(REQUEST *request, int *presult,
			 FR_TOKEN lt, const char *pleft, FR_TOKEN token,
			 FR_TOKEN rt, const char *pright,
			 int cflags, int modreturn)
{
	int result;
	uint32_t lint, rint;
	VALUE_PAIR *vp = NULL;
#ifdef HAVE_REGEX_H
	char buffer[8192];
#else
	cflags = cflags;	/* -Wunused */
#endif

	rt = rt;		/* -Wunused */

	if (lt == T_BARE_WORD) {
		/*
		 *	Maybe check the last return code.
		 */
		if (token == T_OP_CMP_TRUE) {
			int isreturn;

			/*
			 *	Looks like a return code, treat is as such.
			 */
			isreturn = fr_str2int(modreturn_table, pleft, -1);
			if (isreturn != -1) {
				*presult = (modreturn == isreturn);
				return TRUE;
			}
		}

		/*
		 *	Bare words on the left can be attribute names.
		 */
		if (!(radius_get_vp(request, pleft, &vp) < 0)) {
			VALUE_PAIR myvp;

			/*
			 *	VP exists, and that's all we're looking for.
			 */
			if (token == T_OP_CMP_TRUE) {
				*presult = (vp != NULL);
				return TRUE;
			}

			if (!vp) {
				const DICT_ATTR *da;
				
				/*
				 *	The attribute on the LHS may
				 *	have been a dynamically
				 *	registered callback.  i.e. it
				 *	doesn't exist as a VALUE_PAIR.
				 *	If so, try looking for it.
				 */
				da = dict_attrbyname(pleft);
				if (da && (da->vendor == 0) && radius_find_compare(da->attr)) {
					VALUE_PAIR *check = pairmake(pleft, pright, token);
					*presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0);
					RDEBUG3("  Callback returns %d",
						*presult);
					pairfree(&check);
					return TRUE;
				}
				
				RDEBUG2("    (Attribute %s was not found)",
				       pleft);
				*presult = 0;
				return TRUE;
			}

#ifdef HAVE_REGEX_H
			/*
			 * 	Regex comparisons treat everything as
			 *	strings.
			 */
			if ((token == T_OP_REG_EQ) ||
			    (token == T_OP_REG_NE)) {
				vp_prints_value(buffer, sizeof(buffer), vp, 0);
				pleft = buffer;
				goto do_checks;
			}
#endif

			memcpy(&myvp, vp, sizeof(myvp));
			if (!pairparsevalue(&myvp, pright)) {
				RDEBUG2("Failed parsing \"%s\": %s",
				       pright, fr_strerror());
				return FALSE;
			}

			myvp.op = token;
			*presult = paircmp(&myvp, vp);
			RDEBUG3("  paircmp -> %d", *presult);
			return TRUE;
		} /* else it's not a VP in a list */
	}

#ifdef HAVE_REGEX_H
	do_checks:
#endif
	switch (token) {
	case T_OP_GE:
	case T_OP_GT:
	case T_OP_LE:
	case T_OP_LT:
		if (!all_digits(pright)) {
			RDEBUG2("    (Right field is not a number at: %s)", pright);
			return FALSE;
		}
		rint = strtoul(pright, NULL, 0);
		if (!all_digits(pleft)) {
			RDEBUG2("    (Left field is not a number at: %s)", pleft);
			return FALSE;
		}
		lint = strtoul(pleft, NULL, 0);
		break;
		
	default:
		lint = rint = 0;  /* quiet the compiler */
		break;
	}
	
	switch (token) {
	case T_OP_CMP_TRUE:
		/*
		 *	Check for truth or falsehood.
		 */
		if (all_digits(pleft)) {
			lint = strtoul(pleft, NULL, 0);
			result = (lint != 0);
			
		} else {
			result = (*pleft != '\0');
		}
		break;
		

	case T_OP_CMP_EQ:
		result = (strcmp(pleft, pright) == 0);
		break;
		
	case T_OP_NE:
		result = (strcmp(pleft, pright) != 0);
		break;
		
	case T_OP_GE:
		result = (lint >= rint);
		break;
		
	case T_OP_GT:
		result = (lint > rint);
		break;
		
	case T_OP_LE:
		result = (lint <= rint);
		break;
		
	case T_OP_LT:
		result = (lint < rint);
		break;

#ifdef HAVE_REGEX_H
	case T_OP_REG_EQ: {
		int i, compare;
		regex_t reg;
		regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
		
		/*
		 *	Include substring matches.
		 */
		compare = regcomp(&reg, pright, cflags);
		if (compare != 0) {
			if (debug_flag) {
				char errbuf[128];

				regerror(compare, &reg, errbuf, sizeof(errbuf));
				DEBUGE("Failed compiling regular expression: %s", errbuf);
			}
			return FALSE;
		}

		compare = regexec(&reg, pleft,
				  REQUEST_MAX_REGEX + 1,
				  rxmatch, 0);
		regfree(&reg);
		
		/*
		 *	Add new %{0}, %{1}, etc.
		 */
		if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) {
			char *r;

			free(request_data_get(request, request,
					      REQUEST_DATA_REGEX | i));

			/*
			 *	No %{i}, skip it.
			 *	We MAY have %{2} without %{1}.
			 */
			if (rxmatch[i].rm_so == -1) continue;
			
			/*
			 *	Copy substring into allocated buffer
			 */
			r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1);
			memcpy(r, pleft + rxmatch[i].rm_so,
			       rxmatch[i].rm_eo - rxmatch[i].rm_so);
			r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0';

			request_data_add(request, request,
					 REQUEST_DATA_REGEX | i,
					 r, free);
		}
		result = (compare == 0);
	}
		break;
		
	case T_OP_REG_NE: {
		int compare;
		regex_t reg;
		regmatch_t rxmatch[REQUEST_MAX_REGEX + 1];
		
		/*
		 *	Include substring matches.
		 */
		compare = regcomp(&reg, pright, cflags);
		if (compare != 0) {
			if (debug_flag) {
				char errbuf[128];

				regerror(compare, &reg, errbuf, sizeof(errbuf));
				DEBUGE("Failed compiling regular expression: %s", errbuf);
			}
			return FALSE;
		}

		compare = regexec(&reg, pleft,
				  REQUEST_MAX_REGEX + 1,
				  rxmatch, 0);
		regfree(&reg);
		
		result = (compare != 0);
	}
		break;
#endif
		
	default:
		DEBUGE("Comparison operator %s is not supported",
		      fr_token_name(token));
		result = FALSE;
		break;
	}
	
	*presult = result;
	return TRUE;
}