Example #1
0
/** Copy packet to multiple servers
 *
 * Create a duplicate of the packet and send it to a list of realms
 * defined by the presence of the Replicate-To-Realm VP in the control
 * list of the current request.
 *
 * This is pretty hacky and is 100% fire and forget. If you're looking
 * to forward authentication requests to multiple realms and process
 * the responses, this function will not allow you to do that.
 *
 * @param[in] instance 	of this module.
 * @param[in] request 	The current request.
 * @param[in] list	of attributes to copy to the duplicate packet.
 * @param[in] code	to write into the code field of the duplicate packet.
 * @return RCODE fail on error, invalid if list does not exist, noop if no replications succeeded, else ok.
 */
static int replicate_packet(UNUSED void *instance, REQUEST *request, pair_lists_t list, unsigned int code)
{
	int rcode = RLM_MODULE_NOOP;
	VALUE_PAIR *vp, **vps;
	vp_cursor_t cursor;
	home_server_t *home;
	REALM *realm;
	home_pool_t *pool;
	RADIUS_PACKET *packet = NULL;

	/*
	 *	Send as many packets as necessary to different
	 *	destinations.
	 */
	fr_cursor_init(&cursor, &request->config);
	while ((vp = fr_cursor_next_by_num(&cursor, PW_REPLICATE_TO_REALM, 0, TAG_ANY))) {
		realm = realm_find2(vp->vp_strvalue);
		if (!realm) {
			REDEBUG2("Cannot Replicate to unknown realm \"%s\"", vp->vp_strvalue);
			continue;
		}

		/*
		 *	We shouldn't really do this on every loop.
		 */
		switch (request->packet->code) {
		default:
			REDEBUG2("Cannot replicate unknown packet code %d", request->packet->code);
			cleanup(packet);
			return RLM_MODULE_FAIL;

		case PW_CODE_ACCESS_REQUEST:
			pool = realm->auth_pool;
			break;

#ifdef WITH_ACCOUNTING

		case PW_CODE_ACCOUNTING_REQUEST:
			pool = realm->acct_pool;
			break;
#endif

#ifdef WITH_COA
		case PW_CODE_COA_REQUEST:
		case PW_CODE_DISCONNECT_REQUEST:
			pool = realm->coa_pool;
			break;
#endif
		}

		if (!pool) {
			RWDEBUG2("Cancelling replication to Realm %s, as the realm is local", realm->name);
			continue;
		}

		home = home_server_ldb(realm->name, pool, request);
		if (!home) {
			REDEBUG2("Failed to find live home server for realm %s", realm->name);
			continue;
		}

		/*
		 *	For replication to multiple servers we re-use the packet
		 *	we built here.
		 */
		if (!packet) {
			packet = rad_alloc(request, true);
			if (!packet) {
				return RLM_MODULE_FAIL;
			}

			packet->code = code;
			packet->id = fr_rand() & 0xff;
			packet->sockfd = fr_socket(&home->src_ipaddr, 0);
			if (packet->sockfd < 0) {
				REDEBUG("Failed opening socket: %s", fr_strerror());
				rcode = RLM_MODULE_FAIL;
				goto done;
			}

			vps = radius_list(request, list);
			if (!vps) {
				RWDEBUG("List '%s' doesn't exist for this packet",
					fr_int2str(pair_lists, list, "<INVALID>"));
				rcode = RLM_MODULE_INVALID;
				goto done;
			}

			/*
			 *	Don't assume the list actually contains any
			 *	attributes.
			 */
			if (*vps) {
				packet->vps = fr_pair_list_copy(packet, *vps);
				if (!packet->vps) {
					rcode = RLM_MODULE_FAIL;
					goto done;
				}
			}

			/*
			 *	For CHAP, create the CHAP-Challenge if
			 *	it doesn't exist.
			 */
			if ((code == PW_CODE_ACCESS_REQUEST) &&
			    (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
			    (fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
				uint8_t *p;
				vp = radius_pair_create(packet, &packet->vps, PW_CHAP_CHALLENGE, 0);
				vp->length = AUTH_VECTOR_LEN;
				vp->vp_octets = p = talloc_array(vp, uint8_t, vp->length);
				memcpy(p, request->packet->vector, AUTH_VECTOR_LEN);
			}
		} else {
			size_t i;

			for (i = 0; i < sizeof(packet->vector); i++) {
				packet->vector[i] = fr_rand() & 0xff;
			}

			packet->id++;
			TALLOC_FREE(packet->data);
			packet->data_len = 0;
		}

		/*
		 *	(Re)-Write these.
		 */
		packet->dst_ipaddr = home->ipaddr;
		packet->dst_port = home->port;
		memset(&packet->src_ipaddr, 0, sizeof(packet->src_ipaddr));
		packet->src_port = 0;

		/*
		 *	Encode, sign and then send the packet.
		 */
		RDEBUG("Replicating list '%s' to Realm '%s'", fr_int2str(pair_lists, list, "<INVALID>"),
		       realm->name);
		if (rad_send(packet, NULL, home->secret) < 0) {
			REDEBUG("Failed replicating packet: %s", fr_strerror());
			rcode = RLM_MODULE_FAIL;
			goto done;
		}

		/*
		 *	We've sent it to at least one destination.
		 */
		rcode = RLM_MODULE_OK;
	}

	done:

	cleanup(packet);
	return rcode;
}
Example #2
0
static REQUEST *request_setup(FILE *fp)
{
	VALUE_PAIR	*vp;
	REQUEST		*request;
	vp_cursor_t	cursor;
	struct timeval	now;

	/*
	 *	Create and initialize the new request.
	 */
	request = request_alloc(NULL);
	gettimeofday(&now, NULL);
	request->timestamp = now.tv_sec;

	request->packet = rad_alloc(request, false);
	if (!request->packet) {
		ERROR("No memory");
		talloc_free(request);
		return NULL;
	}
	request->packet->timestamp = now;

	request->reply = rad_alloc(request, false);
	if (!request->reply) {
		ERROR("No memory");
		talloc_free(request);
		return NULL;
	}

	request->listener = listen_alloc(request);
	request->client = client_alloc(request);

	request->number = 0;

	request->master_state = REQUEST_ACTIVE;
	request->child_state = REQUEST_RUNNING;
	request->handle = NULL;
	request->server = talloc_typed_strdup(request, "default");

	request->root = &main_config;

	/*
	 *	Read packet from fp
	 */
	if (fr_pair_list_afrom_file(request->packet, &request->packet->vps, fp, &filedone) < 0) {
		fr_perror("unittest");
		talloc_free(request);
		return NULL;
	}

	/*
	 *	Set the defaults for IPs, etc.
	 */
	request->packet->code = PW_CODE_ACCESS_REQUEST;

	request->packet->src_ipaddr.af = AF_INET;
	request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->src_port = 18120;

	request->packet->dst_ipaddr.af = AF_INET;
	request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->dst_port = 1812;

	/*
	 *	Copied from radclient
	 */
#if 1
	/*
	 *	Fix up Digest-Attributes issues
	 */
	for (vp = fr_cursor_init(&cursor, &request->packet->vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		/*
		 *	Double quoted strings get marked up as xlat expansions,
		 *	but we don't support that here.
		 */
		if (vp->type == VT_XLAT) {
			vp->vp_strvalue = vp->xlat;
			vp->xlat = NULL;
			vp->type = VT_DATA;
		}

		if (!vp->da->vendor) switch (vp->da->attr) {
		default:
			break;

			/*
			 *	Allow it to set the packet type in
			 *	the attributes read from the file.
			 */
		case PW_PACKET_TYPE:
			request->packet->code = vp->vp_integer;
			break;

		case PW_PACKET_DST_PORT:
			request->packet->dst_port = (vp->vp_integer & 0xffff);
			break;

		case PW_PACKET_DST_IP_ADDRESS:
			request->packet->dst_ipaddr.af = AF_INET;
			request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
			request->packet->dst_ipaddr.prefix = 32;
			break;

		case PW_PACKET_DST_IPV6_ADDRESS:
			request->packet->dst_ipaddr.af = AF_INET6;
			request->packet->dst_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
			request->packet->dst_ipaddr.prefix = 128;
			break;

		case PW_PACKET_SRC_PORT:
			request->packet->src_port = (vp->vp_integer & 0xffff);
			break;

		case PW_PACKET_SRC_IP_ADDRESS:
			request->packet->src_ipaddr.af = AF_INET;
			request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = vp->vp_ipaddr;
			request->packet->src_ipaddr.prefix = 32;
			break;

		case PW_PACKET_SRC_IPV6_ADDRESS:
			request->packet->src_ipaddr.af = AF_INET6;
			request->packet->src_ipaddr.ipaddr.ip6addr = vp->vp_ipv6addr;
			request->packet->src_ipaddr.prefix = 128;
			break;

		case PW_CHAP_PASSWORD: {
			int i, already_hex = 0;

			/*
			 *	If it's 17 octets, it *might* be already encoded.
			 *	Or, it might just be a 17-character password (maybe UTF-8)
			 *	Check it for non-printable characters.  The odds of ALL
			 *	of the characters being 32..255 is (1-7/8)^17, or (1/8)^17,
			 *	or 1/(2^51), which is pretty much zero.
			 */
			if (vp->vp_length == 17) {
				for (i = 0; i < 17; i++) {
					if (vp->vp_octets[i] < 32) {
						already_hex = 1;
						break;
					}
				}
			}

			/*
			 *	Allow the user to specify ASCII or hex CHAP-Password
			 */
			if (!already_hex) {
				uint8_t *p;
				size_t len, len2;

				len = len2 = vp->vp_length;
				if (len2 < 17) len2 = 17;

				p = talloc_zero_array(vp, uint8_t, len2);

				memcpy(p, vp->vp_strvalue, len);

				rad_chap_encode(request->packet,
						p,
						fr_rand() & 0xff, vp);
				vp->vp_octets = p;
				vp->vp_length = 17;
			}
		}
			break;

		case PW_DIGEST_REALM:
		case PW_DIGEST_NONCE:
		case PW_DIGEST_METHOD:
		case PW_DIGEST_URI:
		case PW_DIGEST_QOP:
		case PW_DIGEST_ALGORITHM:
		case PW_DIGEST_BODY_DIGEST:
		case PW_DIGEST_CNONCE:
		case PW_DIGEST_NONCE_COUNT:
		case PW_DIGEST_USER_NAME:
			/* overlapping! */
		{
			DICT_ATTR const *da;
			uint8_t *p, *q;

			p = talloc_array(vp, uint8_t, vp->vp_length + 2);

			memcpy(p + 2, vp->vp_octets, vp->vp_length);
			p[0] = vp->da->attr - PW_DIGEST_REALM + 1;
			vp->vp_length += 2;
			p[1] = vp->vp_length;

			da = dict_attrbyvalue(PW_DIGEST_ATTRIBUTES, 0);
			rad_assert(da != NULL);
			vp->da = da;

			/*
			 *	Re-do fr_pair_value_memsteal ourselves,
			 *	because we play games with
			 *	vp->da, and fr_pair_value_memsteal goes
			 *	to GREAT lengths to sanitize
			 *	and fix and change and
			 *	double-check the various
			 *	fields.
			 */
			memcpy(&q, &vp->vp_octets, sizeof(q));
			talloc_free(q);

			vp->vp_octets = talloc_steal(vp, p);
			vp->type = VT_DATA;

			VERIFY_VP(vp);
		}

		break;
		}
	} /* loop over the VP's we read in */
#endif

	if (rad_debug_lvl) {
		for (vp = fr_cursor_init(&cursor, &request->packet->vps);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			/*
			 *	Take this opportunity to verify all the VALUE_PAIRs are still valid.
			 */
			if (!talloc_get_type(vp, VALUE_PAIR)) {
				ERROR("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp));

				fr_log_talloc_report(vp);
				rad_assert(0);
			}

			vp_print(fr_log_fp, vp);
		}
		fflush(fr_log_fp);
	}

	/*
	 *	FIXME: set IPs, etc.
	 */
	request->packet->code = PW_CODE_ACCESS_REQUEST;

	request->packet->src_ipaddr.af = AF_INET;
	request->packet->src_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->src_port = 18120;

	request->packet->dst_ipaddr.af = AF_INET;
	request->packet->dst_ipaddr.ipaddr.ip4addr.s_addr = htonl(INADDR_LOOPBACK);
	request->packet->dst_port = 1812;

	/*
	 *	Build the reply template from the request.
	 */
	request->reply->sockfd = request->packet->sockfd;
	request->reply->dst_ipaddr = request->packet->src_ipaddr;
	request->reply->src_ipaddr = request->packet->dst_ipaddr;
	request->reply->dst_port = request->packet->src_port;
	request->reply->src_port = request->packet->dst_port;
	request->reply->id = request->packet->id;
	request->reply->code = 0; /* UNKNOWN code */
	memcpy(request->reply->vector, request->packet->vector,
	       sizeof(request->reply->vector));
	request->reply->vps = NULL;
	request->reply->data = NULL;
	request->reply->data_len = 0;

	/*
	 *	Debugging
	 */
	request->log.lvl = rad_debug_lvl;
	request->log.func = vradlog_request;

	request->username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
	request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);

	return request;
}
/** Check if a given user is already logged in.
 *
 * Process accounting data to determine if a user is already logged in. Sets request->simul_count
 * to the current session count for this user.
 *
 * Check twice. If on the first pass the user exceeds his maximum number of logins, do a second
 * pass and validate all logins by querying the terminal server.
 *
 * @param instance The module instance.
 * @param request  The checksimul request object.
 * @return Operation status (#rlm_rcode_t).
 */
static rlm_rcode_t mod_checksimul(void *instance, REQUEST *request) {
	rlm_couchbase_t *inst = instance;      /* our module instance */
	rlm_rcode_t rcode = RLM_MODULE_OK;     /* return code */
	rlm_couchbase_handle_t *handle = NULL; /* connection pool handle */
	char vpath[256];

	char buffer[MAX_KEY_SIZE];
	char const *vkey;                      /* view path and query key */
	char docid[MAX_KEY_SIZE];              /* document id returned from view */
	char error[512];                       /* view error return */
	int idx = 0;                           /* row array index counter */
	char element[MAX_KEY_SIZE];            /* mapped radius attribute to element name */
	lcb_error_t cb_error = LCB_SUCCESS;    /* couchbase error holder */
	json_object *json, *jval;              /* json object holders */
	json_object *jrows = NULL;             /* json object to hold view rows */
	VALUE_PAIR *vp;                        /* value pair */
	uint32_t client_ip_addr = 0;           /* current client ip address */
	char const *client_cs_id = NULL;       /* current client calling station id */
	char *user_name = NULL;                /* user name from accounting document */
	char *session_id = NULL;               /* session id from accounting document */
	char *cs_id = NULL;                    /* calling station id from accounting document */
	uint32_t nas_addr = 0;                 /* nas address from accounting document */
	uint32_t nas_port = 0;                 /* nas port from accounting document */
	uint32_t framed_ip_addr = 0;           /* framed ip address from accounting document */
	char framed_proto = 0;                 /* framed proto from accounting document */
	int session_time = 0;                  /* session time from accounting document */
	ssize_t slen;

	/* do nothing if this is not enabled */
	if (inst->check_simul != true) {
		RDEBUG3("mod_checksimul returning noop - not enabled");
		return RLM_MODULE_NOOP;
	}

	/* ensure valid username in request */
	if ((!request->username) || (request->username->vp_length == '\0')) {
		RDEBUG3("mod_checksimul - invalid username");
		return RLM_MODULE_INVALID;
	}

	slen = tmpl_expand(&vkey, buffer, sizeof(buffer), request, inst->simul_vkey, NULL, NULL);
	if (slen < 0) return RLM_MODULE_FAIL;
	if ((vkey == buffer) && is_truncated((size_t)slen, sizeof(buffer))) {
		REDEBUG("Key too long, expected < " STRINGIFY(sizeof(buffer)) " bytes, got %zi bytes", slen);
		return RLM_MODULE_FAIL;
	}

	/* get handle */
	handle = fr_connection_get(inst->pool);

	/* check handle */
	if (!handle) return RLM_MODULE_FAIL;

	/* set couchbase instance */
	lcb_t cb_inst = handle->handle;

	/* set cookie */
	cookie_t *cookie = handle->cookie;

	/* build view path */
	snprintf(vpath, sizeof(vpath), "%s?key=\"%s\"&stale=update_after",
		 inst->simul_view, vkey);

	/* query view for document */
	cb_error = couchbase_query_view(cb_inst, cookie, vpath, NULL);

	/* check error and object */
	if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
		/* log error */
		RERROR("failed to execute view request or parse return");
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto finish;
	}

	/* debugging */
	RDEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));

	/* check for error in json object */
	if (json_object_object_get_ex(cookie->jobj, "error", &json)) {
		/* build initial error buffer */
		strlcpy(error, json_object_get_string(json), sizeof(error));
		/* get error reason */
		if (json_object_object_get_ex(cookie->jobj, "reason", &json)) {
			/* append divider */
			strlcat(error, " - ", sizeof(error));
			/* append reason */
			strlcat(error, json_object_get_string(json), sizeof(error));
		}
		/* log error */
		RERROR("view request failed with error: %s", error);
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto finish;
	}

	/* check for document id in return */
	if (!json_object_object_get_ex(cookie->jobj, "rows", &json)) {
		/* log error */
		RERROR("failed to fetch rows from view payload");
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto finish;
	}

	/* get and hold rows */
	jrows = json_object_get(json);

	/* free cookie object */
	if (cookie->jobj) {
		json_object_put(cookie->jobj);
		cookie->jobj = NULL;
	}

	/* check for valid row value */
	if (!jrows || !fr_json_object_is_type(jrows, json_type_array)) {
		/* log error */
		RERROR("no valid rows returned from view: %s", vpath);
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto finish;
	}

	/* debugging */
	RDEBUG3("jrows == %s", json_object_to_json_string(jrows));

	/* set the count */
	request->simul_count = json_object_array_length(jrows);

	/* debugging */
	RDEBUG("found %d open sessions for %s", request->simul_count, request->username->vp_strvalue);

	/* check count */
	if (request->simul_count < request->simul_max) {
		rcode = RLM_MODULE_OK;
		goto finish;
	}

	/*
	 * Current session count exceeds configured maximum.
	 * Continue on to verify the sessions if configured otherwise stop here.
	 */
	if (inst->verify_simul != true) {
		rcode = RLM_MODULE_OK;
		goto finish;
	}

	/* debugging */
	RDEBUG("verifying session count");

	/* reset the count */
	request->simul_count = 0;

	/* get client ip address for MPP detection below */
	if ((vp = fr_pair_find_by_num(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) {
		client_ip_addr = vp->vp_ipaddr;
	}

	/* get calling station id for MPP detection below */
	if ((vp = fr_pair_find_by_num(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) {
		client_cs_id = vp->vp_strvalue;
	}

	/* loop across all row elements */
	for (idx = 0; idx < json_object_array_length(jrows); idx++) {
		/* clear docid */
		memset(docid, 0, sizeof(docid));

		/* fetch current index */
		json = json_object_array_get_idx(jrows, idx);

		/* get document id */
		if (json_object_object_get_ex(json, "id", &jval)) {
			/* copy and check length */
			if (strlcpy(docid, json_object_get_string(jval), sizeof(docid)) >= sizeof(docid)) {
				RERROR("document id from row longer than MAX_KEY_SIZE (%d)", MAX_KEY_SIZE);
				continue;
			}
		}

		/* check for valid doc id */
		if (docid[0] == 0) {
			RWARN("failed to fetch document id from row - skipping");
			continue;
		}

		/* fetch document */
		cb_error = couchbase_get_key(cb_inst, cookie, docid);

		/* check error and object */
		if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
			/* log error */
			RERROR("failed to execute get request or parse return");
			/* set return */
			rcode = RLM_MODULE_FAIL;
			/* return */
			goto finish;
		}

		/* debugging */
		RDEBUG3("cookie->jobj == %s", json_object_to_json_string(cookie->jobj));

		/* get element name for User-Name attribute */
		if (mod_attribute_to_element("User-Name", inst->map, &element) == 0) {
			/* get and check username element */
			if (!json_object_object_get_ex(cookie->jobj, element, &jval)){
				RDEBUG("cannot zap stale entry without username");
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}
			/* copy json string value to user_name */
			user_name = talloc_typed_strdup(request, json_object_get_string(jval));
		} else {
			RDEBUG("failed to find map entry for User-Name attribute");
			rcode = RLM_MODULE_FAIL;
			goto finish;
		}

		/* get element name for Acct-Session-Id attribute */
		if (mod_attribute_to_element("Acct-Session-Id", inst->map, &element) == 0) {
			/* get and check session id element */
			if (!json_object_object_get_ex(cookie->jobj, element, &jval)){
				RDEBUG("cannot zap stale entry without session id");
				rcode = RLM_MODULE_FAIL;
				goto finish;
			}
			/* copy json string value to session_id */
			session_id = talloc_typed_strdup(request, json_object_get_string(jval));
		} else {
			RDEBUG("failed to find map entry for Acct-Session-Id attribute");
			rcode = RLM_MODULE_FAIL;
			goto finish;
		}

		/* get element name for NAS-IP-Address attribute */
		if (mod_attribute_to_element("NAS-IP-Address", inst->map, &element) == 0) {
			/* attempt to get and nas address element */
			if (json_object_object_get_ex(cookie->jobj, element, &jval)){
				nas_addr = inet_addr(json_object_get_string(jval));
			}
		}

		/* get element name for NAS-Port attribute */
		if (mod_attribute_to_element("NAS-Port", inst->map, &element) == 0) {
			/* attempt to get nas port element */
			if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
				nas_port = (uint32_t) json_object_get_int(jval);
			}
		}

		/* check terminal server */
		int check = rad_check_ts(nas_addr, nas_port, user_name, session_id);

		/* take action based on check return */
		if (check == 0) {
			/* stale record - zap it if enabled */
			if (inst->delete_stale_sessions) {
				/* get element name for Framed-IP-Address attribute */
				if (mod_attribute_to_element("Framed-IP-Address", inst->map, &element) == 0) {
					/* attempt to get framed ip address element */
					if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
						framed_ip_addr = inet_addr(json_object_get_string(jval));
					}
				}

				/* get element name for Framed-Port attribute */
				if (mod_attribute_to_element("Framed-Port", inst->map, &element) == 0) {
					/* attempt to get framed port element */
					if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
						if (strcmp(json_object_get_string(jval), "PPP") == 0) {
							framed_proto = 'P';
						} else if (strcmp(json_object_get_string(jval), "SLIP") == 0) {
							framed_proto = 'S';
						}
					}
				}

				/* get element name for Acct-Session-Time attribute */
				if (mod_attribute_to_element("Acct-Session-Time", inst->map, &element) == 0) {
					/* attempt to get session time element */
					if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
						session_time = json_object_get_int(jval);
					}
				}

				/* zap session */
				session_zap(request, nas_addr, nas_port, user_name, session_id,
					    framed_ip_addr, framed_proto, session_time);
			}
		} else if (check == 1) {
			/* user is still logged in - increase count */
			++request->simul_count;

			/* get element name for Framed-IP-Address attribute */
			if (mod_attribute_to_element("Framed-IP-Address", inst->map, &element) == 0) {
				/* attempt to get framed ip address element */
				if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
					framed_ip_addr = inet_addr(json_object_get_string(jval));
				} else {
					/* ensure 0 if not found */
					framed_ip_addr = 0;
				}
			}

			/* get element name for Calling-Station-Id attribute */
			if (mod_attribute_to_element("Calling-Station-Id", inst->map, &element) == 0) {
				/* attempt to get framed ip address element */
				if (json_object_object_get_ex(cookie->jobj, element, &jval)) {
					/* copy json string value to cs_id */
					cs_id = talloc_typed_strdup(request, json_object_get_string(jval));
				} else {
					/* ensure null if not found */
					cs_id = NULL;
				}
			}

			/* Does it look like a MPP attempt? */
			if (client_ip_addr && framed_ip_addr && framed_ip_addr == client_ip_addr) {
				request->simul_mpp = 2;
			} else if (client_cs_id && cs_id && !strncmp(cs_id, client_cs_id, 16)) {
				request->simul_mpp = 2;
			}

		} else {
			/* check failed - return error */
			REDEBUG("failed to check the terminal server for user '%s'", user_name);
			rcode = RLM_MODULE_FAIL;
			goto finish;
		}

		/* free and reset document user name talloc */
		if (user_name) TALLOC_FREE(user_name);

		/* free and reset document calling station id talloc */
		if (cs_id) TALLOC_FREE(cs_id);

		/* free and reset document session id talloc */
		if (session_id) TALLOC_FREE(session_id);

		/* free and reset json object before fetching next row */
		if (cookie->jobj) {
			json_object_put(cookie->jobj);
			cookie->jobj = NULL;
		}
	}

	/* debugging */
	RDEBUG("Retained %d open sessions for %s after verification",
	       request->simul_count, request->username->vp_strvalue);

finish:
	if (user_name) talloc_free(user_name);
	if (cs_id) talloc_free(cs_id);
	if (session_id) talloc_free(session_id);

	/* free rows */
	if (jrows) json_object_put(jrows);

	/* free and reset json object */
	if (cookie->jobj) {
		json_object_put(cookie->jobj);
		cookie->jobj = NULL;
	}

	if (handle) fr_connection_release(inst->pool, handle);

	/*
	 * The Auth module apparently looks at request->simul_count,
	 * not the return value of this module when deciding to deny
	 * a call for too many sessions.
	 */
	return rcode;
}
Example #4
0
/*
 *	Find the named user in this modules database.  Create the set
 *	of attribute-value pairs to check and reply with for this user
 *	from the database. The authentication code only needs to check
 *	the password, the rest is done here.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request)
{
	VALUE_PAIR *password, *chap;
	uint8_t pass_str[MAX_STRING_LEN];

	if (!request->username) {
		REDEBUG("&request:User-Name attribute is required for authentication");
		return RLM_MODULE_INVALID;
	}

	chap = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
	if (!chap) {
		REDEBUG("You set '&control:Auth-Type = CHAP' for a request that "
			"does not contain a CHAP-Password attribute!");
		return RLM_MODULE_INVALID;
	}

	if (chap->vp_length == 0) {
		REDEBUG("&request:CHAP-Password is empty");
		return RLM_MODULE_INVALID;
	}

	if (chap->vp_length != CHAP_VALUE_LENGTH + 1) {
		REDEBUG("&request:CHAP-Password has invalid length");
		return RLM_MODULE_INVALID;
	}

	password = fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY);
	if (password == NULL) {
		if (fr_pair_find_by_num(request->config, PW_USER_PASSWORD, 0, TAG_ANY) != NULL){
			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
			REDEBUG("!!! Please update your configuration so that the \"known !!!");
			REDEBUG("!!! good\" cleartext password is in Cleartext-Password,  !!!");
			REDEBUG("!!! and NOT in User-Password.                            !!!");
			REDEBUG("!!!						          !!!");
			REDEBUG("!!! Authentication will fail because of this.	          !!!");
			REDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		}

		REDEBUG("&control:Cleartext-Password is required for authentication");
		return RLM_MODULE_FAIL;
	}

	rad_chap_encode(request->packet, pass_str, chap->vp_octets[0], password);

	if (RDEBUG_ENABLED3) {
		uint8_t const *p;
		size_t length;
		VALUE_PAIR *vp;
		char buffer[CHAP_VALUE_LENGTH * 2 + 1];

		RDEBUG3("Comparing with \"known good\" &control:Cleartext-Password value \"%s\"",
			password->vp_strvalue);

		vp = fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY);
		if (vp) {
			RDEBUG2("Using challenge from &request:CHAP-Challenge");
			p = vp->vp_octets;
			length = vp->vp_length;
		} else {
			RDEBUG2("Using challenge from authenticator field");
			p = request->packet->vector;
			length = sizeof(request->packet->vector);
		}

		fr_bin2hex(buffer, p, length);
		RINDENT();
		RDEBUG3("CHAP challenge : %s", buffer);

		fr_bin2hex(buffer, chap->vp_octets + 1, CHAP_VALUE_LENGTH);
		RDEBUG3("Client sent    : %s", buffer);

		fr_bin2hex(buffer, pass_str + 1, CHAP_VALUE_LENGTH);
		RDEBUG3("We calculated  : %s", buffer);
		REXDENT();
	} else {
		RDEBUG2("Comparing with \"known good\" Cleartext-Password");
	}

	if (rad_digest_cmp(pass_str + 1, chap->vp_octets + 1, CHAP_VALUE_LENGTH) != 0) {
		REDEBUG("Password comparison failed: password is incorrect");
		return RLM_MODULE_REJECT;
	}

	RDEBUG("CHAP user \"%s\" authenticated successfully", request->username->vp_strvalue);

	return RLM_MODULE_OK;
}
Example #5
0
/*
 *	Mangle username if needed, IN PLACE.
 */
static void rad_mangle(rlm_preprocess_t *inst, REQUEST *request)
{
	int		num_proxy_state;
	VALUE_PAIR	*namepair;
	VALUE_PAIR	*request_pairs;
	VALUE_PAIR	*tmp;
	vp_cursor_t	cursor;

	/*
	 *	Get the username from the request
	 *	If it isn't there, then we can't mangle the request.
	 */
	request_pairs = request->packet->vps;
	namepair = fr_pair_find_by_num(request_pairs, PW_USER_NAME, 0, TAG_ANY);
	if (!namepair || (namepair->vp_length == 0)) {
		return;
	}

	if (inst->with_ntdomain_hack) {
		char *ptr;
		char newname[MAX_STRING_LEN];

		/*
		 *	Windows NT machines often authenticate themselves as
		 *	NT_DOMAIN\username. Try to be smart about this.
		 *
		 *	FIXME: should we handle this as a REALM ?
		 */
		if ((ptr = strchr(namepair->vp_strvalue, '\\')) != NULL) {
			strlcpy(newname, ptr + 1, sizeof(newname));
			/* Same size */
			fr_pair_value_strcpy(namepair, newname);
		}
	}

	if (inst->with_specialix_jetstream_hack) {
		/*
		 *	Specialix Jetstream 8500 24 port access server.
		 *	If the user name is 10 characters or longer, a "/"
		 *	and the excess characters after the 10th are
		 *	appended to the user name.
		 *
		 *	Reported by Lucas Heise <*****@*****.**>
		 */
		if ((strlen(namepair->vp_strvalue) > 10) &&
		    (namepair->vp_strvalue[10] == '/')) {
			fr_pair_value_strcpy(namepair, namepair->vp_strvalue + 11);
		}
	}

	/*
	 *	Small check: if Framed-Protocol present but Service-Type
	 *	is missing, add Service-Type = Framed-User.
	 */
	if (fr_pair_find_by_num(request_pairs, PW_FRAMED_PROTOCOL, 0, TAG_ANY) != NULL &&
	    fr_pair_find_by_num(request_pairs, PW_SERVICE_TYPE, 0, TAG_ANY) == NULL) {
		tmp = radius_pair_create(request->packet, &request->packet->vps, PW_SERVICE_TYPE, 0);
		tmp->vp_integer = PW_FRAMED_USER;
	}

	num_proxy_state = 0;
	for (tmp = fr_cursor_init(&cursor, &request->packet->vps);
	     tmp;
	     tmp = fr_cursor_next(&cursor)) {
		if (tmp->da->vendor != 0) {
			continue;
		}

		if (tmp->da->attr != PW_PROXY_STATE) {
			continue;
		}

		num_proxy_state++;
	}

	if (num_proxy_state > 10) {
		RWDEBUG("There are more than 10 Proxy-State attributes in the request");
		RWDEBUG("You have likely configured an infinite proxy loop");
	}
}
Example #6
0
/*
 *	Preprocess a request before accounting
 */
static rlm_rcode_t CC_HINT(nonnull) mod_preaccounting(void *instance, REQUEST *request)
{
	int r;
	VALUE_PAIR *vp;
	rlm_preprocess_t *inst = instance;

	/*
	 *  Ensure that we have the SAME user name for both
	 *  authentication && accounting.
	 */
	rad_mangle(inst, request);

	if (inst->with_cisco_vsa_hack) {
		/*
		 *	We need to run this hack because the h323-conf-id
		 *	attribute should be used.
		 */
		cisco_vsa_hack(request);
	}

	if (inst->with_alvarion_vsa_hack) {
		/*
		 *	We need to run this hack because the Alvarion
		 *	people are crazy.
		 */
		alvarion_vsa_hack(request->packet->vps);
	}

	if (inst->with_cablelabs_vsa_hack) {
		/*
		 *	We need to run this hack because the Cablelabs
		 *	people are crazy.
		 */
		cablelabs_vsa_hack(&request->packet->vps);
	}

	/*
	 *  Ensure that we log the NAS IP Address in the packet.
	 */
	if (add_nas_attr(request) < 0) {
		return RLM_MODULE_FAIL;
	}

	hints_setup(inst->hints, request);

	/*
	 *	Add an event timestamp.  This means that the rest of
	 *	the server can use it, rather than various error-prone
	 *	manual calculations.
	 */
	vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
	if (!vp) {
		VALUE_PAIR *delay;

		vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
		vp->vp_date = request->packet->timestamp.tv_sec;

		delay = fr_pair_find_by_num(request->packet->vps, PW_ACCT_DELAY_TIME, 0, TAG_ANY);
		if (delay) {
			if ((delay->vp_integer >= vp->vp_date) || (delay->vp_integer == UINT32_MAX)) {
				RWARN("Ignoring invalid Acct-Delay-time of %u seconds", delay->vp_integer);
			} else {
				vp->vp_date -= delay->vp_integer;
			}
		}
	}

	if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
		char buf[1024];
		RIDEBUG("No huntgroup access: [%s] (%s)",
			request->username ? request->username->vp_strvalue : "<NO User-Name>",
			auth_name(buf, sizeof(buf), request, 1));
		return r;
	}

	return r;
}
Example #7
0
/*
 * calculate the MAC for the EAP message, given the key.
 * The "extra" will be appended to the EAP message and included in the
 * HMAC.
 *
 */
int eapsim_checkmac(TALLOC_CTX *ctx, VALUE_PAIR *rvps, uint8_t key[EAPSIM_AUTH_SIZE], uint8_t *extra, int extralen,
		    uint8_t calcmac[20])
{
	int ret;
	eap_packet_raw_t *e;
	uint8_t *buffer;
	int elen,len;
	VALUE_PAIR *mac;

	mac = fr_pair_find_by_num(rvps, PW_EAP_SIM_MAC, 0, TAG_ANY);

	if(!mac || mac->vp_length != 18) {
		/* can't check a packet with no AT_MAC attribute */
		return 0;
	}

	/* get original copy of EAP message, note that it was sanitized
	 * to have a valid length, which we depend upon.
	 */
	e = eap_vp2packet(ctx, rvps);
	if (!e) return 0;

	/* make copy big enough for everything */
	elen = (e->length[0] * 256) + e->length[1];
	len = elen + extralen;

	buffer = talloc_array(ctx, uint8_t, len);
	if (!buffer) {
		talloc_free(e);
		return 0;
	}

	memcpy(buffer, e, elen);
	memcpy(buffer + elen, extra, extralen);

	/*
	 * now look for the AT_MAC attribute in the copy of the buffer
	 * and make sure that the checksum is zero.
	 *
	 */
	{
		uint8_t *attr;

		/* first attribute is 8 bytes into the EAP packet.
		 * 4 bytes for EAP, 1 for type, 1 for subtype, 2 reserved.
		 */
		attr = buffer+8;
		while(attr < (buffer+elen)) {
			if (attr[0] == (PW_EAP_SIM_MAC - PW_EAP_SIM_BASE)) {
				/* zero the data portion, after making sure
				 * the size is >=5. Maybe future versions.
				 * will use more bytes, so be liberal.
				 */
				if(attr[1] < 5) {
					ret = 0;
					goto done;
				}
				memset(&attr[4], 0, (attr[1]-1)*4);
			}
			/* advance the pointer */
			attr += attr[1]*4;
		}
	}

	/* now, HMAC-SHA1 it with the key. */
	fr_hmac_sha1(calcmac, buffer, len, key, 16);

	ret = memcmp(&mac->vp_strvalue[2], calcmac, 16) == 0 ? 1 : 0;
 done:
	talloc_free(e);
	talloc_free(buffer);
	return(ret);
}
Example #8
0
static int dhcprelay_process_client_request(REQUEST *request)
{
	int		rcode;
	uint8_t		maxhops = 16;
	VALUE_PAIR	*vp, *giaddr;
	dhcp_socket_t	*sock;
	RADIUS_PACKET	*packet;

	rad_assert(request->packet->data[0] == 1);

	/*
	 *	Do the forward by ourselves, do not rely on dhcp_socket_send()
	 */
	request->reply->code = 0;

	/*
	 * It's invalid to have giaddr=0 AND a relay option
	 */
	giaddr = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */
	if (giaddr && (giaddr->vp_ipv4addr == htonl(INADDR_ANY)) &&
			fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 82, TAG_ANY)) { /* DHCP-Relay-Agent-Information */
		RDEBUG2("Received packet with giaddr = 0 and containing relay option: Discarding packet");
		return 1;
	}

	/*
	 * RFC 1542 (BOOTP), page 15
	 *
	 * Drop requests if hop-count > 16 or admin specified another value
	 */
	if ((vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 271, TAG_ANY))) { /* DHCP-Relay-Max-Hop-Count */
	    maxhops = vp->vp_uint32;
	}
	vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 259, TAG_ANY); /* DHCP-Hop-Count */
	rad_assert(vp != NULL);
	if (vp->vp_uint8 > maxhops) {
		RDEBUG2("Number of hops is greater than %d: not relaying", maxhops);
		return 1;
	} else {
	    /* Increment hop count */
	    vp->vp_uint8++;
	}

	sock = request->listener->data;

	/*
	 *	Don't muck with the original request packet.  That's
	 *	bad form.  Plus, dhcp_encode() does nothing if
	 *	packet->data is already set.
	 */
	packet = fr_radius_alloc(request, false);
	rcode = -1;

	/*
	 *	Forward the request to the next server using the
	 *	incoming request as a template.
	 */
	packet->code = request->packet->code;
	packet->sockfd = request->packet->sockfd;

	/*
	 *	Forward the request to the next server using the
	 *	incoming request as a template.
	 */
	/* set SRC ipaddr/port to the listener ipaddr/port */
	packet->src_ipaddr.af = AF_INET;
	packet->src_ipaddr.addr.v4.s_addr = sock->lsock.my_ipaddr.addr.v4.s_addr;
	packet->src_port = sock->lsock.my_port;

	vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY); /* DHCP-Relay-To-IP-Address */
	rad_assert(vp != NULL);

	/* set DEST ipaddr/port to the next server ipaddr/port */
	packet->dst_ipaddr.af = AF_INET;
	packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
	packet->dst_port = sock->lsock.my_port;

	packet->vps = request->packet->vps; /* hackity hack */

	/*
	 *	Relaying is not proxying, we just forward it on and forget
	 *	about it, not sending a response to the DHCP client.
	 */
	dhcp_packet_debug(request, packet, false);

	if (fr_dhcpv4_packet_encode(packet) < 0) {
		RPERROR("Failed encoding DHCP packet");
		goto error;
	}

	rcode = fr_dhcpv4_udp_packet_send(packet);

error:
	packet->vps = NULL;
	talloc_free(packet);
	return rcode;
}
Example #9
0
/*
 * Make sure user/pass are clean
 * and then log them
 */
static int rad_authlog(char const *msg, REQUEST *request, int goodpass)
{
	int logit;
	char const *extra_msg = NULL;
	char clean_password[1024];
	char clean_username[1024];
	char buf[1024];
	char extra[1024];
	char *p;
	VALUE_PAIR *username = NULL;

	if (!request->root->log_auth) {
		return 0;
	}

	/*
	 * Get the correct username based on the configured value
	 */
	if (!log_stripped_names) {
		username = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
	} else {
		username = request->username;
	}

	/*
	 *	Clean up the username
	 */
	if (username == NULL) {
		strcpy(clean_username, "<no User-Name attribute>");
	} else {
		fr_prints(clean_username, sizeof(clean_username), username->vp_strvalue, username->vp_length, '\0');
	}

	/*
	 *	Clean up the password
	 */
	if (request->root->log_auth_badpass || request->root->log_auth_goodpass) {
		if (!request->password) {
			VALUE_PAIR *auth_type;

			auth_type = fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY);
			if (auth_type) {
				snprintf(clean_password, sizeof(clean_password),
					 "<via Auth-Type = %s>",
					 dict_valnamebyattr(PW_AUTH_TYPE, 0,
							    auth_type->vp_integer));
			} else {
				strcpy(clean_password, "<no User-Password attribute>");
			}
		} else if (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY)) {
			strcpy(clean_password, "<CHAP-Password>");
		} else {
			fr_prints(clean_password, sizeof(clean_password),
				  request->password->vp_strvalue, request->password->vp_length, '\0');
		}
	}

	if (goodpass) {
		logit = request->root->log_auth_goodpass;
		extra_msg = request->root->auth_goodpass_msg;
	} else {
		logit = request->root->log_auth_badpass;
		extra_msg = request->root->auth_badpass_msg;
	}

	if (extra_msg) {
		extra[0] = ' ';
		p = extra + 1;
		if (radius_xlat(p, sizeof(extra) - 1, request, extra_msg, NULL, NULL) < 0) {
			return -1;
		}
	} else {
		*extra = '\0';
	}

	RAUTH("%s: [%s%s%s] (%s)%s",
		       msg,
		       clean_username,
		       logit ? "/" : "",
		       logit ? clean_password : "",
		       auth_name(buf, sizeof(buf), request, 1),
		       extra);

	return 0;
}
Example #10
0
/** Compare two pair lists except for the password information.
 *
 * For every element in "check" at least one matching copy must be present
 * in "reply".
 *
 * @param[in] request		Current request.
 * @param[in] request_list	request valuepairs.
 * @param[in] check		Check/control valuepairs.
 * @param[in,out] reply_list	Reply value pairs.
 * @return 0 on match.
 */
int paircmp(REQUEST *request,
	    VALUE_PAIR *request_list,
	    VALUE_PAIR *check,
	    VALUE_PAIR **reply_list)
{
	fr_cursor_t		cursor;
	VALUE_PAIR		*check_item;
	VALUE_PAIR		*auth_item;
	fr_dict_attr_t const	*from;

	int			result = 0;
	int			compare;
	bool			first_only;

	for (check_item = fr_cursor_init(&cursor, &check);
	     check_item;
	     check_item = fr_cursor_next(&cursor)) {
		/*
		 *	If the user is setting a configuration value,
		 *	then don't bother comparing it to any attributes
		 *	sent to us by the user.  It ALWAYS matches.
		 */
		if ((check_item->op == T_OP_SET) ||
		    (check_item->op == T_OP_ADD)) {
			continue;
		}

		/*
		 *	Attributes we skip during comparison.
		 *	These are "server" check items.
		 */
		if ((check_item->da == attr_crypt_password) ||
		    (check_item->da == attr_auth_type) ||
		    (check_item->da == attr_autz_type) ||
		    (check_item->da == attr_acct_type) ||
		    (check_item->da == attr_session_type) ||
		    (check_item->da == attr_strip_user_name)) {
			continue;
		}

		/*
		 *	IF the password attribute exists, THEN
		 *	we can do comparisons against it.  If not,
		 *	then the request did NOT contain a
		 *	User-Password attribute, so we CANNOT do
		 *	comparisons against it.
		 *
		 *	This hack makes CHAP-Password work..
		 */
		if (check_item->da == attr_user_password) {
			if (check_item->op == T_OP_CMP_EQ) {
				WARN("Found User-Password == \"...\"");
				WARN("Are you sure you don't mean Cleartext-Password?");
				WARN("See \"man rlm_pap\" for more information");
			}
			if (fr_pair_find_by_num(request_list, 0, FR_USER_PASSWORD, TAG_ANY) == NULL) continue;
		}

		/*
		 *	See if this item is present in the request.
		 */
		first_only = other_attr(check_item->da, &from);

		auth_item = request_list;

	try_again:
		if (!first_only) {
			while (auth_item != NULL) {
				if ((auth_item->da == from) || (!from)) break;

				auth_item = auth_item->next;
			}
		}

		/*
		 *	Not found, it's not a match.
		 */
		if (auth_item == NULL) {
			/*
			 *	Didn't find it.  If we were *trying*
			 *	to not find it, then we succeeded.
			 */
			if (check_item->op == T_OP_CMP_FALSE) {
				continue;
			} else {
				return -1;
			}
		}

		/*
		 *	Else we found it, but we were trying to not
		 *	find it, so we failed.
		 */
		if (check_item->op == T_OP_CMP_FALSE) return -1;

		/*
		 *	We've got to xlat the string before doing
		 *	the comparison.
		 */
		xlat_eval_pair(request, check_item);

		/*
		 *	OK it is present now compare them.
		 */
		compare = paircmp_func(request, auth_item, check_item, check, reply_list);
		switch (check_item->op) {
		case T_OP_EQ:
		default:
			RWDEBUG("Invalid operator '%s' for item %s: reverting to '=='",
				fr_int2str(fr_tokens_table, check_item->op, "<INVALID>"), check_item->da->name);
			/* FALL-THROUGH */
		case T_OP_CMP_TRUE:
		case T_OP_CMP_FALSE:
		case T_OP_CMP_EQ:
			if (compare != 0) result = -1;
			break;

		case T_OP_NE:
			if (compare == 0) result = -1;
			break;

		case T_OP_LT:
			if (compare >= 0) result = -1;
			break;

		case T_OP_GT:
			if (compare <= 0) result = -1;
			break;

		case T_OP_LE:
			if (compare > 0) result = -1;
			break;

		case T_OP_GE:
			if (compare < 0) result = -1;
			break;

#ifdef HAVE_REGEX
		case T_OP_REG_EQ:
		case T_OP_REG_NE:
			if (compare != 0) result = -1;
			break;
#endif
		} /* switch over the operator of the check item */

		/*
		 *	This attribute didn't match, but maybe there's
		 *	another of the same attribute, which DOES match.
		 */
		if ((result != 0) && (!first_only)) {
			auth_item = auth_item->next;
			result = 0;
			goto try_again;
		}

	} /* for every entry in the check item list */

	return result;
}
Example #11
0
/*
 *	Process and reply to an authentication request
 *
 *	The return value of this function isn't actually used right now, so
 *	it's not entirely clear if it is returning the right things. --Pac.
 */
int rad_authenticate(REQUEST *request)
{
#ifdef WITH_SESSION_MGMT
	VALUE_PAIR	*check_item;
#endif
	VALUE_PAIR	*module_msg;
	VALUE_PAIR	*tmp = NULL;
	int		result;
	char		autz_retry = 0;
	int		autz_type = 0;

#ifdef WITH_PROXY
	/*
	 *	If this request got proxied to another server, we need
	 *	to check whether it authenticated the request or not.
	 *
	 *	request->proxy gets set only AFTER authorization, so
	 *	it's safe to check it here.  If it exists, it means
	 *	we're doing a second pass through rad_authenticate().
	 */
	if (request->proxy) {
		int code = 0;

		if (request->proxy_reply) code = request->proxy_reply->code;

		switch (code) {
		/*
		 *	Reply of ACCEPT means accept, thus set Auth-Type
		 *	accordingly.
		 */
		case PW_CODE_ACCESS_ACCEPT:
			tmp = radius_pair_create(request,
						&request->config,
						PW_AUTH_TYPE, 0);
			if (tmp) tmp->vp_integer = PW_AUTH_TYPE_ACCEPT;
			goto authenticate;

		/*
		 *	Challenges are punted back to the NAS without any
		 *	further processing.
		 */
		case PW_CODE_ACCESS_CHALLENGE:
			request->reply->code = PW_CODE_ACCESS_CHALLENGE;
			fr_state_put_vps(request, request->packet, request->reply);
			return RLM_MODULE_OK;

		/*
		 *	ALL other replies mean reject. (this is fail-safe)
		 *
		 *	Do NOT do any authorization or authentication. They
		 *	are being rejected, so we minimize the amount of work
		 *	done by the server, by rejecting them here.
		 */
		case PW_CODE_ACCESS_REJECT:
			rad_authlog("Login incorrect (Home Server says so)",
				    request, 0);
			request->reply->code = PW_CODE_ACCESS_REJECT;
			fr_state_discard(request, request->packet);
			return RLM_MODULE_REJECT;

		default:
			rad_authlog("Login incorrect (Home Server failed to respond)",
				    request, 0);
			fr_state_discard(request, request->packet);
			return RLM_MODULE_REJECT;
		}
	}
#endif
	/*
	 *	Look for, and cache, passwords.
	 */
	if (!request->password) {
		request->password = fr_pair_find_by_num(request->packet->vps, PW_USER_PASSWORD, 0, TAG_ANY);
	}
	if (!request->password) {
		request->password = fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY);
	}

	/*
	 *	Grab the VPS associated with the State attribute.
	 */
	fr_state_get_vps(request, request->packet);

	/*
	 *	Get the user's authorization information from the database
	 */
autz_redo:
	result = process_authorize(autz_type, request);
	switch (result) {
	case RLM_MODULE_NOOP:
	case RLM_MODULE_NOTFOUND:
	case RLM_MODULE_OK:
	case RLM_MODULE_UPDATED:
		break;
	case RLM_MODULE_HANDLED:
		return result;
	case RLM_MODULE_FAIL:
	case RLM_MODULE_INVALID:
	case RLM_MODULE_REJECT:
	case RLM_MODULE_USERLOCK:
	default:
		if ((module_msg = fr_pair_find_by_num(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL) {
			char msg[MAX_STRING_LEN + 16];
			snprintf(msg, sizeof(msg), "Invalid user (%s)",
				 module_msg->vp_strvalue);
			rad_authlog(msg,request,0);
		} else {
			rad_authlog("Invalid user", request, 0);
		}
		request->reply->code = PW_CODE_ACCESS_REJECT;
		return result;
	}
	if (!autz_retry) {
		tmp = fr_pair_find_by_num(request->config, PW_AUTZ_TYPE, 0, TAG_ANY);
		if (tmp) {
			autz_type = tmp->vp_integer;
			RDEBUG2("Using Autz-Type %s",
				dict_valnamebyattr(PW_AUTZ_TYPE, 0, autz_type));
			autz_retry = 1;
			goto autz_redo;
		}
	}

	/*
	 *	If we haven't already proxied the packet, then check
	 *	to see if we should.  Maybe one of the authorize
	 *	modules has decided that a proxy should be used. If
	 *	so, get out of here and send the packet.
	 */
	if (
#ifdef WITH_PROXY
	    (request->proxy == NULL) &&
#endif
	    ((tmp = fr_pair_find_by_num(request->config, PW_PROXY_TO_REALM, 0, TAG_ANY)) != NULL)) {
		REALM *realm;

		realm = realm_find2(tmp->vp_strvalue);

		/*
		 *	Don't authenticate, as the request is going to
		 *	be proxied.
		 */
		if (realm && realm->auth_pool) {
			return RLM_MODULE_OK;
		}

		/*
		 *	Catch users who set Proxy-To-Realm to a LOCAL
		 *	realm (sigh).  But don't complain if it is
		 *	*the* LOCAL realm.
		 */
		if (realm &&(strcmp(realm->name, "LOCAL") != 0)) {
			RWDEBUG2("You set Proxy-To-Realm = %s, but it is a LOCAL realm!  Cancelling proxy request.", realm->name);
		}

		if (!realm) {
			RWDEBUG2("You set Proxy-To-Realm = %s, but the realm does not exist!  Cancelling invalid proxy request.", tmp->vp_strvalue);
		}
	}

#ifdef WITH_PROXY
authenticate:
#endif

	/*
	 *	Validate the user
	 */
	do {
		result = rad_check_password(request);
		if (result > 0) {
			return RLM_MODULE_HANDLED;
		}

	} while(0);

	/*
	 *	Failed to validate the user.
	 *
	 *	We PRESUME that the code which failed will clean up
	 *	request->reply->vps, to be ONLY the reply items it
	 *	wants to send back.
	 */
	if (result < 0) {
		RDEBUG2("Failed to authenticate the user");
		request->reply->code = PW_CODE_ACCESS_REJECT;

		if ((module_msg = fr_pair_find_by_num(request->packet->vps, PW_MODULE_FAILURE_MESSAGE, 0, TAG_ANY)) != NULL){
			char msg[MAX_STRING_LEN+19];

			snprintf(msg, sizeof(msg), "Login incorrect (%s)",
				 module_msg->vp_strvalue);
			rad_authlog(msg, request, 0);
		} else {
			rad_authlog("Login incorrect", request, 0);
		}

		if (request->password) {
			VERIFY_VP(request->password);
			/* double check: maybe the secret is wrong? */
			if ((rad_debug_lvl > 1) && (request->password->da->attr == PW_USER_PASSWORD)) {
				uint8_t const *p;

				p = (uint8_t const *) request->password->vp_strvalue;
				while (*p) {
					int size;

					size = fr_utf8_char(p, -1);
					if (!size) {
						RWDEBUG("Unprintable characters in the password.  Double-check the "
							"shared secret on the server and the NAS!");
						break;
					}
					p += size;
				}
			}
		}
	}

#ifdef WITH_SESSION_MGMT
	if (result >= 0 &&
	    (check_item = fr_pair_find_by_num(request->config, PW_SIMULTANEOUS_USE, 0, TAG_ANY)) != NULL) {
		int r, session_type = 0;
		char		logstr[1024];
		char		umsg[MAX_STRING_LEN + 1];

		tmp = fr_pair_find_by_num(request->config, PW_SESSION_TYPE, 0, TAG_ANY);
		if (tmp) {
			session_type = tmp->vp_integer;
			RDEBUG2("Using Session-Type %s",
				dict_valnamebyattr(PW_SESSION_TYPE, 0, session_type));
		}

		/*
		 *	User authenticated O.K. Now we have to check
		 *	for the Simultaneous-Use parameter.
		 */
		if (request->username &&
		    (r = process_checksimul(session_type, request, check_item->vp_integer)) != 0) {
			char mpp_ok = 0;

			if (r == 2){
				/* Multilink attempt. Check if port-limit > simultaneous-use */
				VALUE_PAIR *port_limit;

				if ((port_limit = fr_pair_find_by_num(request->reply->vps, PW_PORT_LIMIT, 0, TAG_ANY)) != NULL &&
					port_limit->vp_integer > check_item->vp_integer){
					RDEBUG2("MPP is OK");
					mpp_ok = 1;
				}
			}
			if (!mpp_ok){
				if (check_item->vp_integer > 1) {
					snprintf(umsg, sizeof(umsg), "%s (%u)", main_config.denied_msg,
						 check_item->vp_integer);
				} else {
					strlcpy(umsg, main_config.denied_msg, sizeof(umsg));
				}

				request->reply->code = PW_CODE_ACCESS_REJECT;

				/*
				 *	They're trying to log in too many times.
				 *	Remove ALL reply attributes.
				 */
				fr_pair_list_free(&request->reply->vps);
				pair_make_reply("Reply-Message", umsg, T_OP_SET);

				snprintf(logstr, sizeof(logstr), "Multiple logins (max %d) %s",
					check_item->vp_integer,
					r == 2 ? "[MPP attempt]" : "");
				rad_authlog(logstr, request, 1);

				result = -1;
			}
		}
	}
#endif

	/*
	 *	Result should be >= 0 here - if not, it means the user
	 *	is rejected, so we just process post-auth and return.
	 */
	if (result < 0) {
		return RLM_MODULE_REJECT;
	}

	/*
	 *	Set the reply to Access-Accept, if it hasn't already
	 *	been set to something.  (i.e. Access-Challenge)
	 */
	if (request->reply->code == 0)
	  request->reply->code = PW_CODE_ACCESS_ACCEPT;

	if ((module_msg = fr_pair_find_by_num(request->packet->vps, PW_MODULE_SUCCESS_MESSAGE, 0, TAG_ANY)) != NULL){
		char msg[MAX_STRING_LEN+12];

		snprintf(msg, sizeof(msg), "Login OK (%s)",
			 module_msg->vp_strvalue);
		rad_authlog(msg, request, 1);
	} else {
		rad_authlog("Login OK", request, 1);
	}

	return result;
}
Example #12
0
/*
 *	Check password.
 *
 *	Returns:	0  OK
 *			-1 Password fail
 *			-2 Rejected (Auth-Type = Reject, send Port-Message back)
 *			1  End check & return, don't reply
 *
 *	NOTE: NOT the same as the RLM_ values !
 */
static int CC_HINT(nonnull) rad_check_password(REQUEST *request)
{
	vp_cursor_t cursor;
	VALUE_PAIR *auth_type_pair;
	int auth_type = -1;
	int result;
	int auth_type_count = 0;

	/*
	 *	Look for matching check items. We skip the whole lot
	 *	if the authentication type is PW_AUTH_TYPE_ACCEPT or
	 *	PW_AUTH_TYPE_REJECT.
	 */
	fr_cursor_init(&cursor, &request->config);
	while ((auth_type_pair = fr_cursor_next_by_num(&cursor, PW_AUTH_TYPE, 0, TAG_ANY))) {
		auth_type = auth_type_pair->vp_integer;
		auth_type_count++;

		RDEBUG2("Found Auth-Type = %s", dict_valnamebyattr(PW_AUTH_TYPE, 0, auth_type));
		if (auth_type == PW_AUTH_TYPE_REJECT) {
			RDEBUG2("Auth-Type = Reject, rejecting user");

			return -2;
		}
	}

	/*
	 *	Warn if more than one Auth-Type was found, because only the last
	 *	one found will actually be used.
	 */
	if ((auth_type_count > 1) && (rad_debug_lvl)) {
		RERROR("Warning:  Found %d auth-types on request for user '%s'",
			auth_type_count, request->username->vp_strvalue);
	}

	/*
	 *	This means we have a proxy reply or an accept and it wasn't
	 *	rejected in the above loop. So that means it is accepted and we
	 *	do no further authentication.
	 */
	if ((auth_type == PW_AUTH_TYPE_ACCEPT)
#ifdef WITH_PROXY
	    || (request->proxy)
#endif
	    ) {
		RDEBUG2("Auth-Type = Accept, accepting the user");
		return 0;
	}

	/*
	 *	Check that Auth-Type has been set, and reject if not.
	 *
	 *	Do quick checks to see if Cleartext-Password or Crypt-Password have
	 *	been set, and complain if so.
	 */
	if (auth_type < 0) {
		if (fr_pair_find_by_num(request->config, PW_CRYPT_PASSWORD, 0, TAG_ANY) != NULL) {
			RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Crypt'");
			RWDEBUG2("Use the PAP module instead");
		}
		else if (fr_pair_find_by_num(request->config, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY) != NULL) {
			RWDEBUG2("Please update your configuration, and remove 'Auth-Type = Local'");
			RWDEBUG2("Use the PAP or CHAP modules instead");
		}

		/*
		 *	The admin hasn't told us how to
		 *	authenticate the user, so we reject them!
		 *
		 *	This is fail-safe.
		 */

		REDEBUG2("No Auth-Type found: rejecting the user via Post-Auth-Type = Reject");
		return -2;
	}

	/*
	 *	See if there is a module that handles
	 *	this Auth-Type, and turn the RLM_ return
	 *	status into the values as defined at
	 *	the top of this function.
	 */
	result = process_authenticate(auth_type, request);
	switch (result) {
	/*
	 *	An authentication module FAIL
	 *	return code, or any return code that
	 *	is not expected from authentication,
	 *	is the same as an explicit REJECT!
	 */
	case RLM_MODULE_FAIL:
	case RLM_MODULE_INVALID:
	case RLM_MODULE_NOOP:
	case RLM_MODULE_NOTFOUND:
	case RLM_MODULE_REJECT:
	case RLM_MODULE_UPDATED:
	case RLM_MODULE_USERLOCK:
	default:
		result = -1;
		break;

	case RLM_MODULE_OK:
		result = 0;
		break;

	case RLM_MODULE_HANDLED:
		result = 1;
		break;
	}

	return result;
}
Example #13
0
/*
 * Test for passcode validity by asking otpd.
 *
 * If challenge is supplied, it is used to generate the card response
 * against which the passcode will be compared.  If challenge is not
 * supplied, or if the comparison fails, synchronous responses are
 * generated and tested.  NOTE: for async authentications, sync mode
 * responses are still considered valid!  (Assuming module configuration
 * allows sync mode.)
 *
 * Returns one of the RLM_MODULE_* codes.  passcode is filled in.
 * NB: The returned passcode will contain the PIN!  DO NOT LOG!
 */
int otp_pw_valid(REQUEST *request, int pwe, char const *challenge,
		 rlm_otp_t const *opt,
		 char passcode[OTP_MAX_PASSCODE_LEN + 1])
{
	otp_request_t	otp_request;
	otp_reply_t	otp_reply;
	VALUE_PAIR	*cvp, *rvp;
	char const	*username = request->username->vp_strvalue;
	int		rc;

	otp_request.version = 2;

	if (strlcpy(otp_request.username, username,
			sizeof(otp_request.username)) >=
		sizeof(otp_request.username)) {
		AUTH("rlm_otp: username [%s] too long", username);
		return RLM_MODULE_REJECT;
	}
	if (strlcpy(otp_request.challenge, challenge,
			sizeof(otp_request.challenge)) >=
		sizeof(otp_request.challenge)) {
		AUTH("rlm_otp: challenge for [%s] too long", username);
		return RLM_MODULE_REJECT;
	}

	otp_request.pwe.pwe = pwe;

	/*
	 *	otp_pwe_present() (done by caller) guarantees that both of
	 *	these exist
	 */
	cvp = fr_pair_find_by_num(request->packet->vps, pwattr[pwe - 1]->attr,
		       pwattr[pwe - 1]->vendor, TAG_ANY);

	rvp = fr_pair_find_by_num(request->packet->vps, pwattr[pwe]->attr,
		       pwattr[pwe]->vendor, TAG_ANY);

	/* this is just to quiet Coverity */
	if (!rvp || !cvp) {
		return RLM_MODULE_REJECT;
	}

	/*
	 *	Validate available vps based on pwe type.
	 *	Unfortunately (?) otpd must do this also.
	 */
	switch (otp_request.pwe.pwe) {
	case PWE_NONE:
		return RLM_MODULE_NOOP;

	case PWE_PAP:
		if (strlcpy(otp_request.pwe.u.pap.passcode, rvp->vp_strvalue,
				sizeof(otp_request.pwe.u.pap.passcode)) >=
			sizeof(otp_request.pwe.u.pap.passcode)) {
			AUTH("rlm_otp: passcode for [%s] too long",
			       username);

			return RLM_MODULE_REJECT;
		}
		break;

	case PWE_CHAP:
		if (cvp->vp_length > 16) {
			AUTH("rlm_otp: CHAP challenge for [%s] "
			       "too long", username);

			return RLM_MODULE_INVALID;
		}

		if (rvp->vp_length != 17) {
			AUTH("rlm_otp: CHAP response for [%s] "
			      "wrong size", username);

			return RLM_MODULE_INVALID;
		}

		(void) memcpy(otp_request.pwe.u.chap.challenge, cvp->vp_octets,
			      cvp->vp_length);

		otp_request.pwe.u.chap.clen = cvp->vp_length;
		(void) memcpy(otp_request.pwe.u.chap.response, rvp->vp_octets,
			      rvp->vp_length);

		otp_request.pwe.u.chap.rlen = rvp->vp_length;
		break;

	case PWE_MSCHAP:
		if (cvp->vp_length != 8) {
			AUTH("rlm_otp: MS-CHAP challenge for "
			       "[%s] wrong size", username);

			return RLM_MODULE_INVALID;
		}

		if (rvp->vp_length != 50) {
			AUTH("rlm_otp: MS-CHAP response for [%s] "
			       "wrong size", username);

			return RLM_MODULE_INVALID;
		}
		(void) memcpy(otp_request.pwe.u.chap.challenge,
			      cvp->vp_octets, cvp->vp_length);

		otp_request.pwe.u.chap.clen = cvp->vp_length;

		(void) memcpy(otp_request.pwe.u.chap.response,
			      rvp->vp_octets, rvp->vp_length);

		otp_request.pwe.u.chap.rlen = rvp->vp_length;
		break;

	case PWE_MSCHAP2:
		if (cvp->vp_length != 16) {
			AUTH("rlm_otp: MS-CHAP2 challenge for "
				      "[%s] wrong size", username);

			return RLM_MODULE_INVALID;
		}

		if (rvp->vp_length != 50) {
			AUTH("rlm_otp: MS-CHAP2 response for [%s] "
			       "wrong size", username);

			return RLM_MODULE_INVALID;
		}

		(void) memcpy(otp_request.pwe.u.chap.challenge, cvp->vp_octets,
			      cvp->vp_length);

		otp_request.pwe.u.chap.clen = cvp->vp_length;

		(void) memcpy(otp_request.pwe.u.chap.response, rvp->vp_octets,
			      rvp->vp_length);
		otp_request.pwe.u.chap.rlen = rvp->vp_length;
		break;
	} /* switch (otp_request.pwe.pwe) */

	/*
	 *	last byte must also be a terminator so otpd can verify
	 *	length easily.
	 */
	otp_request.username[OTP_MAX_USERNAME_LEN] = '\0';
	otp_request.challenge[OTP_MAX_CHALLENGE_LEN] = '\0';

	if (otp_request.pwe.pwe == PWE_PAP) {
		otp_request.pwe.u.pap.passcode[OTP_MAX_PASSCODE_LEN] = '\0';
	}

	otp_request.allow_sync = opt->allow_sync;
	otp_request.allow_async = opt->allow_async;
	otp_request.challenge_delay = opt->challenge_delay;
	otp_request.resync = 1;


	rc = otp_verify(opt, &otp_request, &otp_reply);
	if (rc == OTP_RC_OK) {
		(void) strcpy(passcode, otp_reply.passcode);
	}

	return otprc2rlmrc(rc);
}
Example #14
0
/*
 *	We've seen a reply from a server.
 *	i.e. we're a relay.
 */
static int dhcprelay_process_server_reply(REQUEST *request)
{
	int rcode;
	VALUE_PAIR *vp, *giaddr;
	dhcp_socket_t *sock;
	RADIUS_PACKET *packet;

	rad_assert(request->packet->data[0] == 2);

	/*
	 * Do the forward by ourselves, do not rely on dhcp_socket_send()
	 */
	request->reply->code = 0;

	sock = request->listener->data;

	/*
	 * Check that packet is for us.
	 */
	giaddr = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */

	/* --with-udpfromto is needed just for the following test */
	if (!giaddr || giaddr->vp_ipv4addr != request->packet->dst_ipaddr.addr.v4.s_addr) {
		RDEBUG2("Packet received from server was not for us (was for 0x%x). Discarding packet",
			ntohl(request->packet->dst_ipaddr.addr.v4.s_addr));
		return 1;
	}

	/*
	 *	Don't muck with the original request packet.  That's
	 *	bad form.  Plus, dhcp_encode() does nothing if
	 *	packet->data is already set.
	 */
	packet = fr_radius_alloc(request, false);
	rcode = -1;

	/*
	 *	Forward the request to the next server using the
	 *	incoming request as a template.
	 */
	packet->code = request->packet->code;
	packet->sockfd = request->packet->sockfd;

	/* set SRC ipaddr/port to the listener ipaddr/port */
	packet->src_ipaddr.af = AF_INET;
	packet->src_port = sock->lsock.my_port;

	/* set DEST ipaddr/port to clientip/68 or broadcast in specific cases */
	packet->dst_ipaddr.af = AF_INET;

	/*
	 *	We're a relay, and send the reply to giaddr.
	 */
	packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);
	packet->dst_port = request->packet->dst_port;		/* server port */

	vp = fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY); /* DHCP-Relay-To-IP-Address */
	if (vp) {
		RDEBUG("DHCP: response will be relayed to previous gateway");
		packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
		giaddr->vp_ipv4addr = vp->vp_ipv4addr;

	} else if ((packet->code == FR_DHCP_NAK) ||
	    !sock->src_interface ||
	    ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) /* DHCP-Flags */ &&
	     (vp->vp_uint32 & 0x8000) &&
	     ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
	      (vp->vp_ipv4addr == htonl(INADDR_ANY))))) {
		/*
		 * RFC 2131, page 23
		 *
		 * Broadcast on
		 * - DHCPNAK
		 * or
		 * - Broadcast flag is set up and ciaddr == NULL
		 */
		RDEBUG2("Response will be broadcast");
		packet->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);

	} else if ((vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) /* DHCP-Client-IP-Address */ &&
		   (vp->vp_ipv4addr != htonl(INADDR_ANY))) {

		/*
		 * RFC 2131, page 23
		 *
		 * Unicast to
		 * - ciaddr if present
		 * otherwise to yiaddr
		 */
		packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
	} else {
		vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */
		if (!vp) {
			RPEDEBUG("Failed to find IP Address for request");
			goto error;
		}

		RDEBUG2("Response will be unicast to &DHCP-Your-IP-Address");
		packet->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;

		/*
		 * When sending a DHCP_OFFER, make sure our ARP table
		 * contains an entry for the client IP address, or else
		 * packet may not be forwarded if it was the first time
		 * the client was requesting an IP address.
		 */
		if (request->packet->code == FR_DHCP_OFFER) {
			VALUE_PAIR *hwvp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 267,
							       TAG_ANY); /* DHCP-Client-Hardware-Address */
			if (hwvp == NULL) {
				RDEBUG2("DHCP_OFFER packet received with no Client Hardware Address. "
					"Discarding packet");
				goto error;
			}
			if (fr_dhcpv4_udp_add_arp_entry(request->packet->sockfd, sock->src_interface,
							&vp->vp_ip, hwvp->vp_ether) < 0) {
				REDEBUG("Failed adding ARP entry");
				goto error;
			}
		}
	}

	packet->vps = request->packet->vps; /* hackity hack */

	/*
	 *	Our response doesn't go through process.c
	 */
	dhcp_packet_debug(request, packet, false);

	if (fr_dhcpv4_packet_encode(packet) < 0) {
		RPERROR("Failed encoding DHCP packet");
		goto error;
	}

	rcode = fr_dhcpv4_udp_packet_send(request->packet);

error:
	packet->vps = NULL;
	talloc_free(packet);
	return rcode;
}
Example #15
0
/*
 * given a radius request with many attributes in the EAP-SIM range, build
 * them all into a single EAP-SIM body.
 *
 */
int map_eapsim_basictypes(RADIUS_PACKET *r, eap_packet_t *ep)
{
	VALUE_PAIR	*vp;
	int		encoded_size;
	uint8_t		*encodedmsg, *attr;
	unsigned int	id, eapcode;
	uint8_t		*macspace;
	uint8_t const	*append;
	int		appendlen;
	unsigned char	subtype;
	vp_cursor_t	cursor;

	macspace = NULL;
	append = NULL;
	appendlen = 0;

	/*
	 * encodedmsg is now an EAP-SIM message.
	 * it might be too big for putting into an EAP-Type-SIM
	 *
	 */
	subtype = (vp = fr_pair_find_by_num(r->vps, PW_EAP_SIM_SUBTYPE, 0, TAG_ANY)) ?
		vp->vp_integer : EAPSIM_START;

	id = (vp = fr_pair_find_by_num(r->vps, PW_EAP_ID, 0, TAG_ANY)) ?
		vp->vp_integer : ((int)getpid() & 0xff);

	eapcode = (vp = fr_pair_find_by_num(r->vps, PW_EAP_CODE, 0, TAG_ANY)) ?
		vp->vp_integer : PW_EAP_REQUEST;

	/*
	 * take a walk through the attribute list to see how much space
	 * that we need to encode all of this.
	 */
	encoded_size = 0;
	for (vp = fr_cursor_init(&cursor, &r->vps);
	     vp;
	     vp = fr_cursor_next(&cursor)) {
		int roundedlen;
		int vplen;

		if ((vp->da->attr < PW_EAP_SIM_BASE) || (vp->da->attr >= (PW_EAP_SIM_BASE + 256))) {
			continue;
		}

		vplen = vp->vp_length;

		/*
		 * the AT_MAC attribute is a bit different, when we get to this
		 * attribute, we pull the contents out, save it for later
		 * processing, set the size to 16 bytes (plus 2 bytes padding).
		 *
		 * At this point, we only care about the size.
		 */
		if(vp->da->attr == PW_EAP_SIM_MAC) {
			vplen = 18;
		}

		/* round up to next multiple of 4, after taking in
		 * account the type and length bytes
		 */
		roundedlen = (vplen + 2 + 3) & ~3;
		encoded_size += roundedlen;
	}

	if (ep->code != PW_EAP_SUCCESS) {
		ep->code = eapcode;
	}

	ep->id = (id & 0xff);
	ep->type.num = PW_EAP_SIM;

	/*
	 * if no attributes were found, do very little.
	 *
	 */
	if (encoded_size == 0) {
		encodedmsg = talloc_array(ep, uint8_t, 3);
		/* FIX: could be NULL */

		encodedmsg[0] = subtype;
		encodedmsg[1] = 0;
		encodedmsg[2] = 0;

		ep->type.length = 3;
		ep->type.data = encodedmsg;

		return 0;
	}


	/*
	 * figured out the length, so allocate some space for the results.
	 *
	 * Note that we do not bother going through an "EAP" stage, which
	 * is a bit strange compared to the unmap, which expects to see
	 * an EAP-SIM virtual attributes.
	 *
	 * EAP is 1-code, 1-identifier, 2-length, 1-type = 5 overhead.
	 *
	 * SIM code adds a subtype, and 2 bytes of reserved = 3.
	 *
	 */
	encoded_size += 3;
	encodedmsg = talloc_array(ep, uint8_t, encoded_size);
	if (!encodedmsg) {
		return 0;
	}
	memset(encodedmsg, 0, encoded_size);

	/*
	 * now walk the attributes again, sticking them in.
	 *
	 * we go three bytes into the encoded message, because there are two
	 * bytes of reserved, and we will fill the "subtype" in later.
	 *
	 */
	attr = encodedmsg+3;

	for (vp = fr_cursor_first(&cursor); vp; vp = fr_cursor_next(&cursor)) {
		int roundedlen;

		if(vp->da->attr < PW_EAP_SIM_BASE ||
		   vp->da->attr >= PW_EAP_SIM_BASE + 256) {
			continue;
		}

		/*
		 * the AT_MAC attribute is a bit different, when we get to this
		 * attribute, we pull the contents out, save it for later
		 * processing, set the size to 16 bytes (plus 2 bytes padding).
		 *
		 * At this point, we put in zeros, and remember where the
		 * sixteen bytes go.
		 */
		if(vp->da->attr == PW_EAP_SIM_MAC) {
			roundedlen = 20;
			memset(&attr[2], 0, 18);
			macspace = &attr[4];
			append = vp->vp_octets;
			appendlen = vp->vp_length;
		} else {
			roundedlen = (vp->vp_length + 2 + 3) & ~3;
			memset(attr, 0, roundedlen);
			memcpy(&attr[2], vp->vp_strvalue, vp->vp_length);
		}
		attr[0] = vp->da->attr - PW_EAP_SIM_BASE;
		attr[1] = roundedlen >> 2;

		attr += roundedlen;
	}

	encodedmsg[0] = subtype;

	ep->type.length = encoded_size;
	ep->type.data = encodedmsg;

	/*
	 * if macspace was set and we have a key,
	 * then we should calculate the HMAC-SHA1 of the resulting EAP-SIM
	 * packet, appended with the value of append.
	 */
	vp = fr_pair_find_by_num(r->vps, PW_EAP_SIM_KEY, 0, TAG_ANY);
	if(macspace != NULL && vp != NULL) {
		unsigned char		*buffer;
		eap_packet_raw_t	*hdr;
		uint16_t		hmaclen, total_length = 0;
		unsigned char		sha1digest[20];

		total_length = EAP_HEADER_LEN + 1 + encoded_size;
		hmaclen = total_length + appendlen;
		buffer = talloc_array(r, uint8_t, hmaclen);
		hdr = (eap_packet_raw_t *) buffer;
		if (!hdr) {
			talloc_free(encodedmsg);
			return 0;
		}

		hdr->code = eapcode & 0xFF;
		hdr->id = (id & 0xFF);
		total_length = htons(total_length);
		memcpy(hdr->length, &total_length, sizeof(total_length));

		hdr->data[0] = PW_EAP_SIM;

		/* copy the data */
		memcpy(&hdr->data[1], encodedmsg, encoded_size);

		/* copy the nonce */
		memcpy(&hdr->data[encoded_size+1], append, appendlen);

		/* HMAC it! */
		fr_hmac_sha1(sha1digest, buffer, hmaclen, vp->vp_octets, vp->vp_length);

		/* done with the buffer, free it */
		talloc_free(buffer);

		/* now copy the digest to where it belongs in the AT_MAC */
		/* note that it is truncated to 128-bits */
		memcpy(macspace, sha1digest, 16);
	}

	/* if we had an AT_MAC and no key, then fail */
	if ((macspace != NULL) && !vp) {
		if (encodedmsg != NULL) {
			talloc_free(encodedmsg);
		}

		return 0;
	}

	return 1;
}
Example #16
0
static rlm_rcode_t dhcp_process(REQUEST *request)
{
	rlm_rcode_t	rcode;
	unsigned int	i;
	VALUE_PAIR	*vp;
	dhcp_socket_t	*sock;

	/*
	 *	If there's a giaddr, save it as the Relay-IP-Address
	 *	in the response.  That way the later code knows where
	 *	to send the reply.
	 */
	vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */
	if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) {
		VALUE_PAIR *relay;

		/* DHCP-Relay-IP-Address */
		MEM(relay = fr_pair_afrom_num(request->reply, DHCP_MAGIC_VENDOR, 222));
		relay->vp_ipv4addr = vp->vp_ipv4addr;
		fr_pair_add(&request->reply->vps, relay);
	}

	vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */
	if (vp) {
		fr_dict_enum_t *dv = fr_dict_enum_by_value(vp->da, &vp->data);

		if (dv) {
			CONF_SECTION *server, *unlang;

			RDEBUG("Trying sub-section dhcp %s {...}", dv->alias);

			server = cf_item_to_section(cf_parent(request->listener->cs));

			unlang = cf_section_find(server, "dhcp", dv->alias);
			rcode = unlang_interpret(request, unlang, RLM_MODULE_NOOP);
		} else {
			REDEBUG("Unknown DHCP-Message-Type %d", vp->vp_uint8);
			rcode = RLM_MODULE_FAIL;
		}
	} else {
		REDEBUG("Failed to find DHCP-Message-Type in packet!");
		rcode = RLM_MODULE_FAIL;
	}

	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */
	if (vp) {
		request->reply->code = vp->vp_uint8;
	}
	else switch (rcode) {
	case RLM_MODULE_OK:
	case RLM_MODULE_UPDATED:
		if (request->packet->code == FR_DHCP_DISCOVER) {
			request->reply->code = FR_DHCP_OFFER;
			break;

		} else if (request->packet->code == FR_DHCP_REQUEST) {
			request->reply->code = FR_DHCP_ACK;
			break;
		}
		request->reply->code = FR_DHCP_NAK;
		break;

	default:
	case RLM_MODULE_REJECT:
	case RLM_MODULE_FAIL:
	case RLM_MODULE_INVALID:
	case RLM_MODULE_NOOP:
	case RLM_MODULE_NOTFOUND:
		if (request->packet->code == FR_DHCP_DISCOVER) {
			request->reply->code = 0; /* ignore the packet */
		} else {
			request->reply->code = FR_DHCP_NAK;
		}
		break;

	case RLM_MODULE_HANDLED:
		request->reply->code = 0; /* ignore the packet */
		break;
	}

	/*
	 *	TODO: Handle 'output' of RLM_MODULE when acting as a
	 *	DHCP relay We may want to not forward packets in
	 *	certain circumstances.
	 */

	/*
	 * 	Handle requests when acting as a DHCP relay
	 */
	vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */
	if (!vp) {
		RPEDEBUG("Someone deleted the DHCP-Opcode!");
		return RLM_MODULE_FAIL;
	}

	/* BOOTREPLY received on port 67 (i.e. from a server) */
	if (vp->vp_uint8 == 2) {
		return dhcprelay_process_server_reply(request);
	}

	/* Packet from client, and we have DHCP-Relay-To-IP-Address */
	if (fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY)) {
		return dhcprelay_process_client_request(request);
	}

	/* else it's a packet from a client, without relaying */
	rad_assert(vp->vp_uint8 == 1); /* BOOTREQUEST */

	sock = request->listener->data;

	/*
	 *	Handle requests when acting as a DHCP server
	 */

	/*
	 *	Releases don't get replies.
	 */
	if (request->packet->code == FR_DHCP_RELEASE) {
		request->reply->code = 0;
	}

	if (request->reply->code == 0) {
		return RLM_MODULE_OK;
	}

	request->reply->sockfd = request->packet->sockfd;

	/*
	 *	Copy specific fields from packet to reply, if they
	 *	don't already exist
	 */
	for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) {
		uint32_t attr = attrnums[i];

		if (fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY)) continue;

		vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY);
		if (vp) {
			fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp));
		}
	}

	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */
	rad_assert(vp != NULL);
	vp->vp_uint8 = 2; /* BOOTREPLY */

	/*
	 *	Allow NAKs to be delayed for a short period of time.
	 */
	if (request->reply->code == FR_DHCP_NAK) {
		vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY, TAG_ANY);
		if (vp) {
			if (vp->vp_uint32 <= 10) {
				request->response_delay.tv_sec = vp->vp_uint32;
				request->response_delay.tv_usec = 0;
			} else {
				request->response_delay.tv_sec = 10;
				request->response_delay.tv_usec = 0;
			}
		} else {
#ifndef USEC
#define USEC 1000000
#endif
			vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY_USEC, TAG_ANY);
			if (vp) {
				if (vp->vp_uint32 <= 10 * USEC) {
					request->response_delay.tv_sec = vp->vp_uint32 / USEC;
					request->response_delay.tv_usec = vp->vp_uint32 % USEC;
				} else {
					request->response_delay.tv_sec = 10;
					request->response_delay.tv_usec = 0;
				}
			}
		}
	}

	/*
	 *	Prepare the reply packet for sending through dhcp_socket_send()
	 */
	request->reply->dst_ipaddr.af = AF_INET;
	request->reply->src_ipaddr.af = AF_INET;
	request->reply->src_ipaddr.prefix = 32;

	/*
	 *	Packet-Src-IP-Address has highest precedence
	 */
	vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_SRC_IP_ADDRESS, TAG_ANY);
	if (vp) {
		request->reply->if_index = 0;	/* Must be 0, we don't know the outbound if_index */
		request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
	/*
	 *	The request was unicast (via a relay)
	 */
	} else if (request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST) &&
		   request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) {
		request->reply->src_ipaddr.addr.v4.s_addr = request->packet->dst_ipaddr.addr.v4.s_addr;
		request->reply->if_index = request->packet->if_index;
	/*
	 *	The listener was bound to an IP address, or we determined
	 *	the address automatically, as it was the only address bound
	 *	to the interface, and we bound to the interface.
	 */
	} else if (sock->src_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) {
		request->reply->src_ipaddr.addr.v4.s_addr = sock->src_ipaddr.addr.v4.s_addr;
#ifdef WITH_IFINDEX_IPADDR_RESOLUTION
	/*
	 *	We built with udpfromto and have the if_index of the receiving
	 *	interface, which we can now resolve to an IP address.
	 */
	} else if (request->packet->if_index > 0) {
		fr_ipaddr_t primary;

		if (fr_ipaddr_from_ifindex(&primary, request->packet->sockfd, request->packet->dst_ipaddr.af,
					   request->packet->if_index) < 0) {
			RPEDEBUG("Failed determining src_ipaddr from if_index");
			return RLM_MODULE_FAIL;
		}
		request->reply->src_ipaddr.addr.v4.s_addr = primary.addr.v4.s_addr;
#endif
	/*
	 *	There's a Server-Identification attribute
	 */
	} else if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 54, TAG_ANY))) {
		request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
	} else {
		REDEBUG("Unable to determine correct src_ipaddr for response");
		return RLM_MODULE_FAIL;
	}
	request->reply->dst_port = request->packet->src_port;
	request->reply->src_port = request->packet->dst_port;

	/*
	 *	Answer to client's nearest DHCP relay.
	 *
	 *	Which may be different than the giaddr given in the
	 *	packet to the client.  i.e. the relay may have a
	 *	public IP, but the gateway a private one.
	 */
	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 272, TAG_ANY); /* DHCP-Relay-IP-Address */
	if (vp && (vp->vp_ipv4addr != ntohl(INADDR_ANY))) {
		RDEBUG2("Reply will be unicast to giaddr from original packet");
		request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
		request->reply->dst_port = request->packet->dst_port;

		vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_DST_PORT, TAG_ANY);
		if (vp) request->reply->dst_port = vp->vp_uint16;

		return RLM_MODULE_OK;
	}

	/*
	 *	Answer to client's nearest DHCP gateway.  In this
	 *	case, the client can reach the gateway, as can the
	 *	server.
	 *
	 *	We also use *our* source port as the destination port.
	 *	Gateways are servers, and listen on the server port,
	 *	not the client port.
	 */
	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */
	if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) {
		RDEBUG2("Reply will be unicast to giaddr");
		request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
		request->reply->dst_port = request->packet->dst_port;
		return RLM_MODULE_OK;
	}

	/*
	 *	If it's a NAK, or the broadcast flag was set, ond
	 *	there's no client-ip-address, send a broadcast.
	 */
	if ((request->reply->code == FR_DHCP_NAK) ||
	    ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) && /* DHCP-Flags */
	     (vp->vp_uint32 & 0x8000) &&
	     ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */
	      (vp->vp_ipv4addr == htonl(INADDR_ANY))))) {
		/*
		 * RFC 2131, page 23
		 *
		 * Broadcast on
		 * - DHCPNAK
		 * or
		 * - Broadcast flag is set up and ciaddr == NULL
		 */
		RDEBUG2("Reply will be broadcast");
		request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);
		return RLM_MODULE_OK;
	}

	/*
	 *	RFC 2131, page 23
	 *
	 *	Unicast to ciaddr if present, otherwise to yiaddr.
	 */
	if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */
	    (vp->vp_ipv4addr != htonl(INADDR_ANY))) {
		RDEBUG2("Reply will be sent unicast to &DHCP-Client-IP-Address");
		request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;
		return RLM_MODULE_OK;
	}

	vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */
	if (!vp) {
		REDEBUG("Can't assign address to client: Neither &reply:DHCP-Client-IP-Address nor "
			"&reply:DHCP-Your-IP-Address set");
		/*
		 *	There is nowhere to send the response to, so don't bother.
		 */
		request->reply->code = 0;
		return RLM_MODULE_FAIL;
	}

#ifdef SIOCSARP
	/*
	 *	The system is configured to listen for broadcast
	 *	packets, which means we'll need to send unicast
	 *	replies, to IPs which haven't yet been assigned.
	 *	Therefore, we need to update the ARP table.
	 *
	 *	However, they haven't specified a interface.  So we
	 *	can't update the ARP table.  And we must send a
	 *	broadcast response.
	 */
	if (sock->lsock.broadcast && !sock->src_interface) {
		WARN("You MUST set \"interface\" if you have \"broadcast = yes\"");
		RDEBUG2("Reply will be broadcast as no interface was defined");
		request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);
		return RLM_MODULE_OK;
	}

	RDEBUG2("Reply will be unicast to &DHCP-Your-IP-Address");
	request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr;

	/*
	 *	When sending a DHCP_OFFER, make sure our ARP table
	 *	contains an entry for the client IP address.
	 *	Otherwise the packet may not be sent to the client, as
	 *	the OS has no ARP entry for it.
	 *
	 *	This is a cute hack to avoid us having to create a raw
	 *	socket to send DHCP packets.
	 */
	if (request->reply->code == FR_DHCP_OFFER) {
		VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */

		if (!hwvp) return RLM_MODULE_FAIL;

		if (fr_dhcpv4_udp_add_arp_entry(request->reply->sockfd, sock->src_interface,
						&vp->vp_ip, hwvp->vp_ether) < 0) {
			RPEDEBUG("Failed adding arp entry");
			return RLM_MODULE_FAIL;
		}
	}
#else
	if (request->packet->src_ipaddr.addr.v4.s_addr != ntohl(INADDR_NONE)) {
		RDEBUG2("Reply will be unicast to the unicast source IP address");
		request->reply->dst_ipaddr.addr.v4.s_addr = request->packet->src_ipaddr.addr.v4.s_addr;
	} else {
		RDEBUG2("Reply will be broadcast as this system does not support ARP updates");
		request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST);
	}
#endif

	return RLM_MODULE_OK;
}
Example #17
0
static rlm_rcode_t mod_authorize(void *instance, REQUEST *request)
{
	rlm_rcode_t rcode = RLM_MODULE_NOOP;

	rlm_sql_t *inst = instance;
	rlm_sql_handle_t  *handle;

	VALUE_PAIR *check_tmp = NULL;
	VALUE_PAIR *reply_tmp = NULL;
	VALUE_PAIR *user_profile = NULL;

	bool	user_found = false;

	sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT;

	int	rows;

	char	*expanded = NULL;

	rad_assert(request->packet != NULL);
	rad_assert(request->reply != NULL);

	if (!inst->config->authorize_check_query && !inst->config->authorize_reply_query &&
	    !inst->config->read_groups && !inst->config->read_profiles) {
		RWDEBUG("No authorization checks configured, returning noop");

		return RLM_MODULE_NOOP;
	}

	/*
	 *	Set, escape, and check the user attr here
	 */
	if (sql_set_user(inst, request, NULL) < 0) {
		return RLM_MODULE_FAIL;
	}

	/*
	 *	Reserve a socket
	 *
	 *	After this point use goto error or goto release to cleanup socket temporary pairlists and
	 *	temporary attributes.
	 */
	handle = fr_connection_get(inst->pool);
	if (!handle) {
		rcode = RLM_MODULE_FAIL;
		goto error;
	}

	/*
	 *	Query the check table to find any conditions associated with this user/realm/whatever...
	 */
	if (inst->config->authorize_check_query) {
		vp_cursor_t cursor;
		VALUE_PAIR *vp;

		if (radius_axlat(&expanded, request, inst->config->authorize_check_query,
				 inst->sql_escape_func, handle) < 0) {
			REDEBUG("Error generating query");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		rows = sql_getvpdata(request, inst, request, &handle, &check_tmp, expanded);
		TALLOC_FREE(expanded);
		if (rows < 0) {
			REDEBUG("Error getting check attributes");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		if (rows == 0) goto skipreply;	/* Don't need to free VPs we don't have */

		/*
		 *	Only do this if *some* check pairs were returned
		 */
		RDEBUG2("User found in radcheck table");
		user_found = true;
		if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) {
			fr_pair_list_free(&check_tmp);
			check_tmp = NULL;
			goto skipreply;
		}

		RDEBUG2("Conditional check items matched, merging assignment check items");
		RINDENT();
		for (vp = fr_cursor_init(&cursor, &check_tmp);
		     vp;
		     vp = fr_cursor_next(&cursor)) {
			if (!fr_assignment_op[vp->op]) continue;

			rdebug_pair(2, request, vp, NULL);
		}
		REXDENT();
		radius_pairmove(request, &request->config, check_tmp, true);

		rcode = RLM_MODULE_OK;
		check_tmp = NULL;
	}

	if (inst->config->authorize_reply_query) {
		/*
		 *	Now get the reply pairs since the paircompare matched
		 */
		if (radius_axlat(&expanded, request, inst->config->authorize_reply_query,
				 inst->sql_escape_func, handle) < 0) {
			REDEBUG("Error generating query");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		rows = sql_getvpdata(request->reply, inst, request, &handle, &reply_tmp, expanded);
		TALLOC_FREE(expanded);
		if (rows < 0) {
			REDEBUG("SQL query error getting reply attributes");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		if (rows == 0) goto skipreply;

		do_fall_through = fall_through(reply_tmp);

		RDEBUG2("User found in radreply table, merging reply items");
		user_found = true;

		rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp, NULL);

		radius_pairmove(request, &request->reply->vps, reply_tmp, true);

		rcode = RLM_MODULE_OK;
		reply_tmp = NULL;
	}

	/*
	 *	Neither group checks or profiles will work without
	 *	a group membership query.
	 */
	if (!inst->config->groupmemb_query) goto release;

skipreply:
	if ((do_fall_through == FALL_THROUGH_YES) ||
	    (inst->config->read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) {
		rlm_rcode_t ret;

		RDEBUG3("... falling-through to group processing");
		ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
		switch (ret) {
		/*
		 *	Nothing bad happened, continue...
		 */
		case RLM_MODULE_UPDATED:
			rcode = RLM_MODULE_UPDATED;
			/* FALL-THROUGH */
		case RLM_MODULE_OK:
			if (rcode != RLM_MODULE_UPDATED) {
				rcode = RLM_MODULE_OK;
			}
			/* FALL-THROUGH */
		case RLM_MODULE_NOOP:
			user_found = true;
			break;

		case RLM_MODULE_NOTFOUND:
			break;

		default:
			rcode = ret;
			goto release;
		}
	}

	/*
	 *	Repeat the above process with the default profile or User-Profile
	 */
	if ((do_fall_through == FALL_THROUGH_YES) ||
	    (inst->config->read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) {
		rlm_rcode_t ret;

		/*
		 *  Check for a default_profile or for a User-Profile.
		 */
		RDEBUG3("... falling-through to profile processing");
		user_profile = fr_pair_find_by_num(request->config, 0, PW_USER_PROFILE, TAG_ANY);

		char const *profile = user_profile ?
				      user_profile->vp_strvalue :
				      inst->config->default_profile;

		if (!profile || !*profile) {
			goto release;
		}

		RDEBUG2("Checking profile %s", profile);

		if (sql_set_user(inst, request, profile) < 0) {
			REDEBUG("Error setting profile");
			rcode = RLM_MODULE_FAIL;
			goto error;
		}

		ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through);
		switch (ret) {
		/*
		 *	Nothing bad happened, continue...
		 */
		case RLM_MODULE_UPDATED:
			rcode = RLM_MODULE_UPDATED;
			/* FALL-THROUGH */
		case RLM_MODULE_OK:
			if (rcode != RLM_MODULE_UPDATED) {
				rcode = RLM_MODULE_OK;
			}
			/* FALL-THROUGH */
		case RLM_MODULE_NOOP:
			user_found = true;
			break;

		case RLM_MODULE_NOTFOUND:
			break;

		default:
			rcode = ret;
			goto release;
		}
	}

	/*
	 *	At this point the key (user) hasn't be found in the check table, the reply table
	 *	or the group mapping table, and there was no matching profile.
	 */
release:
	if (!user_found) {
		rcode = RLM_MODULE_NOTFOUND;
	}

	fr_connection_release(inst->pool, handle);
	sql_unset_user(inst, request);

	return rcode;

error:
	fr_pair_list_free(&check_tmp);
	fr_pair_list_free(&reply_tmp);
	sql_unset_user(inst, request);

	fr_connection_release(inst->pool, handle);

	return rcode;
}
/*
 *	Authenticate the user with the given password.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
{
	rlm_smsotp_t *inst = instance;
	VALUE_PAIR *state;
	int bufsize;
	int *fdp;
	rlm_rcode_t rcode = RLM_MODULE_FAIL;
	char buffer[1000];
	char output[1000];

	fdp = fr_connection_get(inst->pool);
	if (!fdp) return RLM_MODULE_FAIL;

	/* Get greeting */
	bufsize = read_all(fdp, buffer, sizeof(buffer));
	if (bufsize <= 0) {
		REDEBUG("Failed reading from socket");
		goto done;
	}

	/*
	 *  Look for the 'state' attribute.
	 */
#define WRITE_ALL(_a,_b,_c) if (write_all(_a,_b,_c) < 0) goto done;
	state = fr_pair_find_by_num(request->packet->vps, PW_STATE, 0, TAG_ANY);
	if (state) {
		RDEBUG("Found reply to access challenge");

		/* send username */
		snprintf(output, sizeof(output), "check otp for %s\n",
			 request->username->vp_strvalue);
		WRITE_ALL(fdp, output, strlen(output));

		(void) read_all(fdp, buffer, sizeof(buffer));

		/* send password */
		snprintf(output, sizeof(output), "user otp is %s\n",
			 request->password->vp_strvalue);
		WRITE_ALL(fdp, output, strlen(output));

		(void) read_all(fdp, buffer, sizeof(buffer));

		/* set uuid */
		snprintf(output, sizeof(output), "otp id is %s\n",
			 state->vp_strvalue);
		WRITE_ALL(fdp, output, strlen(output));

		(void) read_all(fdp, buffer, sizeof(buffer));

		/* now check the otp */
		WRITE_ALL(fdp, "get check result\n", 17);

		(void) read_all(fdp, buffer, sizeof(buffer));

		/* end the sesssion */
		WRITE_ALL(fdp, "quit\n", 5);

		RDEBUG("answer is %s", buffer);
		if (strcmp(buffer,"OK") == 0) {
			rcode = RLM_MODULE_OK;
		}

		goto done;
	}

	RDEBUG("Generating OTP");

	/* set username */
	snprintf(output, sizeof(output), "generate otp for %s\n",
		 request->username->vp_strvalue);
	WRITE_ALL(fdp, output, strlen(output));

	(void) read_all(fdp, buffer, sizeof(buffer));

	/* end the sesssion */
	WRITE_ALL(fdp, "quit\n", 5);

	RDEBUG("Unique ID is %s", buffer);

	/* check the return string */
	if (strcmp(buffer,"FAILED") == 0) { /* smsotp script returns a error */
		goto done;
	}

	/*
	 *	Create the challenge, and add it to the reply.
	 */

	pair_make_reply("Reply-Message", inst->challenge, T_OP_EQ);
	pair_make_reply("State", buffer, T_OP_EQ);

	/*
	 *  Mark the packet as an Access-Challenge packet.
	 *
	 *  The server will take care of sending it to the user.
	 */
	request->reply->code = PW_CODE_ACCESS_CHALLENGE;
	DEBUG("rlm_smsotp: Sending Access-Challenge");

	rcode = RLM_MODULE_HANDLED;

done:
	fr_connection_release(inst->pool, fdp);
	return rcode;
}
Example #19
0
static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request)
{
	rlm_rcode_t		rcode = RLM_MODULE_OK;
	rlm_sql_handle_t 	*handle = NULL;
	rlm_sql_t		*inst = instance;
	rlm_sql_row_t		row;
	int			check = 0;
	uint32_t		ipno = 0;
	char const     		*call_num = NULL;
	VALUE_PAIR		*vp;
	int			ret;
	uint32_t		nas_addr = 0;
	uint32_t		nas_port = 0;

	char 			*expanded = NULL;

	/* If simul_count_query is not defined, we don't do any checking */
	if (!inst->config->simul_count_query) return RLM_MODULE_NOOP;

	if ((!request->username) || (request->username->vp_length == '\0')) {
		REDEBUG("Zero Length username not permitted");

		return RLM_MODULE_INVALID;
	}

	if (sql_set_user(inst, request, NULL) < 0) {
		return RLM_MODULE_FAIL;
	}

	/* initialize the sql socket */
	handle = fr_connection_get(inst->pool);
	if (!handle) {
		sql_unset_user(inst, request);
		return RLM_MODULE_FAIL;
	}

	if (radius_axlat(&expanded, request, inst->config->simul_count_query, inst->sql_escape_func, handle) < 0) {
		fr_connection_release(inst->pool, handle);
		sql_unset_user(inst, request);
		return RLM_MODULE_FAIL;
	}

	if (rlm_sql_select_query(inst, request, &handle, expanded) != RLM_SQL_OK) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	ret = rlm_sql_fetch_row(&row, inst, request, &handle);
	if (ret != 0) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}
	if (!row) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	request->simul_count = atoi(row[0]);

	(inst->module->sql_finish_select_query)(handle, inst->config);
	TALLOC_FREE(expanded);

	if (request->simul_count < request->simul_max) {
		rcode = RLM_MODULE_OK;
		goto finish;
	}

	/*
	 *	Looks like too many sessions, so let's start verifying
	 *	them, unless told to rely on count query only.
	 */
	if (!inst->config->simul_verify_query) {
		rcode = RLM_MODULE_OK;

		goto finish;
	}

	if (radius_axlat(&expanded, request, inst->config->simul_verify_query, inst->sql_escape_func, handle) < 0) {
		rcode = RLM_MODULE_FAIL;

		goto finish;
	}

	if (rlm_sql_select_query(inst, request, &handle, expanded) != RLM_SQL_OK) goto release;

	/*
	 *      Setup some stuff, like for MPP detection.
	 */
	request->simul_count = 0;

	if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_FRAMED_IP_ADDRESS, TAG_ANY)) != NULL) {
		ipno = vp->vp_ipaddr;
	}

	if ((vp = fr_pair_find_by_num(request->packet->vps, 0, PW_CALLING_STATION_ID, TAG_ANY)) != NULL) {
		call_num = vp->vp_strvalue;
	}

	while (rlm_sql_fetch_row(&row, inst, request, &handle) == 0) {
		row = handle->row;
		if (!row) {
			break;
		}

		if (!row[2]){
			RDEBUG("Cannot zap stale entry. No username present in entry");
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}

		if (!row[1]){
			RDEBUG("Cannot zap stale entry. No session id in entry");
			rcode = RLM_MODULE_FAIL;

			goto finish;
		}

		if (row[3]) {
			nas_addr = inet_addr(row[3]);
		}

		if (row[4]) {
			nas_port = atoi(row[4]);
		}

		check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
		if (check == 0) {
			/*
			 *	Stale record - zap it.
			 */
			if (inst->config->delete_stale_sessions == true) {
				uint32_t framed_addr = 0;
				char proto = 0;
				int sess_time = 0;

				if (row[5])
					framed_addr = inet_addr(row[5]);
				if (row[7]){
					if (strcmp(row[7], "PPP") == 0)
						proto = 'P';
					else if (strcmp(row[7], "SLIP") == 0)
						proto = 'S';
				}
				if (row[8])
					sess_time = atoi(row[8]);
				session_zap(request, nas_addr, nas_port,
					    row[2], row[1], framed_addr,
					    proto, sess_time);
			}
		}
		else if (check == 1) {
			/*
			 *	User is still logged in.
			 */
			++request->simul_count;

			/*
			 *      Does it look like a MPP attempt?
			 */
			if (row[5] && ipno && inet_addr(row[5]) == ipno) {
				request->simul_mpp = 2;
			} else if (row[6] && call_num && !strncmp(row[6],call_num,16)) {
				request->simul_mpp = 2;
			}
		} else {
			/*
			 *      Failed to check the terminal server for
			 *      duplicate logins: return an error.
			 */
			REDEBUG("Failed to check the terminal server for user '%s'.", row[2]);

			rcode = RLM_MODULE_FAIL;
			goto finish;
		}
	}

finish:
	(inst->module->sql_finish_select_query)(handle, inst->config);
release:
	fr_connection_release(inst->pool, handle);
	talloc_free(expanded);
	sql_unset_user(inst, request);

	/*
	 *	The Auth module apparently looks at request->simul_count,
	 *	not the return value of this module when deciding to deny
	 *	a call for too many sessions.
	 */
	return rcode;
}
Example #20
0
/*
 *	Preprocess a request.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	int r;
	rlm_preprocess_t *inst = instance;

	VALUE_PAIR *vp;

	/*
	 *	Mangle the username, to get rid of stupid implementation
	 *	bugs.
	 */
	rad_mangle(inst, request);

	if (inst->with_ascend_hack) {
		/*
		 *	If we're using Ascend systems, hack the NAS-Port-Id
		 *	in place, to go from Ascend's weird values to something
		 *	approaching rationality.
		 */
		ascend_nasport_hack(fr_pair_find_by_num(request->packet->vps, PW_NAS_PORT, 0, TAG_ANY),
				    inst->ascend_channels_per_line);
	}

	if (inst->with_cisco_vsa_hack) {
		/*
		 *	We need to run this hack because the h323-conf-id
		 *	attribute should be used.
		 */
		cisco_vsa_hack(request);
	}

	if (inst->with_alvarion_vsa_hack) {
		/*
		 *	We need to run this hack because the Alvarion
		 *	people are crazy.
		 */
		alvarion_vsa_hack(request->packet->vps);
	}

	if (inst->with_cablelabs_vsa_hack) {
		/*
		 *	We need to run this hack because the Cablelabs
		 *	people are crazy.
		 */
		cablelabs_vsa_hack(&request->packet->vps);
	}

	/*
	 *	Add an event timestamp. Means Event-Timestamp can be used
	 *	consistently instead of one letter expansions.
	 */
	vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY);
	if (!vp) {
		vp = radius_pair_create(request->packet, &request->packet->vps, PW_EVENT_TIMESTAMP, 0);
		vp->vp_date = request->packet->timestamp.tv_sec;
	}

	/*
	 *	Note that we add the Request-Src-IP-Address to the request
	 *	structure BEFORE checking huntgroup access.  This allows
	 *	the Request-Src-IP-Address to be used for huntgroup
	 *	comparisons.
	 */
	if (add_nas_attr(request) < 0) {
		return RLM_MODULE_FAIL;
	}

	hints_setup(inst->hints, request);

	/*
	 *      If there is a PW_CHAP_PASSWORD attribute but there
	 *      is PW_CHAP_CHALLENGE we need to add it so that other
	 *	modules can use it as a normal attribute.
	 */
	if (fr_pair_find_by_num(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) &&
	    fr_pair_find_by_num(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL) {
		vp = radius_pair_create(request->packet, &request->packet->vps, PW_CHAP_CHALLENGE, 0);
		fr_pair_value_memcpy(vp, request->packet->vector, AUTH_VECTOR_LEN);
	}

	if ((r = huntgroup_access(request, inst->huntgroups)) != RLM_MODULE_OK) {
		char buf[1024];
		RIDEBUG("No huntgroup access: [%s] (%s)",
			request->username ? request->username->vp_strvalue : "<NO User-Name>",
			auth_name(buf, sizeof(buf), request, 1));

		return r;
	}

	return RLM_MODULE_OK; /* Meaning: try next authorization module */
}
Example #21
0
/*
 *	rad_accounting: call modules.
 *
 *	The return value of this function isn't actually used right now, so
 *	it's not entirely clear if it is returning the right things. --Pac.
 */
int rad_accounting(REQUEST *request)
{
	int result = RLM_MODULE_OK;


#ifdef WITH_PROXY
#define WAS_PROXIED (request->proxy)
#else
#define WAS_PROXIED (0)
#endif

	/*
	 *	Run the modules only once, before proxying.
	 */
	if (!WAS_PROXIED) {
		VALUE_PAIR	*vp;
		int		acct_type = 0;

		result = module_preacct(request);
		switch (result) {
		/*
		 *	The module has a number of OK return codes.
		 */
		case RLM_MODULE_NOOP:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			break;
		/*
		 *	The module handled the request, stop here.
		 */
		case RLM_MODULE_HANDLED:
			return result;
		/*
		 *	The module failed, or said the request is
		 *	invalid, therefore we stop here.
		 */
		case RLM_MODULE_FAIL:
		case RLM_MODULE_INVALID:
		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_USERLOCK:
		default:
			return result;
		}

		/*
		 *	Do the data storage before proxying. This is to ensure
		 *	that we log the packet, even if the proxy never does.
		 */
		vp = fr_pair_find_by_num(request->config, 0, PW_ACCT_TYPE, TAG_ANY);
		if (vp) {
			acct_type = vp->vp_integer;
			DEBUG2("  Found Acct-Type %s",
			       fr_dict_enum_name_by_da(NULL, vp->da, acct_type));
		}
		result = process_accounting(acct_type, request);
		switch (result) {
		/*
		 *	In case the accounting module returns FAIL,
		 *	it's still useful to send the data to the
		 *	proxy.
		 */
		case RLM_MODULE_FAIL:
		case RLM_MODULE_NOOP:
		case RLM_MODULE_OK:
		case RLM_MODULE_UPDATED:
			break;
		/*
		 *	The module handled the request, don't reply.
		 */
		case RLM_MODULE_HANDLED:
			return result;
		/*
		 *	Neither proxy, nor reply to invalid requests.
		 */
		case RLM_MODULE_INVALID:
		case RLM_MODULE_NOTFOUND:
		case RLM_MODULE_REJECT:
		case RLM_MODULE_USERLOCK:
		default:
			return result;
		}

		/*
		 *	Maybe one of the preacct modules has decided
		 *	that a proxy should be used.
		 */
		if ((vp = fr_pair_find_by_num(request->config, 0, PW_PROXY_TO_REALM, TAG_ANY))) {
			REALM *realm;

			/*
			 *	Check whether Proxy-To-Realm is
			 *	a LOCAL realm.
			 */
			realm = realm_find2(vp->vp_strvalue);
			if (realm && !realm->acct_pool) {
				DEBUG("rad_accounting: Cancelling proxy to realm %s, as it is a LOCAL realm.", realm->name);
				fr_pair_delete_by_num(&request->config, 0, PW_PROXY_TO_REALM, TAG_ANY);
			} else {
				/*
				 *	Don't reply to the NAS now because
				 *	we have to send the proxied packet
				 *	before that.
				 */
				return result;
			}
		}
	}

#ifdef WITH_PROXY
	/*
	 *	We didn't see a reply to the proxied request.  Fail.
	 */
	if (request->proxy && !request->proxy_reply) return RLM_MODULE_FAIL;
#endif

	/*
	 *	We get here IF we're not proxying, OR if we've
	 *	received the accounting reply from the end server,
	 *	THEN we can reply to the NAS.
	 *      If the accounting module returns NOOP, the data
	 *      storage did not succeed, so radiusd should not send
	 *      Accounting-Response.
	 */
	switch (result) {
	/*
	 *	Send back an ACK to the NAS.
	 */
	case RLM_MODULE_OK:
	case RLM_MODULE_UPDATED:
		request->reply->code = PW_CODE_ACCOUNTING_RESPONSE;
		break;

	/*
	 *	Failed to log or to proxy the accounting data,
	 *	therefore don't reply to the NAS.
	 */
	case RLM_MODULE_FAIL:
	case RLM_MODULE_INVALID:
	case RLM_MODULE_NOOP:
	case RLM_MODULE_NOTFOUND:
	case RLM_MODULE_REJECT:
	case RLM_MODULE_USERLOCK:
	default:
		break;
	}
	return result;
}
Example #22
0
/*
 *	Generate a challenge to be presented to the user.
 */
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request)
{
	rlm_otp_t *inst = (rlm_otp_t *) instance;

	char challenge[OTP_MAX_CHALLENGE_LEN + 1];	/* +1 for '\0' terminator */
	int auth_type_found;

	/* Early exit if Auth-Type != inst->name */
	{
		VALUE_PAIR *vp;

		auth_type_found = 0;
		vp = fr_pair_find_by_num(request->config, 0, PW_AUTH_TYPE, TAG_ANY);
		if (vp) {
			auth_type_found = 1;
			if (strcmp(vp->vp_strvalue, inst->name)) {
				return RLM_MODULE_NOOP;
			}
		}
	}

	/* The State attribute will be present if this is a response. */
	if (fr_pair_find_by_num(request->packet->vps, 0, PW_STATE, TAG_ANY) != NULL) {
		DEBUG("rlm_otp: autz: Found response to Access-Challenge");

		return RLM_MODULE_OK;
	}

	/* User-Name attribute required. */
	if (!request->username) {
		RWDEBUG("Attribute \"User-Name\" "
		       "required for authentication");

		return RLM_MODULE_INVALID;
	}

	if (otp_pwe_present(request) == 0) {
		RWDEBUG("Attribute "
			"\"User-Password\" or equivalent required "
			"for authentication");

		return RLM_MODULE_INVALID;
	}

	/*
	 * 	We used to check for special "challenge" and "resync" passcodes
	 * 	here, but these are complicated to explain and application is
	 * 	limited.  More importantly, since we've removed all actual OTP
	 * 	code (now we ask otpd), it's awkward for us to support them.
	 * 	Should the need arise to reinstate these options, the most
	 *	likely choice is to duplicate some otpd code here.
	 */
	if (inst->allow_sync && !inst->allow_async) {
		/* This is the token sync response. */
		if (!auth_type_found) {
			pair_make_config("Auth-Type", inst->name, T_OP_EQ);
		}

		return RLM_MODULE_OK;
	}

	/*
	 *	Generate a random challenge.
	 */
	otp_async_challenge(challenge, inst->challenge_len);

	/*
	 *	Create the State attribute, which will be returned to
	 *	us along with the response.
	 *
	 *	We will need this to verify the response.
	 *
	 *	It must be hmac protected to prevent insertion of arbitrary
	 *	State by an inside attacker.
	 *
	 *	If we won't actually use the State (server config doesn't
	 *	allow async), we just use a trivial State.
	 *
	 *	We always create at least a trivial State, so mod_authorize()
	 *	can quickly pass on to mod_authenticate().
	 */
	{
		int32_t now = htonl(time(NULL)); //!< Low-order 32 bits on LP64.

		char gen_state[OTP_MAX_RADSTATE_LEN];
		size_t len;
		VALUE_PAIR *vp;

		len = otp_gen_state(gen_state, challenge, inst->challenge_len,
				    0, now, inst->hmac_key);

		vp = fr_pair_afrom_num(request->reply, 0, PW_STATE);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		fr_pair_value_memcpy(vp, (uint8_t const *) gen_state, len);
		fr_pair_add(&request->reply->vps, vp);
	}

	/*
	 *	Add the challenge to the reply.
	 */
	{
		VALUE_PAIR *vp;

		char *expanded = NULL;
		ssize_t len;

		/*
		 *	First add the internal OTP challenge attribute to
		 *	the reply list.
		 */
		vp = fr_pair_afrom_num(request->reply, 0, PW_OTP_CHALLENGE);
		if (!vp) {
			return RLM_MODULE_FAIL;
		}

		fr_pair_value_strcpy(vp, challenge);
		vp->op = T_OP_SET;

		fr_pair_add(&request->reply->vps, vp);

		/*
		 *	Then add the message to the user to they known
		 *	what the challenge value is.
		 */

		len = radius_axlat(&expanded, request, inst->chal_prompt, NULL, NULL);
		if (len < 0) {
			return RLM_MODULE_FAIL;
		}

		vp = fr_pair_afrom_num(request->reply, 0, PW_REPLY_MESSAGE);
		if (!vp) {
			talloc_free(expanded);
			return RLM_MODULE_FAIL;
		}
		fr_pair_value_strnsteal(vp, expanded, len);
		vp->op = T_OP_SET;

		fr_pair_add(&request->reply->vps, vp);
	}

	/*
	 *	Mark the packet as an Access-Challenge packet.
	 * 	The server will take care of sending it to the user.
	 */
	request->reply->code = PW_CODE_ACCESS_CHALLENGE;
	DEBUG("rlm_otp: Sending Access-Challenge");

	if (!auth_type_found) {
		pair_make_config("Auth-Type", inst->name, T_OP_EQ);
	}

	return RLM_MODULE_HANDLED;
}
Example #23
0
/** Write accounting data to Couchbase documents
 *
 * Handle accounting requests and store the associated data into JSON documents
 * in couchbase mapping attribute names to JSON element names per the module configuration.
 *
 * When an existing document already exists for the same accounting section the new attributes
 * will be merged with the currently existing data.  When conflicts arrise the new attribute
 * value will replace or be added to the existing value.
 *
 * @param instance The module instance.
 * @param request  The accounting request object.
 * @return Operation status (#rlm_rcode_t).
 */
static rlm_rcode_t mod_accounting(void *instance, REQUEST *request)
{
	rlm_couchbase_t *inst = instance;       /* our module instance */
	rlm_couchbase_handle_t *handle = NULL;  /* connection pool handle */
	rlm_rcode_t rcode = RLM_MODULE_OK;      /* return code */
	VALUE_PAIR *vp;                         /* radius value pair linked list */
	char buffer[MAX_KEY_SIZE];
	char const *dockey;			/* our document key */
	char document[MAX_VALUE_SIZE];          /* our document body */
	char element[MAX_KEY_SIZE];             /* mapped radius attribute to element name */
	int status = 0;                         /* account status type */
	int docfound = 0;                       /* document found toggle */
	lcb_error_t cb_error = LCB_SUCCESS;     /* couchbase error holder */
	ssize_t slen;

	/* assert packet as not null */
	rad_assert(request->packet != NULL);

	/* sanity check */
	if ((vp = fr_pair_find_by_num(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) == NULL) {
		/* log debug */
		RDEBUG("could not find status type in packet");
		/* return */
		return RLM_MODULE_NOOP;
	}

	/* set status */
	status = vp->vp_integer;

	/* acknowledge the request but take no action */
	if (status == PW_STATUS_ACCOUNTING_ON || status == PW_STATUS_ACCOUNTING_OFF) {
		/* log debug */
		RDEBUG("handling accounting on/off request without action");
		/* return */
		return RLM_MODULE_OK;
	}

	/* get handle */
	handle = fr_connection_get(inst->pool);

	/* check handle */
	if (!handle) return RLM_MODULE_FAIL;

	/* set couchbase instance */
	lcb_t cb_inst = handle->handle;

	/* set cookie */
	cookie_t *cookie = handle->cookie;

	/* attempt to build document key */
	slen = tmpl_expand(&dockey, buffer, sizeof(buffer), request, inst->acct_key, NULL, NULL);
	if (slen < 0) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}
	if ((dockey == buffer) && is_truncated((size_t)slen, sizeof(buffer))) {
		REDEBUG("Key too long, expected < " STRINGIFY(sizeof(buffer)) " bytes, got %zi bytes", slen);
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto finish;
	}

	/* attempt to fetch document */
	cb_error = couchbase_get_key(cb_inst, cookie, dockey);

	/* check error and object */
	if (cb_error != LCB_SUCCESS || cookie->jerr != json_tokener_success || !cookie->jobj) {
		/* log error */
		RERROR("failed to execute get request or parse returned json object");
		/* free and reset json object */
		if (cookie->jobj) {
			json_object_put(cookie->jobj);
			cookie->jobj = NULL;
		}
	/* check cookie json object */
	} else if (cookie->jobj) {
		/* set doc found */
		docfound = 1;
		/* debugging */
		RDEBUG3("parsed json body from couchbase: %s", json_object_to_json_string(cookie->jobj));
	}

	/* start json document if needed */
	if (docfound != 1) {
		/* debugging */
		RDEBUG("no existing document found - creating new json document");
		/* create new json object */
		cookie->jobj = json_object_new_object();
		/* set 'docType' element for new document */
		json_object_object_add(cookie->jobj, "docType", json_object_new_string(inst->doctype));
		/* default startTimestamp and stopTimestamp to null values */
		json_object_object_add(cookie->jobj, "startTimestamp", NULL);
		json_object_object_add(cookie->jobj, "stopTimestamp", NULL);
	}

	/* status specific replacements for start/stop time */
	switch (status) {
	case PW_STATUS_START:
		/* add start time */
		if ((vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
			/* add to json object */
			json_object_object_add(cookie->jobj, "startTimestamp",
					       mod_value_pair_to_json_object(request, vp));
		}
		break;

	case PW_STATUS_STOP:
		/* add stop time */
		if ((vp = fr_pair_find_by_num(request->packet->vps, PW_EVENT_TIMESTAMP, 0, TAG_ANY)) != NULL) {
			/* add to json object */
			json_object_object_add(cookie->jobj, "stopTimestamp",
					       mod_value_pair_to_json_object(request, vp));
		}
		/* check start timestamp and adjust if needed */
		mod_ensure_start_timestamp(cookie->jobj, request->packet->vps);
		break;

	case PW_STATUS_ALIVE:
		/* check start timestamp and adjust if needed */
		mod_ensure_start_timestamp(cookie->jobj, request->packet->vps);
		break;

	default:
		/* don't doing anything */
		rcode = RLM_MODULE_NOOP;
		/* return */
		goto finish;
	}

	/* loop through pairs and add to json document */
	for (vp = request->packet->vps; vp; vp = vp->next) {
		/* map attribute to element */
		if (mod_attribute_to_element(vp->da->name, inst->map, &element) == 0) {
			/* debug */
			RDEBUG3("mapped attribute %s => %s", vp->da->name, element);
			/* add to json object with mapped name */
			json_object_object_add(cookie->jobj, element, mod_value_pair_to_json_object(request, vp));
		}
	}

	/* copy json string to document and check size */
	if (strlcpy(document, json_object_to_json_string(cookie->jobj), sizeof(document)) >= sizeof(document)) {
		/* this isn't good */
		RERROR("could not write json document - insufficient buffer space");
		/* set return */
		rcode = RLM_MODULE_FAIL;
		/* return */
		goto finish;
	}

	/* debugging */
	RDEBUG3("setting '%s' => '%s'", dockey, document);

	/* store document/key in couchbase */
	cb_error = couchbase_set_key(cb_inst, dockey, document, inst->expire);

	/* check return */
	if (cb_error != LCB_SUCCESS) {
		RERROR("failed to store document (%s): %s (0x%x)", dockey, lcb_strerror(NULL, cb_error), cb_error);
	}

finish:
	/* free and reset json object */
	if (cookie->jobj) {
		json_object_put(cookie->jobj);
		cookie->jobj = NULL;
	}

	/* release our connection handle */
	if (handle) {
		fr_connection_release(inst->pool, handle);
	}

	/* return */
	return rcode;
}