Example #1
0
static int eap_aka_compose(eap_session_t *eap_session)
{
	eap_aka_session_t	*eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
	fr_cursor_t		cursor;
	fr_cursor_t		to_encode;
	VALUE_PAIR		*head = NULL, *vp;
	REQUEST			*request = eap_session->request;
	ssize_t			ret;
	fr_sim_encode_ctx_t	encoder_ctx = {
					.root = fr_dict_root(dict_eap_aka),
					.keys = &eap_aka_session->keys,

					.iv = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
						0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
					.iv_included = false,

					.hmac_md = eap_aka_session->mac_md,
					.eap_packet = eap_session->this_round->request,
					.hmac_extra = NULL,
					.hmac_extra_len = 0
				};

	fr_cursor_init(&cursor, &eap_session->request->reply->vps);
	fr_cursor_init(&to_encode, &head);

	while ((vp = fr_cursor_current(&cursor))) {
		if (!fr_dict_parent_common(encoder_ctx.root, vp->da, true)) {
			fr_cursor_next(&cursor);
			continue;
		}
		vp = fr_cursor_remove(&cursor);

		/*
		 *	Silently discard encrypted attributes until
		 *	the peer should have k_encr.  These can be
		 *	added by policy, and seem to cause
		 *	wpa_supplicant to fail if sent before the challenge.
		 */
		if (!eap_aka_session->allow_encrypted && fr_dict_parent_common(attr_eap_aka_encr_data, vp->da, true)) {
			RWDEBUG("Silently discarding &reply:%s: Encrypted attributes not allowed in this round",
				vp->da->name);
			talloc_free(vp);
			continue;
		}

		fr_cursor_append(&to_encode, vp);
	}

	RDEBUG2("Encoding EAP-AKA attributes");
	log_request_pair_list(L_DBG_LVL_2, request, head, NULL);

	eap_session->this_round->request->type.num = eap_aka_session->type;
	eap_session->this_round->request->id = eap_aka_session->aka_id++ & 0xff;
	eap_session->this_round->set_request_id = true;

	ret = fr_sim_encode(eap_session->request, head, &encoder_ctx);
	fr_cursor_head(&to_encode);
	fr_cursor_free_list(&to_encode);

	if (ret < 0) {
		RPEDEBUG("Failed encoding EAP-AKA data");
		return -1;
	}
	return 0;
}

/** Send an EAP-AKA identity request to the supplicant
 *
 * There are three types of user identities that can be implemented
 * - Permanent identities such as [email protected]
 *   Permanent identities can be identified by the leading zero followed by
 *   by 15 digits (the IMSI number).
 * - Ephemeral identities (pseudonyms).  These are identities assigned for
 *   identity privacy so the user can't be tracked.  These can identities
 *   can either be generated as per the 3GPP 'Security aspects of non-3GPP accesses'
 *   document section 14, where a set of up to 16 encryption keys are used
 *   to reversibly encrypt the IMSI. Alternatively the pseudonym can be completely
 *   randomised and stored in a datastore.
 * - A fast resumption ID which resolves to data used for fast resumption.
 *
 * In order to perform full authentication the original IMSI is required for
 * forwarding to the HLR. In the case where we can't match/decrypt the pseudonym,
 * or can't perform fast resumption, we need to request the full identity from
 * the supplicant.
 *
 * @param[in] eap_session	to continue.
 * @return
 *	- 0 on success.
 *	- <0 on failure.
 */
static int eap_aka_send_identity_request(eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	eap_aka_session_t	*eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);
	VALUE_PAIR		*vp;
	RADIUS_PACKET		*packet;
	fr_cursor_t		cursor;

	RDEBUG2("Sending AKA-Identity (%s)", fr_int2str(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"));
	eap_session->this_round->request->code = FR_EAP_CODE_REQUEST;
	eap_aka_session->allow_encrypted = false;	/* In case this is after failed fast-resumption */

	packet = request->reply;
	fr_cursor_init(&cursor, &packet->vps);

	/*
	 *	Set the subtype to identity request
	 */
	vp = fr_pair_afrom_da(packet, attr_eap_aka_subtype);
	vp->vp_uint16 = FR_EAP_AKA_SUBTYPE_VALUE_AKA_IDENTITY;
	fr_cursor_append(&cursor, vp);

	/*
	 *	Select the right type of identity request attribute
	 */
	switch (eap_aka_session->id_req) {
	case SIM_ANY_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_aka_any_id_req);
		break;

	case SIM_PERMANENT_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_aka_permanent_id_req);
		break;

	case SIM_FULLAUTH_ID_REQ:
		vp = fr_pair_afrom_da(packet, attr_eap_aka_fullauth_id_req);
		break;

	default:
		rad_assert(0);
	}
	vp->vp_bool = true;
	fr_cursor_append(&cursor, vp);

	/*
	 *	Encode the packet
	 */
	if (eap_aka_compose(eap_session) < 0) {
	failure:
		fr_pair_list_free(&packet->vps);
		return -1;
	}

	/*
	 *	Digest the packet contents, updating our checkcode.
	 */
	if (!eap_aka_session->checkcode_state &&
	    fr_sim_crypto_init_checkcode(eap_aka_session, &eap_aka_session->checkcode_state,
	    				 eap_aka_session->checkcode_md) < 0) {
		RPEDEBUG("Failed initialising checkcode");
		goto failure;
	}
	if (fr_sim_crypto_update_checkcode(eap_aka_session->checkcode_state, eap_session->this_round->request) < 0) {
		RPEDEBUG("Failed updating checkcode");
		goto failure;
	}

	return 0;
}
Example #2
0
/*
 * (foo == bar), with nested conditionals.
 */
static int parse_condition(policy_lex_file_t *lexer, policy_item_t **tail)
{
    int rcode, seen_not = FALSE;
    policy_lex_t token, compare;
    char lhs[256], rhs[256];
    policy_condition_t *this;

    token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
    if (token != POLICY_LEX_L_BRACKET) {
        fprintf(stderr, "%s[%d]: Expected '(', got \"%s\"\n",
                lexer->filename, lexer->lineno,
                fr_int2str(rlm_policy_tokens, token, lhs));
        return 0;
    }

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

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

redo:
    token = policy_lex_file(lexer, 0, lhs, sizeof(lhs));
    switch (token) {
    case POLICY_LEX_L_BRACKET:
        if (!policy_lex_push_token(lexer, token)) {
            rlm_policy_free_item((policy_item_t *) this);
            return 0;
        }

        this->compare = POLICY_LEX_L_BRACKET;
        this->child_condition = POLICY_LEX_L_BRACKET;
        rcode = parse_condition(lexer, &(this->child));
        if (!rcode) {
            rlm_policy_free_item((policy_item_t *) this);
            return rcode;
        }
        break;

    case POLICY_LEX_L_NOT:
        if (seen_not) {
            fprintf(stderr, "%s[%d]: Syntax error at \"!!\"\n",
                    lexer->filename, lexer->lineno);
            rlm_policy_free_item((policy_item_t *) this);
            return 0;
        }

        debug_tokens("[NOT] ");

        token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
        if (token != POLICY_LEX_L_BRACKET) {
            seen_not = this->sense = 1;
            goto redo;
        }

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

    case POLICY_LEX_BARE_WORD:
        this->lhs_type = token;
        token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
        if (token == POLICY_LEX_L_BRACKET) {
            debug_tokens("[IF-CALL %s] ", lhs);

            /*
             *	Function call.
             */
            if (rlm_policy_find(lexer->policies, lhs) == NULL) {
                fprintf(stderr, "%s[%d]: Undefined function \"%s\"\n",
                        lexer->filename, lexer->lineno,
                        lhs);
                rlm_policy_free_item((policy_item_t *) this);
                return 0;

            }

            /*
             *	this->lhs set up below, after "check"
             */
            this->lhs_type = POLICY_LEX_FUNCTION;

            /*
             *	Copied from parse_call
             */
            token = policy_lex_file(lexer, 0, NULL, 0);
            if (token != POLICY_LEX_L_BRACKET) {
                fprintf(stderr, "%s[%d]: Expected left bracket, got \"%s\"\n",
                        lexer->filename, lexer->lineno,
                        fr_int2str(rlm_policy_tokens, token, "?"));
                rlm_policy_free_item((policy_item_t *) this);
                return 0;
            }

            token = policy_lex_file(lexer, 0, NULL, 0);
            if (token != POLICY_LEX_R_BRACKET) {
                fprintf(stderr, "%s[%d]: Expected right bracket, got \"%s\"\n",
                        lexer->filename, lexer->lineno,
                        fr_int2str(rlm_policy_tokens, token, "?"));
                rlm_policy_free_item((policy_item_t *) this);
                return 0;
            }
        } /* else it's a comparison? */
        goto check;

    case POLICY_LEX_DOUBLE_QUOTED_STRING:
        this->lhs_type = token;

        /*
         *	Got word.  May just be test for existence.
         */
check:
        token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
        if (token == POLICY_LEX_R_BRACKET) {
            debug_tokens("[TEST %s] ", lhs);
            this->lhs = strdup(lhs);
            this->compare = POLICY_LEX_CMP_TRUE;
            break;
        }

        compare = policy_lex_file(lexer, 0, rhs, sizeof(rhs));
        switch (compare) {
        case POLICY_LEX_CMP_EQUALS:
        case POLICY_LEX_CMP_NOT_EQUALS:
        case POLICY_LEX_RX_EQUALS:
        case POLICY_LEX_RX_NOT_EQUALS:
        case POLICY_LEX_CMP_TRUE:
        case POLICY_LEX_CMP_FALSE:
        case POLICY_LEX_LT:
        case POLICY_LEX_GT:
        case POLICY_LEX_LE:
        case POLICY_LEX_GE:
            break;

        default:
            fprintf(stderr, "%s[%d]: Invalid operator \"%s\"\n",
                    lexer->filename, lexer->lineno,
                    fr_int2str(rlm_policy_tokens, compare, rhs));
            rlm_policy_free_item((policy_item_t *) this);
            return 0;
        }

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

    default:
        fprintf(stderr, "%s[%d]: Unexpected lhs token\n",
                lexer->filename, lexer->lineno);
        rlm_policy_free_item((policy_item_t *) this);
        return 0;
    }

    token = policy_lex_file(lexer, 0, NULL, 0);
    if (token != POLICY_LEX_R_BRACKET) {
        fprintf(stderr, "%s[%d]: Expected ')', got \"%s\"\n",
                lexer->filename, lexer->lineno,
                fr_int2str(rlm_policy_tokens, token, "?"));
        rlm_policy_free_item((policy_item_t *) this);
        return 0;
    }

    /*
     *	After the end of condition, we MAY have && or ||
     */
    token = policy_lex_file(lexer, POLICY_LEX_FLAG_PEEK, NULL, 0);
    if ((token == POLICY_LEX_L_AND) || (token == POLICY_LEX_L_OR)) {
        token = policy_lex_file(lexer, 0, NULL, 0); /* skip over it */
        debug_tokens("[%s] ",
                     fr_int2str(rlm_policy_tokens, token, "?"));
        this->child_condition = token;
        rcode = parse_condition(lexer, &(this->child));
        if (!rcode) {
            rlm_policy_free_item((policy_item_t *) this);
            return 0;
        }
    }

    *tail = (policy_item_t *) this;

    return 1;
}
Example #3
0
/*
 *	Log the message to the logfile. Include the severity and
 *	a time stamp.
 */
int vradlog(log_type_t type, char const *fmt, va_list ap)
{
    unsigned char *p;
    char buffer[10240];	/* The largest config item size, then extra for prefixes and suffixes */
    char *unsan;
    size_t len;
    int colourise = default_log.colourise;

    /*
     *	NOT debugging, and trying to log debug messages.
     *
     *	Throw the message away.
     */
    if (!debug_flag && ((type & L_DBG) != 0)) {
        return 0;
    }

    /*
     *	If we don't want any messages, then
     *	throw them away.
     */
    if (default_log.dst == L_DST_NULL) {
        return 0;
    }

    buffer[0] = '\0';
    len = 0;

    if (colourise) {
        len += strlcpy(buffer + len, fr_int2str(colours, type, ""), sizeof(buffer) - len) ;
        if (len == 0) {
            colourise = false;
        }
    }

    /*
     *	Mark the point where we treat the buffer as unsanitized.
     */
    unsan = buffer + len;

    /*
     *	Don't print timestamps to syslog, it does that for us.
     *	Don't print timestamps and error types for low levels
     *	of debugging.
     *
     *	Print timestamps for non-debugging, and for high levels
     *	of debugging.
     */
    if (default_log.dst != L_DST_SYSLOG) {
        if ((debug_flag != 1) && (debug_flag != 2)) {
            time_t timeval;

            timeval = time(NULL);
            CTIME_R(&timeval, buffer + len, sizeof(buffer) - len - 1);

            len = strlen(buffer);
            len += strlcpy(buffer + len, fr_int2str(levels, type, ": "), sizeof(buffer) - len);
        } else goto add_prefix;
    } else {
add_prefix:
        if (len < sizeof(buffer)) switch (type) {
            case L_DBG_WARN:
                len += strlcpy(buffer + len, "WARNING: ", sizeof(buffer) - len);
                break;

            case L_DBG_ERR:
                len += strlcpy(buffer + len, "ERROR: ", sizeof(buffer) - len);
                break;

            default:
                break;
            }
    }

    if (len < sizeof(buffer)) {
        len += vsnprintf(buffer + len, sizeof(buffer) - len - 1, fmt, ap);
    }

    /*
     *	Filter out control chars and non UTF8 chars
     */
    for (p = (unsigned char *)unsan; *p != '\0'; p++) {
        int clen;

        switch (*p) {
        case '\r':
        case '\n':
            *p = ' ';
            break;

        case '\t':
            continue;

        default:
            clen = fr_utf8_char(p);
            if (!clen) {
                *p = '?';
                continue;
            }
            p += (clen - 1);
            break;
        }
    }

    if (colourise && (len < sizeof(buffer))) {
        len += strlcpy(buffer + len, VTC_RESET, sizeof(buffer) - len);
    }

    if (len < (sizeof(buffer) - 2)) {
        buffer[len]	= '\n';
        buffer[len + 1] = '\0';
    } else {
        buffer[sizeof(buffer) - 2] = '\n';
        buffer[sizeof(buffer) - 1] = '\0';
    }

    switch (default_log.dst) {

#ifdef HAVE_SYSLOG_H
    case L_DST_SYSLOG:
        switch(type) {
        case L_DBG:
        case L_WARN:
        case L_DBG_WARN:
        case L_DBG_ERR:
        case L_DBG_ERR_REQ:
        case L_DBG_WARN_REQ:
            type = LOG_DEBUG;
            break;
        case L_AUTH:
        case L_PROXY:
        case L_ACCT:
            type = LOG_NOTICE;
            break;
        case L_INFO:
            type = LOG_INFO;
            break;
        case L_ERR:
            type = LOG_ERR;
            break;
        }
        syslog(type, "%s", buffer);
        break;
#endif

    case L_DST_FILES:
    case L_DST_STDOUT:
    case L_DST_STDERR:
        return write(default_log.fd, buffer, strlen(buffer));

    default:
    case L_DST_NULL:	/* should have been caught above */
        break;
    }

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

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

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

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

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

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

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

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

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

    *tail = (policy_item_t *) this;

    return 1;
}
Example #5
0
/*
 *	Parse an "include filename" statement
 *
 *	FIXME: Tie this file into the CONF_SECTION for HUP handling!
 */
static int parse_include(policy_lex_file_t *lexer)
{
    char *p;
    policy_lex_t token;
    char filename[1024];
    char buffer[2048];

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

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

#ifdef HAVE_DIRENT_H
        p = strrchr(p + 1, '/');
        if (p && !p[1]) {
            DIR		*dir;
            struct dirent	*dp;

            p++;

            dir = opendir(buffer);
            if (!dir) {
                fprintf(stderr, "%s[%d]: Error opening %s:%s\n",
                        lexer->filename, lexer->lineno,
                        buffer, strerror(errno));
                return 0;
            }

            /*
             *	Read the directory, ignoring "." files.
             */
            while ((dp = readdir(dir)) != NULL) {
                struct stat buf;

                if (dp->d_name[0] == '.') continue;
                if (strchr(dp->d_name, '~') != NULL) continue;

                strlcpy(p, dp->d_name,
                        sizeof(buffer) - (p - buffer));

                if ((stat(buffer, &buf) != 0) ||
                        S_ISDIR(buf.st_mode)) continue;

                debug_tokens("\nincluding file %s\n", buffer);
                if (!rlm_policy_parse(lexer->policies, buffer)) {
                    closedir(dir);
                    return 0;
                }
            }
            closedir(dir);
            return 1;
        } /* else it must have been a normalx file */
#endif
    } else {
        snprintf(buffer, sizeof(buffer), "%s/%s",
                 radius_dir, filename);
    }

    /*
     *	Handle one include file.
     */
    debug_tokens("\nincluding file %s\n", buffer);
    if (!rlm_policy_parse(lexer->policies, buffer)) {
        return 0;
    }

    return 1;
}
/** Synchronously load cookie data
 *
 * FIXME: This should not be synchronous, but integrating it into the event loop
 *	before the server has started processing requests makes my head hurt.
 *
 * @param[in] ctx	to allocate cookie buffer in.
 * @param[out] cookie	Where to write the cookie we loaded.
 * @param[in] listen	structure encapsulating the LDAP
 * @param[in] config	of the sync we're loading the cookie for.
 * @return
 *	- -1 on failure.
 *	- 0 on success.
 *	- 1 no cookie returned.
 */
static int proto_ldap_cookie_load(TALLOC_CTX *ctx, uint8_t **cookie, rad_listen_t *listen, sync_config_t const *config)
{
	proto_ldap_inst_t	*inst = talloc_get_type_abort(listen->data, proto_ldap_inst_t);
	REQUEST			*request;
	CONF_SECTION		*unlang;
	int			ret = 0;

	rlm_rcode_t		rcode;

	request = proto_ldap_request_setup(listen, inst, 0);
	if (!request) return -1;

	proto_ldap_attributes_add(request, config);
	request->packet->code = LDAP_SYNC_CODE_COOKIE_STORE;

	unlang = cf_section_find(request->server_cs, "load", "Cookie");
	if (!unlang) {
		RDEBUG2("Ignoring %s operation.  Add \"load Cookie {}\" to virtual-server \"%s\""
			" to handle", fr_int2str(ldap_sync_code_table, request->packet->code, "<INVALID>"),
			cf_section_name2(request->server_cs));
	}

	*cookie = NULL;

	rcode = unlang_interpret_synchronous(request, unlang, RLM_MODULE_NOOP);
	switch (rcode) {
	case RLM_MODULE_OK:
	case RLM_MODULE_UPDATED:
	{
		VALUE_PAIR *vp;

		vp = fr_pair_find_by_da(request->reply->vps, attr_ldap_sync_cookie, TAG_ANY);
		if (!vp) {
			if (config->allow_refresh) RDEBUG2("No &reply:Cookie attribute found.  All entries matching "
							   "sync configuration will be returned");
			ret = 1;
			goto finish;
		}

		/*
		 *	So the request pool doesn't hang around indefinitely.
		 */
		MEM(*cookie = talloc_memdup(ctx, vp->vp_octets, vp->vp_length));
		ret = 0;
	}
		break;

	case RLM_MODULE_NOOP:
		if (config->allow_refresh) RDEBUG2("Section returned \"noop\".  All entries matching sync "
						   "configuration will be returned");
		ret = 1;
		break;

	default:
		RERROR("Section must return \"ok\", \"updated\", or \"noop\" for listener instantiation to succeed");
		ret = -1;
		break;
	}

finish:
	talloc_free(request);
	return ret;
}
Example #7
0
/*
 *	Simple xlat to read text data from a URL
 */
static ssize_t rest_xlat(void *instance, REQUEST *request,
			 char const *fmt, char *out, size_t freespace)
{
	rlm_rest_t	*inst = instance;
	void		*handle;
	int		hcode;
	int		ret;
	ssize_t		len, outlen = 0;
	char		*uri = NULL;
	char const	*p = fmt, *q;
	char const	*body;
	http_method_t	method;

	/* There are no configurable parameters other than the URI */
	rlm_rest_section_t section = {
		.name = "xlat",
		.method = HTTP_METHOD_GET,
		.body = HTTP_BODY_NONE,
		.body_str = "application/x-www-form-urlencoded",
		.require_auth = false,
		.force_to = HTTP_BODY_PLAIN
	};
	*out = '\0';

	rad_assert(fmt);

	RDEBUG("Expanding URI components");

	handle = fr_connection_get(inst->pool);
	if (!handle) return -1;

	/*
	 *  Extract the method from the start of the format string (if there is one)
	 */
	method = fr_substr2int(http_method_table, p, HTTP_METHOD_UNKNOWN, -1);
	if (method != HTTP_METHOD_UNKNOWN) {
		section.method = method;
		p += strlen(http_method_table[method].name);
	}

	/*
	 *  Trim whitespace
	 */
	while (isspace(*p) && p++);

	/*
	 *  Unescape parts of xlat'd URI, this allows REST servers to be specified by
	 *  request attributes.
	 */
	len = rest_uri_host_unescape(&uri, instance, request, handle, p);
	if (len <= 0) {
		outlen = -1;
		goto finish;
	}

	/*
	 *  Extract freeform body data (url can't contain spaces)
	 */
	q = strchr(p, ' ');
	if (q && (*++q != '\0')) {
		section.body = HTTP_BODY_CUSTOM_LITERAL;
		section.data = q;
	}

	RDEBUG("Sending HTTP %s to \"%s\"", fr_int2str(http_method_table, section.method, NULL), uri);

	/*
	 *  Configure various CURL options, and initialise the read/write
	 *  context data.
	 *
	 *  @todo We could extract the User-Name and password from the URL string.
	 */
	ret = rest_request_config(instance, &section, request, handle, section.method, section.body,
				  uri, NULL, NULL);
	talloc_free(uri);
	if (ret < 0) {
		outlen = -1;
		goto finish;
	}

	/*
	 *  Send the CURL request, pre-parse headers, aggregate incoming
	 *  HTTP body data into a single contiguous buffer.
	 */
	ret = rest_request_perform(instance, &section, request, handle);
	if (ret < 0) {
		outlen = -1;
		goto finish;
	}

	hcode = rest_get_handle_code(handle);
	switch (hcode) {
	case 404:
	case 410:
	case 403:
	case 401:
	{
		outlen = -1;
error:
		rest_response_error(request, handle);
		goto finish;
	}
	case 204:
		goto finish;

	default:
		/*
		 *	Attempt to parse content if there was any.
		 */
		if ((hcode >= 200) && (hcode < 300)) {
			break;
		} else if (hcode < 500) {
			outlen = -2;
			goto error;
		} else {
			outlen = -1;
			goto error;
		}
	}

	len = rest_get_handle_data(&body, handle);
	if ((size_t) len >= freespace) {
		REDEBUG("Insufficient space to write HTTP response, needed %zu bytes, have %zu bytes", len + 1,
			freespace);
		outlen = -1;
		goto finish;
	}
	if (len > 0) {
		outlen = len;
		strlcpy(out, body, len + 1);	/* strlcpy takes the size of the buffer */
	}

finish:
	rlm_rest_cleanup(instance, &section, handle);

	fr_connection_release(inst->pool, handle);

	return outlen;
}

/*
 *	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_authorize(void *instance, REQUEST *request)
{
	rlm_rest_t *inst = instance;
	rlm_rest_section_t *section = &inst->authorize;

	void *handle;
	int hcode;
	int rcode = RLM_MODULE_OK;
	int ret;

	if (!section->name) return RLM_MODULE_NOOP;

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

	ret = rlm_rest_perform(instance, section, handle, request, NULL, NULL);
	if (ret < 0) {
		rcode = RLM_MODULE_FAIL;
		goto finish;
	}

	hcode = rest_get_handle_code(handle);
	switch (hcode) {
	case 404:
	case 410:
		rcode = RLM_MODULE_NOTFOUND;
		break;

	case 403:
		rcode = RLM_MODULE_USERLOCK;
		break;

	case 401:
		/*
		 *	Attempt to parse content if there was any.
		 */
		ret = rest_response_decode(inst, section, request, handle);
		if (ret < 0) {
			rcode = RLM_MODULE_FAIL;
			break;
		}

		rcode = RLM_MODULE_REJECT;
		break;

	case 204:
		rcode = RLM_MODULE_OK;
		break;

	default:
		/*
		 *	Attempt to parse content if there was any.
		 */
		if ((hcode >= 200) && (hcode < 300)) {
			ret = rest_response_decode(inst, section, request, handle);
			if (ret < 0) 	   rcode = RLM_MODULE_FAIL;
			else if (ret == 0) rcode = RLM_MODULE_OK;
			else		   rcode = RLM_MODULE_UPDATED;
			break;
		} else if (hcode < 500) {
			rcode = RLM_MODULE_INVALID;
		} else {
			rcode = RLM_MODULE_FAIL;
		}
	}

finish:
	switch (rcode) {
	case RLM_MODULE_INVALID:
	case RLM_MODULE_FAIL:
	case RLM_MODULE_USERLOCK:
		rest_response_error(request, handle);
		break;

	default:
		break;
	}

	rlm_rest_cleanup(instance, section, handle);

	fr_connection_release(inst->pool, handle);

	return rcode;
}
Example #8
0
/** Convert CONFIG_PAIR (which may contain refs) to value_pair_map_t.
 *
 * Treats the left operand as an attribute reference
 * @verbatim<request>.<list>.<attribute>@endverbatim
 *
 * Treatment of left operand depends on quotation, barewords are treated as
 * attribute references, double quoted values are treated as expandable strings,
 * single quoted values are treated as literal strings.
 *
 * Return must be freed with talloc_free
 *
 * @param[in] ctx for talloc
 * @param[in] cp to convert to map.
 * @param[in] dst_request_def The default request to insert unqualified
 *	attributes into.
 * @param[in] dst_list_def The default list to insert unqualified attributes
 *	into.
 * @param[in] src_request_def The default request to resolve attribute
 *	references in.
 * @param[in] src_list_def The default list to resolve unqualified attributes
 *	in.
 * @return value_pair_map_t if successful or NULL on error.
 */
value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp,
				request_refs_t dst_request_def,
				pair_lists_t dst_list_def,
				request_refs_t src_request_def,
				pair_lists_t src_list_def)
{
	value_pair_map_t *map;
	char const *attr;
	char const *value;
	FR_TOKEN type;
	CONF_ITEM *ci = cf_pairtoitem(cp);

	if (!cp) return NULL;

	map = talloc_zero(ctx, value_pair_map_t);

	attr = cf_pair_attr(cp);
	value = cf_pair_value(cp);
	if (!value) {
		cf_log_err(ci, "Missing attribute value");
		goto error;
	}

	map->dst = radius_attr2tmpl(map, attr, dst_request_def, dst_list_def);
	if (!map->dst) {
		cf_log_err(ci, "Syntax error in attribute definition");
		goto error;
	}

	/*
	 *	Bare words always mean attribute references.
	 */
	type = cf_pair_value_type(cp);
	if (type == T_BARE_WORD) {
		if (*value == '&') {
			map->src = radius_attr2tmpl(map, value + 1, src_request_def, src_list_def);
		} else {
			if (!isdigit((int) *value) &&
			    ((strchr(value, ':') != NULL) ||
			     (dict_attrbyname(value) != NULL))) {
				map->src = radius_attr2tmpl(map, value, src_request_def, src_list_def);
			}
			if (map->src) {
				WDEBUG("%s[%d]: Please add '&' for attribute reference '%s = &%s'",
				       cf_pair_filename(cp), cf_pair_lineno(cp),
				       attr, value);
			} else {
				map->src = radius_str2tmpl(map, value, type);
			}
		}
	} else {
		map->src = radius_str2tmpl(map, value, type);
	}

	if (!map->src) {
		goto error;
	}

	map->op = cf_pair_operator(cp);
	map->ci = ci;

	/*
	 *	Lots of sanity checks for insane people...
	 */

	/*
	 *	We don't support implicit type conversion,
	 *	except for "octets"
	 */
	if (map->dst->da && map->src->da &&
	    (map->src->da->type != map->dst->da->type) &&
	    (map->src->da->type != PW_TYPE_OCTETS) &&
	    (map->dst->da->type != PW_TYPE_OCTETS)) {
		cf_log_err(ci, "Attribute type mismatch");
		goto error;
	}

	/*
	 *	What exactly where you expecting to happen here?
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type == VPT_TYPE_LIST)) {
		cf_log_err(ci, "Can't copy list into an attribute");
		goto error;
	}

	/*
	 *	Can't copy an xlat expansion or literal into a list,
	 *	we don't know what type of attribute we'd need
	 *	to create
	 */
	if ((map->dst->type == VPT_TYPE_LIST) &&
	    ((map->src->type == VPT_TYPE_XLAT) || (map->src->type == VPT_TYPE_LITERAL))) {
		cf_log_err(ci, "Can't copy value into list (we don't know which attribute to create)");
		goto error;
	}

	/*
	 *	Depending on the attribute type, some operators are
	 *	disallowed.
	 */
	if (map->dst->type == VPT_TYPE_ATTR) {
		if ((map->op != T_OP_EQ) &&
		    (map->op != T_OP_CMP_EQ) &&
		    (map->op != T_OP_ADD) &&
		    (map->op != T_OP_SUB) &&
		    (map->op != T_OP_LE) &&
		    (map->op != T_OP_GE) &&
		    (map->op != T_OP_CMP_FALSE) &&
		    (map->op != T_OP_SET)) {
			cf_log_err(ci, "Invalid operator for attribute");
			goto error;
		}
	}

	switch (map->src->type) {
		/*
		 *	Only += and -= operators are supported for list copy.
		 */
		case VPT_TYPE_LIST:
			switch (map->op) {
			case T_OP_SUB:
			case T_OP_ADD:
				break;

			default:
				cf_log_err(ci, "Operator \"%s\" not allowed "
					   "for list copy",
					   fr_int2str(fr_tokens, map->op, "<INVALID>"));
				goto error;
			}
		break;

		default:
			break;
	}

	return map;

error:
	talloc_free(map);
	return NULL;
}
Example #9
0
/**  Print a template to a string
 *
 * @param[out] buffer for the output string
 * @param[in] bufsize of the buffer
 * @param[in] vpt to print
 * @return the size of the string printed
 */
size_t radius_tmpl2str(char *buffer, size_t bufsize, value_pair_tmpl_t const *vpt)
{
	char c;
	char const *p;
	char *q = buffer;
	char *end;

	switch (vpt->type) {
	default:
		return 0;

	case VPT_TYPE_REGEX:
		c = '/';
		break;

	case VPT_TYPE_XLAT:
		c = '"';
		break;

	case VPT_TYPE_LITERAL:	/* single-quoted or bare word */
		/*
		 *	Hack
		 */
		for (p = vpt->name; *p != '\0'; p++) {
			if (*p == ' ') break;
			if (*p == '\'') break;
			if (!dict_attr_allowed_chars[(int) *p]) break;
		}

		if (!*p) {
			strlcpy(buffer, vpt->name, bufsize);
			return strlen(buffer);
		}

		c = '\'';
		break;

	case VPT_TYPE_EXEC:
		c = '`';
		break;

	case VPT_TYPE_ATTR:
		buffer[0] = '&';
		if (vpt->request == REQUEST_CURRENT) {
			if (vpt->list == PAIR_LIST_REQUEST) {
				strlcpy(buffer + 1, vpt->da->name, bufsize - 1);
			} else {
				snprintf(buffer + 1, bufsize - 1, "%s:%s",
					 fr_int2str(pair_lists, vpt->list, ""),
					 vpt->da->name);
			}

		} else {
			snprintf(buffer + 1, bufsize - 1, "%s.%s:%s",
				 fr_int2str(request_refs, vpt->request, ""),
				 fr_int2str(pair_lists, vpt->list, ""),
				 vpt->da->name);
		}
		return strlen(buffer);

	case VPT_TYPE_DATA:
		{
			VALUE_PAIR *vp;
			TALLOC_CTX *ctx;

			memcpy(&ctx, &vpt, sizeof(ctx)); /* hack */

			vp = pairalloc(ctx, vpt->da);
			memcpy(&vp->data, vpt->vpd, sizeof(vp->data));
			vp->length = vpt->length;

			q = vp_aprint(vp, vp);

			if ((vpt->da->type != PW_TYPE_STRING) &&
			    (vpt->da->type != PW_TYPE_DATE)) {
				strlcpy(buffer, q, bufsize);
			} else {
				/*
				 *	FIXME: properly escape the string...
				 */
				snprintf(buffer, bufsize, "\"%s\"", q);
			}

			talloc_free(q);
			pairfree(&vp);
			return strlen(buffer);
		}
	}

	if (bufsize <= 3) {
	no_room:
		*buffer = '\0';
		return 0;
	}

	p = vpt->name;
	*(q++) = c;
	end = buffer + bufsize - 3; /* quotes + EOS */

	while (*p && (q < end)) {
		if (*p == c) {
			if ((q - end) < 4) goto no_room; /* escape, char, quote, EOS */
			*(q++) = '\\';
			*(q++) = *(p++);
			continue;
		}

		switch (*p) {
		case '\\':
			if ((q - end) < 4) goto no_room;
			*(q++) = '\\';
			*(q++) = *(p++);
			break;

		case '\r':
			if ((q - end) < 4) goto no_room;
			*(q++) = '\\';
			*(q++) = 'r';
			p++;
			break;

		case '\n':
			if ((q - end) < 4) goto no_room;
			*(q++) = '\\';
			*(q++) = 'r';
			p++;
			break;

		case '\t':
			if ((q - end) < 4) goto no_room;
			*(q++) = '\\';
			*(q++) = 't';
			p++;
			break;

		default:
			*(q++) = *(p++);
			break;
		}
	}

	*(q++) = c;
	*q = '\0';

	return q - buffer;
}
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] req_list request valuepairs.
 * @param[in] check Check/control valuepairs.
 * @param[in,out] rep_list Reply value pairs.
 *
 * @return 0 on match.
 */
int paircompare(REQUEST *request, VALUE_PAIR *req_list, VALUE_PAIR *check,
		VALUE_PAIR **rep_list)
{
	vp_cursor_t cursor;
	VALUE_PAIR *check_item;
	VALUE_PAIR *auth_item;
	DICT_ATTR 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;
		}

		if (!check_item->da->vendor) switch (check_item->da->attr) {
			/*
			 *	Attributes we skip during comparison.
			 *	These are "server" check items.
			 */
			case PW_CRYPT_PASSWORD:
			case PW_AUTH_TYPE:
			case PW_AUTZ_TYPE:
			case PW_ACCT_TYPE:
			case PW_SESSION_TYPE:
			case PW_STRIP_USER_NAME:
				continue;
				break;

			/*
			 *	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..
			 */
			case PW_USER_PASSWORD:
				if (check_item->op == T_OP_CMP_EQ) {
					WDEBUG("Found User-Password == \"...\".");
					WDEBUG("Are you sure you don't mean Cleartext-Password?");
					WDEBUG("See \"man rlm_pap\" for more information");
				}
				if (pairfind(req_list, PW_USER_PASSWORD, 0, TAG_ANY) == NULL) {
					continue;
				}
				break;
		}

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

		auth_item = req_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.
		 */
		radius_xlat_do(request, check_item);

		/*
		 *	OK it is present now compare them.
		 */
		compare = radius_callback_compare(request, auth_item,
						  check_item, check, rep_list);

		switch (check_item->op) {
			case T_OP_EQ:
			default:
				RWDEBUG("Invalid operator '%s' for item %s: reverting to '=='",
					fr_int2str(fr_tokens, 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_H
			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
/*
 *	Debug print a map / VP
 */
static void debug_map(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR const *vp)
{
	char *value;
	char buffer[1024];

	switch (map->src->type) {
		/*
		 *	Just print the value being assigned
		 */
		default:
		case VPT_TYPE_LITERAL:
			vp_prints_value(buffer, sizeof(buffer), vp, '\'');
			value = buffer;
			break;

		case VPT_TYPE_XLAT:
		case VPT_TYPE_XLAT_STRUCT:
			vp_prints_value(buffer, sizeof(buffer), vp, '"');
			value = buffer;
			break;

		case VPT_TYPE_DATA:
			vp_prints_value(buffer, sizeof(buffer), vp, 0);
			value = buffer;
			break;

			/*
			 *	Just printing the value doesn't make sense, but we still
			 *	want to know what it was...
			 */
		case VPT_TYPE_LIST:
			vp_prints_value(buffer, sizeof(buffer), vp, '\'');
			value = talloc_typed_asprintf(request, "&%s%s -> %s", map->src->name, vp->da->name, buffer);
			break;

		case VPT_TYPE_ATTR:
			vp_prints_value(buffer, sizeof(buffer), vp, '\'');
			value = talloc_typed_asprintf(request, "&%s -> %s", map->src->name, buffer);
			break;
	}

	switch (map->dst->type) {
		case VPT_TYPE_LIST:
			RDEBUG("\t%s%s %s %s", map->dst->name, vp->da->name,
			       fr_int2str(fr_tokens, vp->op, "<INVALID>"), value);
			break;

		case VPT_TYPE_ATTR:
			if (vp->da->type != PW_TYPE_STRING) {
				RDEBUG("\t%s %s %s", map->dst->name,
				       fr_int2str(fr_tokens, vp->op, "<INVALID>"), value);
			} else {
				RDEBUG("\t%s %s '%s'", map->dst->name,
				       fr_int2str(fr_tokens, vp->op, "<INVALID>"), value);
			}
			break;

		default:
			break;
	}

	if (value != buffer) talloc_free(value);
}
Example #12
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(void *instance, REQUEST *request,
			    pair_lists_t list, unsigned int code)
{
	int rcode = RLM_MODULE_NOOP;
	VALUE_PAIR *vp, **vps, *last;
	home_server *home;
	REALM *realm;
	home_pool_t *pool;
	RADIUS_PACKET *packet = NULL;

	instance = instance;	/* -Wunused */
	last = request->config_items;

	/*
	 *	Send as many packets as necessary to different
	 *	destinations.
	 */
	while (1) {
		vp = pairfind(last, PW_REPLICATE_TO_REALM, 0, TAG_ANY);
		if (!vp) break;

		last = vp->next;

		realm = realm_find2(vp->vp_strvalue);
		if (!realm) {
			RDEBUG2("ERROR: Cannot Replicate to unknown realm %s", realm);
			continue;
		}
		
		/*
		 *	We shouldn't really do this on every loop.
		 */
		switch (request->packet->code) {
		default:
			RDEBUG2("ERROR: Cannot replicate unknown packet code %d",
				request->packet->code);
			cleanup(packet);
			return RLM_MODULE_FAIL;
		
		case PW_AUTHENTICATION_REQUEST:
			pool = realm->auth_pool;
			break;
			
#ifdef WITH_ACCOUNTING
			
		case PW_ACCOUNTING_REQUEST:
			pool = realm->acct_pool;
			break;
#endif
			
#ifdef WITH_COA
		case PW_COA_REQUEST:
		case PW_DISCONNECT_REQUEST:
			pool = realm->acct_pool;
			break;
#endif
		}
		
		if (!pool) {
			RDEBUG2(" WARNING: Cancelling replication to Realm %s, as the realm is local.", realm->name);
			continue;
		}
		
		home = home_server_ldb(realm->name, pool, request);
		if (!home) {
			RDEBUG2("ERROR: 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(1);
			if (!packet) return RLM_MODULE_FAIL;
			packet->sockfd = -1;
			packet->code = code;
			packet->id = fr_rand() & 0xff;

			packet->sockfd = fr_socket(&home->src_ipaddr, 0);
			if (packet->sockfd < 0) {
				RDEBUG("ERROR: Failed opening socket: %s", fr_strerror());
				rcode = RLM_MODULE_FAIL;
				goto done;
			}
			
			vps = radius_list(request, list);
			if (!vps) {
				RDEBUG("WARNING: List '%s' doesn't exist for "
				       "this packet", fr_int2str(pair_lists,
				       list, "¿unknown?"));
				rcode = RLM_MODULE_INVALID;
				goto done;
			}
			
			/*
			 *	Don't assume the list actually contains any
			 *	attributes.
			 */
			if (*vps) {
				packet->vps = paircopy(*vps);
				if (!packet->vps) {
					RDEBUG("ERROR: Out of memory!");
					rcode = RLM_MODULE_FAIL;
					goto done;
				}
			}
			


			/*
			 *	For CHAP, create the CHAP-Challenge if
			 *	it doesn't exist.
			 */
			if ((code == PW_AUTHENTICATION_REQUEST) &&
			    (pairfind(request->packet->vps, PW_CHAP_PASSWORD, 0, TAG_ANY) != NULL) &&
			    (pairfind(request->packet->vps, PW_CHAP_CHALLENGE, 0, TAG_ANY) == NULL)) {
				vp = radius_paircreate(request, &packet->vps,
						       PW_CHAP_CHALLENGE, 0,
						       PW_TYPE_OCTETS);
				vp->length = AUTH_VECTOR_LEN;
				memcpy(vp->vp_strvalue, 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++;
			free(packet->data);
			packet->data = NULL;
			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, "¿unknown?"),realm->name);
		if (rad_send(packet, NULL, home->secret) < 0) {
			RDEBUG("ERROR: 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 #13
0
/** Query the LDAP directory to check if a group object includes a user object as a member
 *
 * @param[in] inst rlm_ldap configuration.
 * @param[in] request Current request.
 * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect.
 * @param[in] check vp containing the group value (name or dn).
 * @return One of the RLM_MODULE_* values.
 */
rlm_rcode_t rlm_ldap_check_groupobj_dynamic(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn,
					    VALUE_PAIR *check)

{
	ldap_rcode_t	status;

	char		base_dn[LDAP_MAX_DN_STR_LEN + 1];
	char 		filter[LDAP_MAX_FILTER_STR_LEN + 1];
	char const	*dn = base_dn;

	int		ret;

	rad_assert(inst->groupobj_base_dn);

	switch (check->op) {
	case T_OP_CMP_EQ:
	case T_OP_CMP_FALSE:
	case T_OP_CMP_TRUE:
	case T_OP_REG_EQ:
	case T_OP_REG_NE:
		break;

	default:
		REDEBUG("Operator \"%s\" not allowed for LDAP group comparisons",
			fr_int2str(fr_tokens, check->op, "<INVALID>"));
		return 1;
	}

	RDEBUG2("Checking for user in group objects");

	if (rlm_ldap_is_dn(check->vp_strvalue, check->vp_length)) {
		char const *filters[] = { inst->groupobj_filter, inst->groupobj_membership_filter };

		RINDENT();
		ret = rlm_ldap_xlat_filter(request,
					   filters, sizeof(filters) / sizeof(*filters),
					   filter, sizeof(filter));
		REXDENT();

		if (ret < 0) return RLM_MODULE_INVALID;

		dn = check->vp_strvalue;
	} else {
		char name_filter[LDAP_MAX_FILTER_STR_LEN];
		char const *filters[] = { name_filter, inst->groupobj_filter, inst->groupobj_membership_filter };

		if (!inst->groupobj_name_attr) {
			REDEBUG("Told to search for group by name, but missing 'group.name_attribute' "
				"directive");

			return RLM_MODULE_INVALID;
		}

		snprintf(name_filter, sizeof(name_filter), "(%s=%s)", inst->groupobj_name_attr, check->vp_strvalue);
		RINDENT();
		ret = rlm_ldap_xlat_filter(request,
					   filters, sizeof(filters) / sizeof(*filters),
					   filter, sizeof(filter));
		REXDENT();
		if (ret < 0) return RLM_MODULE_INVALID;


		/*
		 *	rlm_ldap_find_user does this, too.  Oh well.
		 */
		RINDENT();
		ret = radius_xlat(base_dn, sizeof(base_dn), request, inst->groupobj_base_dn,
				  rlm_ldap_escape_func, NULL);
		REXDENT();
		if (ret < 0) {
			REDEBUG("Failed creating base_dn");

			return RLM_MODULE_INVALID;
		}
	}

	RINDENT();
	status = rlm_ldap_search(inst, request, pconn, dn, inst->groupobj_scope, filter, NULL, NULL);
	REXDENT();
	switch (status) {
	case LDAP_PROC_SUCCESS:
		RDEBUG("User found in group object \"%s\"", dn);
		break;

	case LDAP_PROC_NO_RESULT:
		return RLM_MODULE_NOTFOUND;

	default:
		return RLM_MODULE_FAIL;
	}

	return RLM_MODULE_OK;
}
Example #14
0
/** Process the Peer's response and advantage the state machine
 *
 */
static rlm_rcode_t mod_process(UNUSED void *instance, eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	eap_aka_session_t	*eap_aka_session = talloc_get_type_abort(eap_session->opaque, eap_aka_session_t);

	fr_sim_decode_ctx_t	ctx = {
					.keys = &eap_aka_session->keys,
				};
	VALUE_PAIR		*vp, *vps, *subtype_vp;
	fr_cursor_t		cursor;

	eap_aka_subtype_t	subtype;

	int			ret;

	/*
	 *	RFC 4187 says we ignore the contents of the
	 *	next packet after we send our success notification
	 *	and always send a success.
	 */
	if (eap_aka_session->state == EAP_AKA_SERVER_SUCCESS_NOTIFICATION) {
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_SUCCESS);
		return RLM_MODULE_HANDLED;
	}

	/* vps is the data from the client */
	vps = request->packet->vps;

	fr_cursor_init(&cursor, &request->packet->vps);
	fr_cursor_tail(&cursor);

	ret = fr_sim_decode(eap_session->request,
			    &cursor,
			    dict_eap_aka,
			    eap_session->this_round->response->type.data,
			    eap_session->this_round->response->type.length,
			    &ctx);
	/*
	 *	RFC 4187 says we *MUST* notify, not just
	 *	send an EAP-Failure in this case where
	 *	we cannot decode an EAP-AKA packet.
	 */
	if (ret < 0) {
		RPEDEBUG2("Failed decoding EAP-AKA attributes");
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;	/* We need to process more packets */
	}

	vp = fr_cursor_current(&cursor);
	if (vp && RDEBUG_ENABLED2) {
		RDEBUG2("EAP-AKA decoded attributes");
		log_request_pair_list(L_DBG_LVL_2, request, vp, NULL);
	}

	subtype_vp = fr_pair_find_by_da(vps, attr_eap_aka_subtype, TAG_ANY);
	if (!subtype_vp) {
		REDEBUG("Missing EAP-AKA-Subtype");
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;				/* We need to process more packets */
	}
	subtype = subtype_vp->vp_uint16;

	switch (eap_aka_session->state) {
	/*
	 *	Here we expected the peer to send
	 *	us identities for validation.
	 */
	case EAP_AKA_SERVER_IDENTITY:
		switch (subtype) {
		case EAP_AKA_IDENTITY:
			if (process_eap_aka_identity(eap_session, vps) == 0) return RLM_MODULE_HANDLED;
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;	/* We need to process more packets */

		/*
		 *	Case 1 where we're allowed to send an EAP-Failure
		 *
		 *	This can happen in the case of a conservative
		 *	peer, where it refuses to provide the permanent
		 *	identity.
		 */
		case EAP_AKA_CLIENT_ERROR:
		{
			char buff[20];

			vp = fr_pair_find_by_da(vps, attr_eap_aka_client_error_code, TAG_ANY);
			if (!vp) {
				REDEBUG("EAP-AKA Peer rejected AKA-Identity (%s) with client-error message but "
					"has not supplied a client error code",
					fr_int2str(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"));
			} else {
				REDEBUG("Client rejected AKA-Identity (%s) with error: %s (%i)",
					fr_int2str(sim_id_request_table, eap_aka_session->id_req, "<INVALID>"),
					fr_pair_value_enum(vp, buff), vp->vp_uint16);
			}
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;
		}

		case EAP_AKA_NOTIFICATION:
		notification:
		{
			char buff[20];

			vp = fr_pair_afrom_da(vps, attr_eap_aka_notification);
			if (!vp) {
				REDEBUG2("Received AKA-Notification with no notification code");
				eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
				return RLM_MODULE_HANDLED;			/* We need to process more packets */
			}

			/*
			 *	Case 3 where we're allowed to send an EAP-Failure
			 */
			if (!(vp->vp_uint16 & 0x8000)) {
				REDEBUG2("AKA-Notification %s (%i) indicates failure", fr_pair_value_enum(vp, buff),
					 vp->vp_uint16);
				eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
				return RLM_MODULE_REJECT;
			}

			/*
			 *	...if it's not a failure, then re-enter the
			 *	current state.
			 */
			REDEBUG2("Got AKA-Notification %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16);
			eap_aka_state_enter(eap_session, eap_aka_session->state);
			return RLM_MODULE_HANDLED;
		}

		default:
		unexpected_subtype:
			/*
			 *	RFC 4187 says we *MUST* notify, not just
			 *	send an EAP-Failure in this case.
			 */
			REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;				/* We need to process more packets */
		}

	/*
	 *	Process the response to our previous challenge.
	 */
	case EAP_AKA_SERVER_CHALLENGE:
		switch (subtype) {
		case EAP_AKA_CHALLENGE:
			switch (process_eap_aka_challenge(eap_session, vps)) {
			case 1:
				return RLM_MODULE_HANDLED;

			case 0:
				return RLM_MODULE_OK;

			case -1:
				eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
				return RLM_MODULE_HANDLED;			/* We need to process more packets */
			}


		case EAP_AKA_SYNCHRONIZATION_FAILURE:
			REDEBUG("EAP-AKA Peer synchronization failure");	/* We can't handle these yet */
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;				/* We need to process more packets */

		/*
		 *	Case 1 where we're allowed to send an EAP-Failure
		 */
		case EAP_AKA_CLIENT_ERROR:
		{
			char buff[20];

			vp = fr_pair_find_by_da(vps, attr_eap_aka_client_error_code, TAG_ANY);
			if (!vp) {
				REDEBUG("EAP-AKA Peer rejected AKA-Challenge with client-error message but "
					"has not supplied a client error code");
			} else {
				REDEBUG("Client rejected AKA-Challenge with error: %s (%i)",
					fr_pair_value_enum(vp, buff), vp->vp_uint16);
			}
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;
		}

		/*
		 *	Case 2 where we're allowed to send an EAP-Failure
		 */
		case EAP_AKA_AUTHENTICATION_REJECT:
			REDEBUG("EAP-AKA Peer Rejected AUTN");
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;

		case EAP_AKA_NOTIFICATION:
			goto notification;

		default:
			goto unexpected_subtype;
		}

	/*
	 *	Peer acked our failure
	 */
	case EAP_AKA_SERVER_FAILURE_NOTIFICATION:
		switch (subtype) {
		case EAP_AKA_NOTIFICATION:
			RDEBUG2("AKA-Notification ACKed, sending EAP-Failure");
			eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE);
			return RLM_MODULE_REJECT;

		default:
			goto unexpected_subtype;
		}

	/*
	 *	Something bad happened...
	 */
	default:
		rad_assert(0);
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;				/* We need to process more packets */
	}
}

/** Initiate the EAP-SIM session by starting the state machine
 *
 */
static rlm_rcode_t mod_session_init(void *instance, eap_session_t *eap_session)
{
	REQUEST				*request = eap_session->request;
	eap_aka_session_t		*eap_aka_session;
	rlm_eap_aka_t			*inst = instance;
	fr_sim_id_type_t		type;
	fr_sim_method_hint_t		method;

	MEM(eap_aka_session = talloc_zero(eap_session, eap_aka_session_t));

	eap_session->opaque = eap_aka_session;

	/*
	 *	Set default configuration, we may allow these
	 *	to be toggled by attributes later.
	 */
	eap_aka_session->request_identity = inst->request_identity;
	eap_aka_session->send_result_ind = inst->protected_success;
	eap_aka_session->id_req = SIM_NO_ID_REQ;	/* Set the default */

	/*
	 *	This value doesn't have be strong, but it is
	 *	good if it is different now and then.
	 */
	eap_aka_session->aka_id = (fr_rand() & 0xff);

	/*
	 *	Process the identity that we received in the
	 *	EAP-Identity-Response and use it to determine
	 *	the initial request we send to the Supplicant.
	 */
	if (fr_sim_id_type(&type, &method,
			   eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
		RPWDEBUG2("Failed parsing identity, continuing anyway");
	}

	/*
	 *	Unless AKA-Prime is explicitly disabled,
	 *	use it... It has stronger keying, and
	 *	binds authentication to the network.
	 */
	switch (eap_session->type) {
	case FR_EAP_AKA_PRIME:
	default:
		RDEBUG2("New EAP-AKA' session");
		eap_aka_session->type = FR_EAP_AKA_PRIME;
		eap_aka_session->kdf = FR_EAP_AKA_KDF_VALUE_EAP_AKA_PRIME_WITH_CK_PRIME_IK_PRIME;
		eap_aka_session->checkcode_md = eap_aka_session->mac_md = EVP_sha256();
		eap_aka_session->keys.network = (uint8_t *)talloc_bstrndup(eap_aka_session, inst->network_name,
									   talloc_array_length(inst->network_name) - 1);
		eap_aka_session->keys.network_len = talloc_array_length(eap_aka_session->keys.network) - 1;
		switch (method) {
		default:
			RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're "
				"attempting EAP-AKA'", fr_int2str(sim_id_method_hint_table, method, "<INVALID>"));
			break;

		case SIM_METHOD_HINT_AKA_PRIME:
		case SIM_METHOD_HINT_UNKNOWN:
			break;
		}
		break;

	case FR_EAP_AKA:
		RDEBUG2("New EAP-AKA session");
		eap_aka_session->type = FR_EAP_AKA;
		eap_aka_session->kdf = FR_EAP_AKA_KDF_VALUE_EAP_AKA;	/* Not actually sent */
		eap_aka_session->checkcode_md = eap_aka_session->mac_md = EVP_sha1();
		eap_aka_session->send_at_bidding = true;
		switch (method) {
		default:
			RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're "
				"attempting EAP-AKA", fr_int2str(sim_id_method_hint_table, method, "<INVALID>"));
			break;

		case SIM_METHOD_HINT_AKA:
		case SIM_METHOD_HINT_UNKNOWN:
			break;
		}
		break;
	}
	eap_session->process = mod_process;

	/*
	 *	Admin wants us to always request an identity
	 *	initially.  The RFC says this is also the
	 *	better way to operate, as the supplicant
	 *	can 'decorate' the identity in the identity
	 *	response.
	 */
	if (inst->request_identity) {
	request_id:
		/*
		 *	We always start by requesting
		 *	any ID initially as we can
		 *	always negotiate down.
		 */
		eap_aka_session->id_req = SIM_ANY_ID_REQ;
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_IDENTITY);
		return RLM_MODULE_HANDLED;
	}
	/*
	 *	Figure out what type of identity we have
	 *	and use it to determine the initial
	 *	request we send.
	 */
	switch (type) {
	/*
	 *	If there's no valid tag on the identity
	 *	then it's probably been decorated by the
	 *	supplicant.
	 *
	 *	Request the unmolested identity
	 */
	case SIM_ID_TYPE_UNKNOWN:
		RWDEBUG("Identity format unknown, sending Identity request");
		goto request_id;

	/*
	 *	These types need to be transformed into something
	 *	usable before we can do anything.
	 */
	case SIM_ID_TYPE_PSEUDONYM:
	case SIM_ID_TYPE_FASTAUTH:

	/*
	 *	Permanent ID means we can just send the challenge
	 */
	case SIM_ID_TYPE_PERMANENT:
		eap_aka_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1;
		MEM(eap_aka_session->keys.identity = talloc_memdup(eap_aka_session, eap_session->identity,
								   eap_aka_session->keys.identity_len));
		eap_aka_state_enter(eap_session, EAP_AKA_SERVER_CHALLENGE);
		return RLM_MODULE_HANDLED;
	}

	return RLM_MODULE_HANDLED;
}
Example #15
0
/** Print out attribute info
 *
 * Prints out all instances of a current attribute, or all attributes in a list.
 *
 * At higher debugging levels, also prints out alternative decodings of the same
 * value. This is helpful to determine types for unknown attributes of long
 * passed vendors, or just crazy/broken NAS.
 *
 * It's also useful for exposing issues in the packet decoding functions, as in
 * some cases they get fed random garbage data.
 *
 * This expands to a zero length string.
 */
static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt,
			       char *out, UNUSED size_t outlen)
{
	VALUE_PAIR *vp, **vps;
	REQUEST *current;
	value_pair_tmpl_t vpt;
	vp_cursor_t cursor;
	char buffer[1024];

	if (!RDEBUG_ENABLED2) {
		*out = '\0';
		return -1;
	}

	while (isspace((int) *fmt)) fmt++;
	if (*fmt == '&') fmt++;

	if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) {
		return -1;
	}

	current = request;
	if (radius_request(&current, vpt.request) < 0) return -2;

	vps = radius_list(current, vpt.list);
	if (!vps) {
		return -2;
	}

	RIDEBUG("Attributes matching \"%s\"", fmt);
	vp = fr_cursor_init(&cursor, vps);

	if (vpt.da) {
		vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
	}
	while (vp) {
		DICT_ATTR *dac = NULL;
		DICT_VENDOR *dv;
		VALUE_PAIR *vpc = NULL;
		FR_NAME_NUMBER const *type;

		vp_prints_value(buffer, sizeof(buffer), vp, '\'');

		if (vp->da->flags.has_tag) {
			RIDEBUG2("\t%s:%s:%i %s %s",
				fr_int2str(pair_lists, vpt.list, "<INVALID>"),
				vp->da->name,
				vp->tag,
				fr_int2str(fr_tokens, vp->op, "<INVALID>"),
				buffer);
		} else {
			RIDEBUG2("\t%s:%s %s %s",
				fr_int2str(pair_lists, vpt.list, "<INVALID>"),
				vp->da->name,
				fr_int2str(fr_tokens, vp->op, "<INVALID>"),
				buffer);
		}

		if (!RDEBUG_ENABLED3) {
			goto next_vp;
		}

		if (vp->da->vendor) {
			dv = dict_vendorbyvalue(vp->da->vendor);
			RDEBUG3("\t\tvendor        : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown");
		}
		RDEBUG3("\t\ttype          : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>"));
		RDEBUG3("\t\tlength        : %zu", vp->length);

		dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR));
		if (!dac) {
			return -1;
		}
		dac->flags.vp_free = 0;

		if (!RDEBUG_ENABLED4) {
			goto next_vp;
		}

		type = dict_attr_types;
		while (type->name) {
			int pad;
			ssize_t len;
			uint8_t const *data = NULL;
			vpc = NULL;

			if ((PW_TYPE) type->number == vp->da->type) {
				goto next_type;
			}

			switch (type->number) {
				case PW_TYPE_INVALID:		/* Not real type */
				case PW_TYPE_MAX:		/* Not real type */
				case PW_TYPE_EXTENDED:		/* Not safe/appropriate */
				case PW_TYPE_LONG_EXTENDED:	/* Not safe/appropriate */
				case PW_TYPE_TLV:		/* Not safe/appropriate */
				case PW_TYPE_VSA:		/* @fixme We need special behaviour for these */
					goto next_type;
				default:
					break;
			}

			dac->type = type->number;
			len = rad_vp2data(&data, vp);
			if (len < 0) {
				goto next_type;
			}
			if (data2vp(NULL, NULL, NULL, dac, data, len, len, &vpc) < 0) {
				goto next_type;
			}

			/*
			 *	data2vp has knowledge of expected format lengths, if the length
			 *	from rad_vp2data doesn't match, it encodes the attribute
			 *	as raw octets. This results in many useless debug lines with
			 *	the same hex string.
			 */
			if ((type->number != PW_TYPE_OCTETS) && (vpc->da->type == PW_TYPE_OCTETS)) {
				goto next_type;
			}

			if (!vp_prints_value(buffer, sizeof(buffer), vpc, '\'')) {
				goto next_type;
			}

			if ((pad = (11 - strlen(type->name))) < 0) {
				pad = 0;
			}

			/*
			 *	@fixme: if the value happens to decode as a VSA
			 *	(someone put a VSA into a VSA?), we probably to print
			 *	extended info for that/reparse
			 */
			RDEBUG4("\t\tas %s%*s: %s", type->name, pad, " ", buffer);

			next_type:
			talloc_free(vpc);
			type++;
		}
		next_vp:

		talloc_free(dac);

		if (vpt.da) {
			vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY);
		} else {
			vp = fr_cursor_next(&cursor);
		}
	}

	*out = '\0';
	return 0;
}
Example #16
0
/** Run the server state machine
 *
 */
static void eap_sim_state_enter(eap_session_t *eap_session, eap_sim_server_state_t new_state)
{
	REQUEST			*request = eap_session->request;
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);

	if (new_state != eap_sim_session->state) {
		RDEBUG2("Changed state %s -> %s",
			fr_int2str(sim_state_table, eap_sim_session->state, "<unknown>"),
			fr_int2str(sim_state_table, new_state, "<unknown>"));
		eap_sim_session->state = new_state;
	} else {
		RDEBUG2("Reentering state %s",
			fr_int2str(sim_state_table, eap_sim_session->state, "<unknown>"));
	}

	switch (new_state) {
	/*
	 *	Send our version list
	 */
	case EAP_SIM_SERVER_START:
	start:
		if (eap_sim_send_start(eap_session) < 0) {
		notify_failure:
			eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
			return;
		}
		break;

	/*
	 *	Send the EAP-SIM Challenge message.
	 */
	case EAP_SIM_SERVER_CHALLENGE:
		if (eap_sim_send_challenge(eap_session) < 0) goto notify_failure;
		break;

	case EAP_SIM_SERVER_REAUTHENTICATE:
		if (eap_sim_send_reauthentication(eap_session) < 0) goto start;
		break;

	/*
	 *	Sent a protected success notification
	 */
	case EAP_SIM_SERVER_SUCCESS_NOTIFICATION:
		if (eap_sim_send_eap_success_notification(eap_session) < 0) goto notify_failure;
		break;

	/*
	 *	Send the EAP Success message (we're done)
	 */
	case EAP_SIM_SERVER_SUCCESS:
		if (eap_sim_send_eap_success(eap_session) < 0) goto notify_failure;
		return;

	/*
	 *	Send a general failure notification
	 */
	case EAP_SIM_SERVER_FAILURE_NOTIFICATION:
		if (eap_sim_send_eap_failure_notification(eap_session) < 0) {	/* Fallback to EAP-Failure */
			eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
		}
		return;

	/*
	 *	Send an EAP-Failure (we're done)
	 */
	case EAP_SIM_SERVER_FAILURE:
		eap_sim_send_eap_failure(eap_session);
		return;

	default:
		rad_assert(0);	/* Invalid transition */
		eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
		return;
	}
}
/** Very simple state machine to process requests
 *
 * Unlike normal protocol requests which may have multiple distinct states,
 * we really only have REQUEST_INIT and REQUEST_RECV phases.
 *
 * Conversion of LDAPMessage to VALUE_PAIR structs is done in the listener
 * because we cannot easily duplicate the LDAPMessage to send it across to
 * the worker for parsing.
 *
 * Most LDAP directories can only handle between 2000-5000 modifications a second
 * so we're unlikely to be I/O or CPU bound using this division of responsibilities.
 *
 * @param[in] request	to process.
 * @param[in] action	If something has signalled that the request should stop
 *			being processed.
 */
static void request_running(REQUEST *request, fr_state_signal_t action)
{
	CONF_SECTION	*unlang;
	char const	*verb;
	char const	*state;
	rlm_rcode_t	rcode = RLM_MODULE_FAIL;

	REQUEST_VERIFY(request);

	/*
	 *	Async (in the same thread, tho) signal to be done.
	 */
	if (action == FR_SIGNAL_CANCEL) goto done;

	/*
	 *	We ignore all other actions.
	 */
	if (action != FR_SIGNAL_RUN) return;

	switch (request->request_state) {
	case REQUEST_INIT:
		if (RDEBUG_ENABLED) proto_ldap_packet_debug(request, request->packet, true);
		log_request_proto_pair_list(L_DBG_LVL_1, request, request->packet->vps, "");

		request->server_cs = request->listener->server_cs;
		request->component = "ldap";

		switch (request->packet->code) {
		case LDAP_SYNC_CODE_PRESENT:
			verb = "recv";
			state = "Present";
			break;

		case LDAP_SYNC_CODE_ADD:
			verb = "recv";
			state = "Add";
			break;

		case LDAP_SYNC_CODE_MODIFY:
			verb = "recv";
			state = "Modify";
			break;

		case LDAP_SYNC_CODE_DELETE:
			verb = "recv";
			state = "Delete";
			break;

		case LDAP_SYNC_CODE_COOKIE_LOAD:
			verb = "load";
			state = "Cookie";
			break;

		case LDAP_SYNC_CODE_COOKIE_STORE:
			verb = "store";
			state = "Cookie";
			break;

		default:
			rad_assert(0);
			return;
		}
		unlang = cf_section_find(request->server_cs, verb, state);
		if (!unlang) unlang = cf_section_find(request->server_cs, "recv", "*");
		if (!unlang) {
			RDEBUG2("Ignoring %s operation.  Add \"%s %s {}\" to virtual-server \"%s\""
				" to handle", fr_int2str(ldap_sync_code_table, request->packet->code, "<INVALID>"),
				verb, state, cf_section_name2(request->server_cs));
			rcode = RLM_MODULE_NOOP;
			goto done;
		}

		RDEBUG("Running '%s %s' from file %s", cf_section_name1(unlang),
		       cf_section_name2(unlang), cf_filename(unlang));
		unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME);

		request->request_state = REQUEST_RECV;
		/* FALL-THROUGH */

	case REQUEST_RECV:
		rcode = unlang_interpret_resume(request);

		if (request->master_state == REQUEST_STOP_PROCESSING) goto done;

		if (rcode == RLM_MODULE_YIELD) return;

		/* FALL-THROUGH */
	default:
	done:
		switch (rcode) {
		case RLM_MODULE_UPDATED:
		case RLM_MODULE_OK:
		{

		}

		default:
			break;
		}
		rad_assert(request->log.unlang_indent == 0);
		//request_delete(request);
		break;
	}
}
Example #18
0
/** Authenticate a previously sent challenge
 *
 */
static rlm_rcode_t mod_process(UNUSED void *instance, eap_session_t *eap_session)
{
	REQUEST			*request = eap_session->request;
	eap_sim_session_t	*eap_sim_session = talloc_get_type_abort(eap_session->opaque, eap_sim_session_t);
	fr_sim_decode_ctx_t	ctx = {
					.keys = &eap_sim_session->keys,
				};
	VALUE_PAIR		*subtype_vp, *from_peer, *vp;
	fr_cursor_t		cursor;

	eap_sim_subtype_t	subtype;

	int			ret;

	/*
	 *	VPS is the data from the client
	 */
	from_peer = eap_session->request->packet->vps;

	fr_cursor_init(&cursor, &request->packet->vps);
	fr_cursor_tail(&cursor);

	ret = fr_sim_decode(eap_session->request,
			    &cursor,
			    dict_eap_sim,
			    eap_session->this_round->response->type.data,
			    eap_session->this_round->response->type.length,
			    &ctx);
	/*
	 *	RFC 4186 says we *MUST* notify, not just
	 *	send an EAP-Failure in this case where
	 *	we cannot decode an EAP-AKA packet.
	 */
	if (ret < 0) {
		RPEDEBUG2("Failed decoding EAP-SIM attributes");
		eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;	/* We need to process more packets */
	}

	vp = fr_cursor_current(&cursor);
	if (vp && RDEBUG_ENABLED2) {
		RDEBUG2("Decoded EAP-SIM attributes");
		log_request_pair_list(L_DBG_LVL_2, request, vp, NULL);
	}

	subtype_vp = fr_pair_find_by_da(from_peer, attr_eap_sim_subtype, TAG_ANY);
	if (!subtype_vp) {
		REDEBUG("Missing EAP-SIM-Subtype");
		eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;				/* We need to process more packets */
	}
	subtype = subtype_vp->vp_uint16;

	switch (eap_sim_session->state) {
	/*
	 *	Response to our advertised versions and request for an ID
	 *	This is very similar to Identity negotiation in EAP-AKA[']
	 */
	case EAP_SIM_SERVER_START:
		switch (subtype) {
		case EAP_SIM_START:
			if (process_eap_sim_start(eap_session, from_peer) == 0) return RLM_MODULE_HANDLED;
			eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;	/* We need to process more packets */

		/*
		 *	Case 1 where we're allowed to send an EAP-Failure
		 *
		 *	This can happen in the case of a conservative
		 *	peer, where it refuses to provide the permanent
		 *	identity.
		 */
		case EAP_SIM_CLIENT_ERROR:
		{
			char buff[20];

			vp = fr_pair_find_by_da(from_peer, attr_eap_sim_client_error_code, TAG_ANY);
			if (!vp) {
				REDEBUG("EAP-SIM Peer rejected SIM-Start (%s) with client-error message but "
					"has not supplied a client error code",
					fr_int2str(sim_id_request_table, eap_sim_session->id_req, "<INVALID>"));
			} else {
				REDEBUG("Client rejected SIM-Start (%s) with error: %s (%i)",
					fr_int2str(sim_id_request_table, eap_sim_session->id_req, "<INVALID>"),
					fr_pair_value_enum(vp, buff), vp->vp_uint16);
			}
			eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
			return RLM_MODULE_REJECT;
		}

		case EAP_SIM_NOTIFICATION:
		notification:
		{
			char buff[20];

			vp = fr_pair_afrom_da(from_peer, attr_eap_sim_notification);
			if (!vp) {
				REDEBUG2("Received SIM-Notification with no notification code");
				eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
				return RLM_MODULE_HANDLED;			/* We need to process more packets */
			}

			/*
			 *	Case 2 where we're allowed to send an EAP-Failure
			 */
			if (!(vp->vp_uint16 & 0x8000)) {
				REDEBUG2("SIM-Notification %s (%i) indicates failure", fr_pair_value_enum(vp, buff),
					 vp->vp_uint16);
				eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
				return RLM_MODULE_REJECT;
			}

			/*
			 *	...if it's not a failure, then re-enter the
			 *	current state.
			 */
			REDEBUG2("Got SIM-Notification %s (%i)", fr_pair_value_enum(vp, buff), vp->vp_uint16);
			eap_sim_state_enter(eap_session, eap_sim_session->state);
			return RLM_MODULE_HANDLED;

		default:
		unexpected_subtype:
			/*
			 *	RFC 4186 says we *MUST* notify, not just
			 *	send an EAP-Failure in this case.
			 */
			REDEBUG("Unexpected subtype %pV", &subtype_vp->data);
			eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
			return RLM_MODULE_HANDLED;				/* We need to process more packets */
		}
		}

	/*
	 *	Process the response to our previous challenge.
	 */
	case EAP_SIM_SERVER_CHALLENGE:
		switch (subtype) {
		/*
		 *	A response to our EAP-Sim/Request/Challenge!
		 */
		case EAP_SIM_CHALLENGE:
			switch (process_eap_sim_challenge(eap_session, from_peer)) {
			case 1:
				return RLM_MODULE_HANDLED;

			case 0:
				return RLM_MODULE_OK;

			case -1:
				eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
				return RLM_MODULE_HANDLED;			/* We need to process more packets */
			}

		case EAP_SIM_CLIENT_ERROR:
		{
			char buff[20];

			vp = fr_pair_find_by_da(from_peer, attr_eap_sim_client_error_code, TAG_ANY);
			if (!vp) {
				REDEBUG("EAP-SIM Peer rejected SIM-Challenge with client-error message but "
					"has not supplied a client error code");
			} else {
				REDEBUG("Client rejected SIM-Challenge with error: %s (%i)",
					fr_pair_value_enum(vp, buff), vp->vp_uint16);
			}
			eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
			return RLM_MODULE_REJECT;
		}

		case EAP_SIM_NOTIFICATION:
			goto notification;

		default:
			goto unexpected_subtype;
		}

	/*
	 *	Peer acked our failure
	 */
	case EAP_SIM_SERVER_FAILURE_NOTIFICATION:
		switch (subtype) {
		case EAP_SIM_NOTIFICATION:
			RDEBUG2("SIM-Notification ACKed, sending EAP-Failure");
			eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE);
			return RLM_MODULE_REJECT;

		default:
			goto unexpected_subtype;
		}

	/*
	 *	Something bad happened...
	 */
	default:
		rad_assert(0);
		eap_sim_state_enter(eap_session, EAP_SIM_SERVER_FAILURE_NOTIFICATION);
		return RLM_MODULE_HANDLED;				/* We need to process more packets */
	}
}

/*
 *	Initiate the EAP-SIM session by starting the state machine
 *      and initiating the state.
 */
static rlm_rcode_t mod_session_init(void *instance, eap_session_t *eap_session)
{
	REQUEST				*request = eap_session->request;
	eap_sim_session_t		*eap_sim_session;
	rlm_eap_sim_t			*inst = instance;
	fr_sim_id_type_t		type;
	fr_sim_method_hint_t		method;

	MEM(eap_sim_session = talloc_zero(eap_session, eap_sim_session_t));

	eap_session->opaque = eap_sim_session;

	/*
	 *	Set default configuration, we may allow these
	 *	to be toggled by attributes later.
	 */
	eap_sim_session->send_result_ind = inst->protected_success;
	eap_sim_session->id_req = SIM_ANY_ID_REQ;	/* Set the default */

	/*
	 *	This value doesn't have be strong, but it is
	 *	good if it is different now and then.
	 */
	eap_sim_session->sim_id = (fr_rand() & 0xff);

	/*
	 *	Save the keying material, because it could change on a subsequent retrieval.
	 */
	RDEBUG2("New EAP-SIM session");

	/*
	 *	Process the identity that we received in the
	 *	EAP-Identity-Response and use it to determine
	 *	the initial request we send to the Supplicant.
	 */
	if (fr_sim_id_type(&type, &method,
			   eap_session->identity, talloc_array_length(eap_session->identity) - 1) < 0) {
		RPWDEBUG2("Failed parsing identity, continuing anyway");
	}

	switch (method) {
	default:
		RWDEBUG("EAP-Identity-Response hints that EAP-%s should be started, but we're attempting EAP-SIM",
			fr_int2str(sim_id_method_hint_table, method, "<INVALID>"));
		break;

	case SIM_METHOD_HINT_SIM:
	case SIM_METHOD_HINT_UNKNOWN:
		break;
	}
	eap_session->process = mod_process;

	/*
	 *	Figure out what type of identity we have
	 *	and use it to determine the initial
	 *	request we send.
	 */
	switch (type) {
	/*
	 *	These types need to be transformed into something
	 *	usable before we can do anything.
	 */
	case SIM_ID_TYPE_UNKNOWN:
	case SIM_ID_TYPE_PSEUDONYM:
	case SIM_ID_TYPE_FASTAUTH:
	/*
	 *	Permanent ID means we can just send the challenge
	 */
	case SIM_ID_TYPE_PERMANENT:
		eap_sim_session->keys.identity_len = talloc_array_length(eap_session->identity) - 1;
		MEM(eap_sim_session->keys.identity = talloc_memdup(eap_sim_session, eap_session->identity,
								   eap_sim_session->keys.identity_len));
		eap_sim_state_enter(eap_session, EAP_SIM_SERVER_START);
		return RLM_MODULE_HANDLED;
	}

	return RLM_MODULE_HANDLED;
}
/** Modify user's object in LDAP
 *
 * Process a modifcation map to update a user object in the LDAP directory.
 *
 * @param inst rlm_ldap instance.
 * @param request Current request.
 * @param section that holds the map to process.
 * @return one of the RLM_MODULE_* values.
 */
static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section)
{
	rlm_rcode_t	rcode = RLM_MODULE_OK;

	ldap_handle_t	*conn = NULL;

	LDAPMod		*mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP];
	LDAPMod		**modify = mod_p;

	char		*passed[LDAP_MAX_ATTRMAP * 2];
	int		i, total = 0, last_pass = 0;

	char 		*expanded[LDAP_MAX_ATTRMAP];
	int		last_exp = 0;

	char const	*attr;
	char const	*value;

	char const	*dn;
	/*
	 *	Build our set of modifications using the update sections in
	 *	the config.
	 */
	CONF_ITEM  	*ci;
	CONF_PAIR	*cp;
	CONF_SECTION 	*cs;
	FR_TOKEN	op;
	char		path[MAX_STRING_LEN];

	char		*p = path;

	rad_assert(section);

	/*
	 *	Locate the update section were going to be using
	 */
	if (section->reference[0] != '.') {
		*p++ = '.';
	}

	if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) {
		goto error;
	}

	ci = cf_reference_item(NULL, section->cs, path);
	if (!ci) {
		goto error;
	}

	if (!cf_item_is_section(ci)){
		REDEBUG("Reference must resolve to a section");

		goto error;
	}

	cs = cf_section_sub_find(cf_itemtosection(ci), "update");
	if (!cs) {
		REDEBUG("Section must contain 'update' subsection");

		goto error;
	}

	/*
	 *	Iterate over all the pairs, building our mods array
	 */
	for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) {
	     	int do_xlat = false;

	     	if (total == LDAP_MAX_ATTRMAP) {
	     		REDEBUG("Modify map size exceeded");

	     		goto error;
	     	}

		if (!cf_item_is_pair(ci)) {
			REDEBUG("Entry is not in \"ldap-attribute = value\" format");

			goto error;
		}

		/*
		 *	Retrieve all the information we need about the pair
		 */
		cp = cf_itemtopair(ci);
		value = cf_pair_value(cp);
		attr = cf_pair_attr(cp);
		op = cf_pair_operator(cp);

		if (!value || (*value == '\0')) {
			RDEBUG("Empty value string, skipping attribute \"%s\"", attr);

			continue;
		}

		switch (cf_pair_value_type(cp))
		{
			case T_BARE_WORD:
			case T_SINGLE_QUOTED_STRING:
			break;
			case T_BACK_QUOTED_STRING:
			case T_DOUBLE_QUOTED_STRING:
				do_xlat = true;
			break;
			default:
				rad_assert(0);
				goto error;
		}

		if (op == T_OP_CMP_FALSE) {
			passed[last_pass] = NULL;
		} else if (do_xlat) {
			char *exp = NULL;

			if (radius_xlat(exp, 0, request, value, NULL, NULL) <= 0) {
				RDEBUG("Skipping attribute \"%s\"", attr);

				talloc_free(exp);

				continue;
			}

			expanded[last_exp++] = exp;
			passed[last_pass] = exp;
		/*
		 *	Static strings
		 */
		} else {
			memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass]));
		}

		passed[last_pass + 1] = NULL;

		mod_s[total].mod_values = &(passed[last_pass]);

		last_pass += 2;

		switch (op)
		{
		/*
		 *  T_OP_EQ is *NOT* supported, it is impossible to
		 *  support because of the lack of transactions in LDAP
		 */
		case T_OP_ADD:
			mod_s[total].mod_op = LDAP_MOD_ADD;
			break;

		case T_OP_SET:
			mod_s[total].mod_op = LDAP_MOD_REPLACE;
			break;

		case T_OP_SUB:
		case T_OP_CMP_FALSE:
			mod_s[total].mod_op = LDAP_MOD_DELETE;
			break;

#ifdef LDAP_MOD_INCREMENT
		case T_OP_INCRM:
			mod_s[total].mod_op = LDAP_MOD_INCREMENT;
			break;
#endif
		default:
			REDEBUG("Operator '%s' is not supported for LDAP modify operations",
			        fr_int2str(fr_tokens, op, "<INVALID>"));

			goto error;
		}

		/*
		 *	Now we know the value is ok, copy the pointers into
		 *	the ldapmod struct.
		 */
		memcpy(&(mod_s[total].mod_type), &(attr), sizeof(mod_s[total].mod_type));

		mod_p[total] = &(mod_s[total]);
		total++;
	}

	if (total == 0) {
		rcode = RLM_MODULE_NOOP;
		goto release;
	}

	mod_p[total] = NULL;

	conn = rlm_ldap_get_socket(inst, request);
	if (!conn) return RLM_MODULE_FAIL;


	dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode);
	if (!dn || (rcode != RLM_MODULE_OK)) {
		goto error;
	}

	rcode = rlm_ldap_modify(inst, request, &conn, dn, modify);

	release:
	error:
	/*
	 *	Free up any buffers we allocated for xlat expansion
	 */
	for (i = 0; i < last_exp; i++) {
		talloc_free(expanded[i]);
	}

	rlm_ldap_release_socket(inst, conn);

	return rcode;
}
Example #20
0
/** Evaluate a map
 *
 * @param[in] request the REQUEST
 * @param[in] modreturn the previous module return code
 * @param[in] depth of the recursion (only used for debugging)
 * @param[in] c the condition to evaluate
 * @return -1 on error, 0 for "no match", 1 for "match".
 */
int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth,
			fr_cond_t const *c)
{
	int rcode;
	char *lhs, *rhs;
	value_pair_map_t *map;

	rad_assert(c->type == COND_TYPE_MAP);
	map = c->data.map;

	rad_assert(map->dst->type != VPT_TYPE_UNKNOWN);
	rad_assert(map->src->type != VPT_TYPE_UNKNOWN);
	rad_assert(map->dst->type != VPT_TYPE_LIST);
	rad_assert(map->src->type != VPT_TYPE_LIST);
	rad_assert(map->dst->type != VPT_TYPE_REGEX);
	rad_assert(map->dst->type != VPT_TYPE_REGEX_STRUCT);

	EVAL_DEBUG("Map %s ? %s",
		   fr_int2str(template_names, map->dst->type, "???"),
		   fr_int2str(template_names, map->src->type, "???"));

	/*
	 *	Verify regexes.
	 */
	if ((map->src->type == VPT_TYPE_REGEX) ||
	    (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
		rad_assert(map->op == T_OP_REG_EQ);
	} else {
		rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE)));
	}

	/*
	 *	They're both attributes.  Do attribute-specific work.
	 *
	 *	LHS is DST.  RHS is SRC <sigh>
	 */
	if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to ATTR");
		if ((radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) ||
		    (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0)) {
		    return -2;
		}

		return paircmp_op(lhs_vp, map->op, rhs_vp);
	}

	/*
	 *	LHS is a cast.  Do type-specific comparisons, as if
	 *	the LHS was a real attribute.
	 */
	if (c->cast) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		/*
		 *	Try to copy data from the VP which is being
		 *	casted, instead of printing it to a string and
		 *	then re-parsing it.
		 */
		if (map->dst->type == VPT_TYPE_ATTR) {
			VALUE_PAIR *cast_vp;

			if (radius_tmpl_get_vp(&cast_vp, request, map->dst) < 0) {
				return false;
			}

			lhs_vp = pairalloc(request, c->cast);
			if (!lhs_vp) return false;

			/*
			 *	In a separate function for clarity
			 */
			if (do_cast_copy(lhs_vp, cast_vp) < 0) {
				talloc_free(lhs_vp);
				return false;
			}

		} else {
			rcode = get_cast_vp(&lhs_vp, request, map->dst, c->cast);
			if (rcode < 0) {
				return rcode;
			}
		}
		rad_assert(lhs_vp);

		/*
		 *	Get either a real VP, or parse the RHS into a
		 *	VP, and return that.
		 */
		if (map->src->type == VPT_TYPE_ATTR) {
			if (radius_tmpl_get_vp(&rhs_vp, request, map->src) < 0) {
				return -2;
			}
		} else {
			rcode = get_cast_vp(&rhs_vp, request, map->src, c->cast);
			if (rcode < 0) {
				return rcode;
			}
			rad_assert(rhs_vp);
		}
		if (!rhs_vp) return -2;

		EVAL_DEBUG("CAST to %s",
			   fr_int2str(dict_attr_types,
				      c->cast->type, "?Unknown?"));

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&lhs_vp);
		if (map->src->type != VPT_TYPE_ATTR) {
			pairfree(&rhs_vp);
		}
		return rcode;
	}

	/*
	 *	Might be a virtual comparison
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type != VPT_TYPE_REGEX) &&
	    (map->src->type != VPT_TYPE_REGEX_STRUCT) &&
	    (c->pass2_fixup == PASS2_PAIRCOMPARE)) {
		int ret;
		VALUE_PAIR *lhs_vp;

		EVAL_DEBUG("virtual ATTR to DATA");

		rcode = get_cast_vp(&lhs_vp, request, map->src, map->dst->vpt_da);
		if (rcode < 0) {
			return rcode;
		}
		rad_assert(lhs_vp);

		/*
		 *	paircompare requires the operator be set for the
		 *	check attribute.
		 */
		lhs_vp->op = map->op;
		ret = paircompare(request, request->packet->vps, lhs_vp, NULL);
		talloc_free(lhs_vp);
		if (ret == 0) {
			return true;
		}
		return false;
	}
	rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE);

	/*
	 *	RHS has been pre-parsed into binary data.  Go check
	 *	that.
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type == VPT_TYPE_DATA)) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to DATA");

		if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) {
			return -2;
		}

		rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->vpt_da);
		if (rcode < 0) {
			return rcode;
		}
		rad_assert(rhs_vp);

#ifdef WITH_EVAL_DEBUG
		debug_pair(lhs_vp);
		debug_pair(rhs_vp);
#endif

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		pairfree(&rhs_vp);
		return rcode;
	}

	rad_assert(map->src->type != VPT_TYPE_DATA);
	rad_assert(map->dst->type != VPT_TYPE_DATA);

#ifdef HAVE_REGEX_H
	/*
	 *	Parse regular expressions.
	 */
	if ((map->src->type == VPT_TYPE_REGEX) ||
	    (map->src->type == VPT_TYPE_REGEX_STRUCT)) {
		return do_regex(request, map);
	}
#endif

	/*
	 *	The RHS now needs to be expanded into a string.
	 */
	rcode = radius_expand_tmpl(&rhs, request, map->src);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}
	rad_assert(rhs != NULL);

	/*
	 *	User-Name == FOO
	 *
	 *	Parse the RHS to be the same DA as the LHS.  do
	 *	comparisons.  So long as it's not a regex, which does
	 *	string comparisons.
	 *
	 *	The LHS may be a virtual attribute, too.
	 */
	if (map->dst->type == VPT_TYPE_ATTR) {
		VALUE_PAIR *lhs_vp, *rhs_vp;

		EVAL_DEBUG("ATTR to non-REGEX");

		/*
		 *	No LHS means no match
		 */
		if (radius_tmpl_get_vp(&lhs_vp, request, map->dst) < 0) {
			/*
			 *	Not a real attr: might be a dynamic comparison.
			 */
			if ((map->dst->type == VPT_TYPE_ATTR) &&
			    (map->dst->vpt_da->vendor == 0) &&
			    radius_find_compare(map->dst->vpt_da)) {
				rhs_vp = pairalloc(request, map->dst->vpt_da);
				rad_assert(rhs_vp != NULL);
				if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
					talloc_free(rhs);
					EVAL_DEBUG("FAIL %d", __LINE__);
					return -1;
				}
				talloc_free(rhs);

				rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0);
				pairfree(&rhs_vp);
				return rcode;
			}

			return -2;
		}

		/*
		 *	Get VP for RHS
		 */
		rhs_vp = pairalloc(request, map->dst->vpt_da);
		rad_assert(rhs_vp != NULL);
		if (pairparsevalue(rhs_vp, rhs, 0) < 0) {
			talloc_free(rhs);
			pairfree(&rhs_vp);
			EVAL_DEBUG("FAIL %d", __LINE__);
			return -1;
		}

		rcode = paircmp_op(lhs_vp, map->op, rhs_vp);
		talloc_free(rhs);
		pairfree(&rhs_vp);
		return rcode;
	}

	/*
	 *	The LHS is a string.  Expand it.
	 */
	rcode = radius_expand_tmpl(&lhs, request, map->dst);
	if (rcode < 0) {
		EVAL_DEBUG("FAIL %d", __LINE__);
		return rcode;
	}
	rad_assert(lhs != NULL);

	EVAL_DEBUG("LHS is %s", lhs);

	/*
	 *	Loop over the string, doing comparisons
	 */
	if (all_digits(lhs) && all_digits(rhs)) {
		int lint, rint;

		lint = strtoul(lhs, NULL, 0);
		rint = strtoul(rhs, NULL, 0);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (lint == rint);

		case T_OP_NE:
			return (lint != rint);

		case T_OP_LT:
			return (lint < rint);

		case T_OP_GT:
			return (lint > rint);

		case T_OP_LE:
			return (lint <= rint);

		case T_OP_GE:
			return (lint >= rint);

		default:
			break;
		}

	} else {
		rad_assert(lhs != NULL);
		rad_assert(rhs != NULL);

		rcode = strcmp(lhs, rhs);
		talloc_free(lhs);
		talloc_free(rhs);

		switch (map->op) {
		case T_OP_CMP_EQ:
			return (rcode == 0);

		case T_OP_NE:
			return (rcode != 0);

		case T_OP_LT:
			return (rcode < 0);

		case T_OP_GT:
			return (rcode > 0);

		case T_OP_LE:
			return (rcode <= 0);

		case T_OP_GE:
			return (rcode >= 0);

		default:
			break;
		}
	}

	EVAL_DEBUG("FAIL %d", __LINE__);
	return -1;
}
Example #21
0
static int parse_sub_section(CONF_SECTION *parent, rlm_rest_section_t *config, rlm_components_t comp)
{
	CONF_SECTION *cs;

	char const *name = section_type_value[comp].section;

	cs = cf_section_sub_find(parent, name);
	if (!cs) {
		config->name = NULL;
		return 0;
	}

	if (cf_section_parse(cs, config, section_config) < 0) {
		config->name = NULL;
		return -1;
	}

	/*
	 *  Add section name (Maybe add to headers later?).
	 */
	config->name = name;

	/*
	 *  Sanity check
	 */
	 if ((config->username && !config->password) || (!config->username && config->password)) {
		cf_log_err_cs(cs, "'username' and 'password' must both be set or both be absent");

		return -1;
	 }

	/*
	 *  Convert HTTP method auth and body type strings into their integer equivalents.
	 */
	config->auth = fr_str2int(http_auth_table, config->auth_str, HTTP_AUTH_UNKNOWN);
	if (config->auth == HTTP_AUTH_UNKNOWN) {
		cf_log_err_cs(cs, "Unknown HTTP auth type '%s'", config->auth_str);

		return -1;
	} else if ((config->auth != HTTP_AUTH_NONE) && !http_curl_auth[config->auth]) {
		cf_log_err_cs(cs, "Unsupported HTTP auth type \"%s\", check libcurl version, OpenSSL build "
			      "configuration, then recompile this module", config->auth_str);

		return -1;
	}

	config->method = fr_str2int(http_method_table, config->method_str, HTTP_METHOD_CUSTOM);
	config->timeout = ((config->timeout_tv.tv_usec * 1000) + (config->timeout_tv.tv_sec / 1000));

	/*
	 *  We don't have any custom user data, so we need to select the right encoder based
	 *  on the body type.
	 *
	 *  To make this slightly more/less confusing, we accept both canonical body_types,
	 *  and content_types.
	 */
	if (!config->data) {
		config->body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
		if (config->body == HTTP_BODY_UNKNOWN) {
			config->body = fr_str2int(http_content_type_table, config->body_str, HTTP_BODY_UNKNOWN);
		}

		if (config->body == HTTP_BODY_UNKNOWN) {
			cf_log_err_cs(cs, "Unknown HTTP body type '%s'", config->body_str);
			return -1;
		}

		switch (http_body_type_supported[config->body]) {
		case HTTP_BODY_UNSUPPORTED:
			cf_log_err_cs(cs, "Unsupported HTTP body type \"%s\", please submit patches",
				      config->body_str);
			return -1;

		case HTTP_BODY_INVALID:
			cf_log_err_cs(cs, "Invalid HTTP body type.  \"%s\" is not a valid web API data "
				      "markup format", config->body_str);
			return -1;

		case HTTP_BODY_UNAVAILABLE:
			cf_log_err_cs(cs, "Unavailable HTTP body type.  \"%s\" is not available in this "
				      "build", config->body_str);
			return -1;

		default:
			break;
		}
	/*
	 *  We have custom body data so we set HTTP_BODY_CUSTOM_XLAT, but also need to try and
	 *  figure out what content-type to use. So if they've used the canonical form we
	 *  need to convert it back into a proper HTTP content_type value.
	 */
	} else {
		http_body_type_t body;

		config->body = HTTP_BODY_CUSTOM_XLAT;

		body = fr_str2int(http_body_type_table, config->body_str, HTTP_BODY_UNKNOWN);
		if (body != HTTP_BODY_UNKNOWN) {
			config->body_str = fr_int2str(http_content_type_table, body, config->body_str);
		}
	}

	if (config->force_to_str) {
		config->force_to = fr_str2int(http_body_type_table, config->force_to_str, HTTP_BODY_UNKNOWN);
		if (config->force_to == HTTP_BODY_UNKNOWN) {
			config->force_to = fr_str2int(http_content_type_table, config->force_to_str, HTTP_BODY_UNKNOWN);
		}

		if (config->force_to == HTTP_BODY_UNKNOWN) {
			cf_log_err_cs(cs, "Unknown forced response body type '%s'", config->force_to_str);
			return -1;
		}

		switch (http_body_type_supported[config->force_to]) {
		case HTTP_BODY_UNSUPPORTED:
			cf_log_err_cs(cs, "Unsupported forced response body type \"%s\", please submit patches",
				      config->force_to_str);
			return -1;

		case HTTP_BODY_INVALID:
			cf_log_err_cs(cs, "Invalid HTTP forced response body type.  \"%s\" is not a valid web API data "
				      "markup format", config->force_to_str);
			return -1;

		default:
			break;
		}
	}

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

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

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

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

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

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

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

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

            {
                const DICT_ATTR *dattr;

                /*
                 *	Bare words MUST be dictionary attributes
                 */

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

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

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

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

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

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

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

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

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

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

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

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

    *tail = (policy_item_t *) this;

    return 1;
}
Example #24
0
/*
 *	Print an Ascend binary filter attribute to a string,
 *	Grrr... Ascend makes the server do this work, instead
 *	of doing it on the NAS.
 *
 *	Note we don't bother checking 'len' after the snprintf's.
 *	This function should ONLY be called with a large (~1k) buffer.
 */
void print_abinary(char *out, size_t outlen, uint8_t const *data, size_t len, int8_t quote)
{
    size_t 	i;
    char	*p;
    ascend_filter_t	const *filter;

    static char const *action[] = {"drop", "forward"};
    static char const *direction[] = {"out", "in"};

    p = out;

    /*
     *  Just for paranoia: wrong size filters get printed as octets
     */
    if (len != sizeof(*filter)) {
        strcpy(p, "0x");
        p += 2;
        outlen -= 2;
        for (i = 0; i < len; i++) {
            snprintf(p, outlen, "%02x", data[i]);
            p += 2;
            outlen -= 2;
        }
        return;
    }

    if (quote > 0) {
        *(p++) = (char) quote;
        outlen -= 3;			/* account for leading & trailing quotes */
    }

    filter = (ascend_filter_t const *) data;
    i = snprintf(p, outlen, "%s %s %s", fr_int2str(filterType, filter->type, "??"),
                 direction[filter->direction & 0x01], action[filter->forward & 0x01]);

    p += i;
    outlen -= i;

    /*
    *	Handle IP filters
    */
    if (filter->type == RAD_FILTER_IP) {

        if (filter->u.ip.srcip) {
            i = snprintf(p, outlen, " srcip %d.%d.%d.%d/%d",
                         ((uint8_t const *) &filter->u.ip.srcip)[0],
                         ((uint8_t const *) &filter->u.ip.srcip)[1],
                         ((uint8_t const *) &filter->u.ip.srcip)[2],
                         ((uint8_t const *) &filter->u.ip.srcip)[3],
                         filter->u.ip.srcmask);
            p += i;
            outlen -= i;
        }

        if (filter->u.ip.dstip) {
            i = snprintf(p, outlen, " dstip %d.%d.%d.%d/%d",
                         ((uint8_t const *) &filter->u.ip.dstip)[0],
                         ((uint8_t const *) &filter->u.ip.dstip)[1],
                         ((uint8_t const *) &filter->u.ip.dstip)[2],
                         ((uint8_t const *) &filter->u.ip.dstip)[3],
                         filter->u.ip.dstmask);
            p += i;
            outlen -= i;
        }

        i = snprintf(p, outlen, " %s", fr_int2str(filterProtoName, filter->u.ip.proto, "??"));
        p += i;
        outlen -= i;

        if (filter->u.ip.srcPortComp > RAD_NO_COMPARE) {
            i = snprintf(p, outlen, " srcport %s %d",
                         fr_int2str(filterCompare, filter->u.ip.srcPortComp, "??"),
                         ntohs(filter->u.ip.srcport));
            p += i;
            outlen -= i;
        }

        if (filter->u.ip.dstPortComp > RAD_NO_COMPARE) {
            i = snprintf(p, outlen, " dstport %s %d",
                         fr_int2str(filterCompare, filter->u.ip.dstPortComp, "??"),
                         ntohs(filter->u.ip.dstport));
            p += i;
            outlen -= i;
        }

        if (filter->u.ip.established) {
            i = snprintf(p, outlen, " est");
            p += i;
        }

        /*
         *	Handle IPX filters
         */
    } else if (filter->type == RAD_FILTER_IPX) {
        /* print for source */
        if (filter->u.ipx.src.net) {
            i = snprintf(p, outlen, " srcipxnet 0x%04x srcipxnode 0x%02x%02x%02x%02x%02x%02x",
                         (unsigned int)ntohl(filter->u.ipx.src.net),
                         filter->u.ipx.src.node[0], filter->u.ipx.src.node[1],
                         filter->u.ipx.src.node[2], filter->u.ipx.src.node[3],
                         filter->u.ipx.src.node[4], filter->u.ipx.src.node[5]);
            p += i;
            outlen -= i;

            if (filter->u.ipx.srcSocComp > RAD_NO_COMPARE) {
                i = snprintf(p, outlen, " srcipxsock %s 0x%04x",
                             fr_int2str(filterCompare, filter->u.ipx.srcSocComp, "??"),
                             ntohs(filter->u.ipx.src.socket));
                p += i;
                outlen -= i;
            }
        }

        /* same for destination */
        if (filter->u.ipx.dst.net) {
            i = snprintf(p, outlen, " dstipxnet 0x%04x dstipxnode 0x%02x%02x%02x%02x%02x%02x",
                         (unsigned int)ntohl(filter->u.ipx.dst.net),
                         filter->u.ipx.dst.node[0], filter->u.ipx.dst.node[1],
                         filter->u.ipx.dst.node[2], filter->u.ipx.dst.node[3],
                         filter->u.ipx.dst.node[4], filter->u.ipx.dst.node[5]);
            p += i;
            outlen -= i;

            if (filter->u.ipx.dstSocComp > RAD_NO_COMPARE) {
                i = snprintf(p, outlen, " dstipxsock %s 0x%04x",
                             fr_int2str(filterCompare, filter->u.ipx.dstSocComp, "??"),
                             ntohs(filter->u.ipx.dst.socket));
                p += i;
            }
        }
    } else if (filter->type == RAD_FILTER_GENERIC) {
        int count;

        i = snprintf(p, outlen, " %u ", (unsigned int) ntohs(filter->u.generic.offset));
        p += i;

        /* show the mask */
        for (count = 0; count < ntohs(filter->u.generic.len); count++) {
            i = snprintf(p, outlen, "%02x", filter->u.generic.mask[count]);
            p += i;
            outlen -= i;
        }

        strcpy(p, " ");
        p++;
        outlen--;

        /* show the value */
        for (count = 0; count < ntohs(filter->u.generic.len); count++) {
            i = snprintf(p, outlen, "%02x", filter->u.generic.value[count]);
            p += i;
            outlen -= i;
        }

        i = snprintf(p, outlen, " %s", (filter->u.generic.compNeq) ? "!=" : "==");
        p += i;
        outlen -= i;

        if (filter->u.generic.more != 0) {
            i = snprintf(p, outlen, " more");
            p += i;
        }
    }

    if (quote > 0) {
        *(p++) = (char) quote;
    }
    *p = '\0';
}
Example #25
0
/*
 *	Function to return a token saying what it read, and possibly
 *	a buffer of the quoted string or bare word.
 */
static policy_lex_t policy_lex_file(policy_lex_file_t *lexer,
                                    int flags,
                                    char *mystring, size_t mystringlen)
{
    policy_lex_t token = POLICY_LEX_BARE_WORD; /* to prime it */

    if (lexer->debug & POLICY_DEBUG_PRINT_TOKENS) {
        flags |= POLICY_LEX_FLAG_PRINT_TOKEN;
    }

    if (!lexer->fp) {
        return POLICY_LEX_EOF;
    }

    /*
     *	Starting off, the buffer needs to be primed.
     */
    if (!lexer->parse) {
        lexer->parse = fgets(lexer->buffer,
                             sizeof(lexer->buffer),
                             lexer->fp);

        if (!lexer->parse) {
            return POLICY_LEX_EOF;
        }

        lexer->lineno = 1;
    } /* buffer is primed, read stuff */

    if (lexer->token != POLICY_LEX_BAD) {
        token = lexer->token;
        lexer->token = POLICY_LEX_BAD;
        return token;
    }

    /*
     *	Ignore whitespace, and keep filling the buffer
     */
    while (lexer->parse) {
        const char *next;

        next = policy_lex_string(lexer->parse, &token,
                                 mystring, mystringlen);
        switch (token) {
        case POLICY_LEX_WHITESPACE: /* skip whitespace */
            lexer->parse = next;
            continue;

        case POLICY_LEX_EOL: /* read another line */
            lexer->parse = fgets(lexer->buffer,
                                 sizeof(lexer->buffer),
                                 lexer->fp);
            lexer->lineno++;
            if (flags & POLICY_LEX_FLAG_RETURN_EOL) {
                return POLICY_LEX_EOL;
            }
            break;	/* read another token */

        default:	/* return the token */
            if (!(flags & POLICY_LEX_FLAG_PEEK)) {
                lexer->parse = next;
            }
            if (flags & POLICY_LEX_FLAG_PRINT_TOKEN) {
                debug_tokens("[%s token %s] ",
                             (flags & POLICY_LEX_FLAG_PEEK) ? "peek " : "",
                             fr_int2str(rlm_policy_tokens,
                                        token, "?"));
            }
            return token;
            break;
        }
    } /* loop until EOF */

    /*
     *	Close it for the user.
     */
    fclose(lexer->fp);
    lexer->fp = NULL;

    return POLICY_LEX_EOF;
}
Example #26
0
size_t xlat_sprint(char *buffer, size_t bufsize, xlat_exp_t const *node)
{
	size_t len;
	char *p, *end;

	if (!node) {
		*buffer = '\0';
		return 0;
	}

	p = buffer;
	end = buffer + bufsize;

	while (node) {
		switch (node->type) {
		case XLAT_LITERAL:
			strlcpy(p, node->fmt, end - p);
			p += strlen(p);
			break;

		case XLAT_PERCENT:
			p[0] = '%';
			p[1] = node->fmt[0];
			p += 2;
			break;

		case XLAT_ATTRIBUTE:
			*(p++) = '%';
			*(p++) = '{';

			if (node->ref != REQUEST_CURRENT) {
				strlcpy(p, fr_int2str(request_refs, node->ref, "??"), end - p);
				p += strlen(p);
				*(p++) = '.';
			}

			if ((node->ref != REQUEST_CURRENT) ||
			    (node->list != PAIR_LIST_REQUEST)) {
				strlcpy(p, fr_int2str(pair_lists, node->list, "??"), end - p);
				p += strlen(p);
				*(p++) = ':';
			}

			strlcpy(p, node->da->name, end - p);
			p += strlen(p);

			if (node->tag != TAG_ANY) {
				*(p++) = ':';
				snprintf(p, end - p, "%u", node->tag);
				p += strlen(p);
			}

			if (node->num != 0) {
				*(p++) = '[';

				if (node->num == 65536) {
					*(p++) = '#';

				} else if (node->num == 65537) {
					*(p++) = '*';

				} else {
					snprintf(p, end - p, "%u", node->num);
					p += strlen(p);
				}
				*(p++) = ']';
			}
			*(p++) = '}';
			break;
#ifdef HAVE_REGEX_H
		case XLAT_REGEX:
			snprintf(p, end - p, "%%{%u}", node->num);
			p += strlen(p);
			break;
#endif
		case XLAT_VIRTUAL:
			*(p++) = '%';
			*(p++) = '{';
			strlcpy(p, node->fmt, end - p);
			p += strlen(p);
			*(p++) = '}';
			break;

		case XLAT_MODULE:
			*(p++) = '%';
			*(p++) = '{';
			strlcpy(p, node->xlat->name, end - p);
			p += strlen(p);
			*(p++) = ':';
			rad_assert(node->child != NULL);
			len = xlat_sprint(p, end - p, node->child);
			p += len;
			*(p++) = '}';
			break;

		case XLAT_ALTERNATE:
			*(p++) = '%';
			*(p++) = '{';

			len = xlat_sprint(p, end - p, node->child);
			p += len;

			*(p++) = ':';
			*(p++) = '-';

			len = xlat_sprint(p, end - p, node->alternate);
			p += len;

			*(p++) = '}';
			break;
		}


		if (p == end) break;

		node = node->next;
	}

	*p = '\0';

	return p - buffer;
}
/** Convert CONFIG_PAIR (which may contain refs) to value_pair_map_t.
 *
 * Treats the left operand as an attribute reference
 * @verbatim<request>.<list>.<attribute>@endverbatim 
 *
 * Treatment of left operand depends on quotation, barewords are treated as
 * attribute references, double quoted values are treated as expandable strings,
 * single quoted values are treated as literal strings.
 *
 * Return must be freed with radius_mapfree.
 *
 * @param[in] cp to convert to map.
 * @param[in] dst_request_def The default request to insert unqualified
 *	attributes into.
 * @param[in] dst_list_def The default list to insert unqualified attributes
 *	into.
 * @param[in] src_request_def The default request to resolve attribute
 *	references in.
 * @param[in] src_list_def The default list to resolve unqualified attributes
 *	in.
 * @return value_pair_map_t if successful or NULL on error.
 */
value_pair_map_t *radius_cp2map(CONF_PAIR *cp,
				request_refs_t dst_request_def,
				pair_lists_t dst_list_def,
				request_refs_t src_request_def,
				pair_lists_t src_list_def)
{
	value_pair_map_t *map;
	const char *attr;
	const char *value;
	FR_TOKEN type;
	CONF_ITEM *ci = cf_pairtoitem(cp); 
	
	if (!cp) return NULL;
	
	map = rad_calloc(sizeof(value_pair_map_t));

	attr = cf_pair_attr(cp);
	value = cf_pair_value(cp);
	if (!value) {
		cf_log_err(ci, "Missing attribute value");
		
		goto error;
	}
	
	map->dst = radius_attr2tmpl(attr, dst_request_def, dst_list_def);
	if (!map->dst){
		goto error;
	}

	/*
	 *	Bare words always mean attribute references.
	 */
	type = cf_pair_value_type(cp);
	map->src = type == T_BARE_WORD ?
		   radius_attr2tmpl(value, src_request_def, src_list_def) :
		   radius_str2tmpl(value, type);

	if (!map->src) {
		goto error;
	}

	map->op = cf_pair_operator(cp);
	map->ci = ci;
	
	/*
	 *	Lots of sanity checks for insane people...
	 */
	 
	/*
	 *	We don't support implicit type conversion
	 */
	if (map->dst->da && map->src->da &&
	    (map->src->da->type != map->dst->da->type)) {
		cf_log_err(ci, "Attribute type mismatch");
		
		goto error;
	}
	
	/*
	 *	What exactly where you expecting to happen here?
	 */
	if ((map->dst->type == VPT_TYPE_ATTR) &&
	    (map->src->type == VPT_TYPE_LIST)) {
		cf_log_err(ci, "Can't copy list into an attribute");
		
		goto error;
	}

	switch (map->src->type) 
	{
	
		/*
		 *	Only += and -= operators are supported for list copy.
		 */
		case VPT_TYPE_LIST:
			switch (map->op) {
			case T_OP_SUB:
			case T_OP_ADD:
				break;
			
			default:
				cf_log_err(ci, "Operator \"%s\" not allowed "
					   "for list copy",
					   fr_int2str(fr_tokens, map->op,
						      "¿unknown?"));
				goto error;
			}
		break;
		/*
		 *	@todo add support for exec expansion.
		 */
		case VPT_TYPE_EXEC:
			cf_log_err(ci, "Exec values are not allowed");
			break;
		default:
			break;
	}
	
	return map;
	
	error:
		radius_mapfree(&map);
		return NULL;
}
Example #28
0
/** Print data as integer, not as VALUE.
 *
 */
static ssize_t xlat_integer(UNUSED void *instance, REQUEST *request,
			    char const *fmt, char *out, size_t outlen)
{
	VALUE_PAIR 	*vp;

	uint64_t 	int64 = 0;	/* Needs to be initialised to zero */
	uint32_t	int32 = 0;	/* Needs to be initialised to zero */

	while (isspace((int) *fmt)) fmt++;

	if ((radius_get_vp(&vp, request, fmt) < 0) || !vp) {
		*out = '\0';
		return 0;
	}

	switch (vp->da->type) {
	case PW_TYPE_OCTETS:
	case PW_TYPE_STRING:
		if (vp->length > 8) {
			break;
		}

		if (vp->length > 4) {
			memcpy(&int64, vp->vp_octets, vp->length);
			return snprintf(out, outlen, "%" PRIu64, htonll(int64));
		}

		memcpy(&int32, vp->vp_octets, vp->length);
		return snprintf(out, outlen, "%i", htonl(int32));

	case PW_TYPE_INTEGER64:
		return snprintf(out, outlen, "%" PRIu64, vp->vp_integer64);

	/*
	 *	IP addresses are treated specially, as parsing functions assume the value
	 *	is bigendian and will convert it for us.
	 */
	case PW_TYPE_IPADDR:
		return snprintf(out, outlen, "%u", htonl(vp->vp_ipaddr));

	case PW_TYPE_INTEGER:
	case PW_TYPE_DATE:
	case PW_TYPE_BYTE:
	case PW_TYPE_SHORT:
		return snprintf(out, outlen, "%u", vp->vp_integer);

	/*
	 *	Ethernet is weird... It's network related, so we assume to it should be
	 *	bigendian.
	 */
	case PW_TYPE_ETHERNET:
		memcpy(&int64, &vp->vp_ether, vp->length);
		return snprintf(out, outlen, "%" PRIu64, htonll(int64));

	case PW_TYPE_SIGNED:
		return snprintf(out, outlen, "%i", vp->vp_signed);

	default:
		break;
	}

	REDEBUG("Type '%s' of length %zu cannot be converted to integer",
		fr_int2str(dict_attr_types, vp->da->type, PW_TYPE_INVALID), vp->length);
	*out = '\0';

	return -1;
}
Example #29
0
void vradlog_request(log_type_t type, log_debug_t lvl, REQUEST *request, char const *msg, va_list ap)
{
    size_t len = 0;
    char const *filename = default_log.file;
    FILE *fp = NULL;
    char buffer[10240];	/* The largest config item size, then extra for prefixes and suffixes */
    char *p;
    char const *extra = "";
    va_list aq;

    rad_assert(request);

    /*
     *	Debug messages get treated specially.
     */
    if ((type & L_DBG) != 0) {

        if (!radlog_debug_enabled(type, lvl, request)) {
            return;
        }

        /*
         *	Use the debug output file, if specified,
         *	otherwise leave it as the default log file.
         */
#ifdef WITH_COMMAND_SOCKET
        filename = default_log.debug_file;
        if (!filename)
#endif

            filename = default_log.file;
    }

    if (request && filename) {
        radlog_func_t rl = request->log.func;

        request->log.func = NULL;

        /*
         *	This is SLOW!  Doing it for every log message
         *	in every request is NOT recommended!
         */

        /* FIXME: escape chars! */
        if (radius_xlat(buffer, sizeof(buffer), request, filename, NULL, NULL) < 0) {
            return;
        }
        request->log.func = rl;

        p = strrchr(buffer, FR_DIR_SEP);
        if (p) {
            *p = '\0';
            if (rad_mkdir(buffer, S_IRWXU) < 0) {
                ERROR("Failed creating %s: %s", buffer, fr_syserror(errno));
                return;
            }
            *p = FR_DIR_SEP;
        }

        fp = fopen(buffer, "a");
    }

    /*
     *	Print timestamps to the file.
     */
    if (fp) {
        time_t timeval;
        timeval = time(NULL);

#ifdef HAVE_GMTIME_R
        if (log_dates_utc) {
            struct tm utc;
            gmtime_r(&timeval, &utc);
            ASCTIME_R(&utc, buffer, sizeof(buffer) - 1);
        } else
#endif
        {
            CTIME_R(&timeval, buffer, sizeof(buffer) - 1);
        }

        len = strlen(buffer);
        p = strrchr(buffer, '\n');
        if (p) {
            p[0] = ' ';
            p[1] = '\0';
        }

        len += strlcpy(buffer + len, fr_int2str(levels, type, ": "), sizeof(buffer) - len);
        if (len >= sizeof(buffer)) goto finish;
    }

    if (request && request->module[0]) {
        len = snprintf(buffer + len, sizeof(buffer) - len, "%s : ", request->module);
        if (len >= sizeof(buffer)) goto finish;
    }

    /*
     *  If we don't copy the original ap we get a segfault from vasprintf. This is apparently
     *  due to ap sometimes being implemented with a stack offset which is invalidated if
     *  ap is passed into another function. See here:
     *  http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html
     *
     *  I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when
     *  running unit tests which generate errors under CI.
     */
    va_copy(aq, ap);
    vsnprintf(buffer + len, sizeof(buffer) - len, msg, aq);
    va_end(aq);

finish:
    switch (type) {
    case L_DBG_WARN:
        extra = "WARNING: ";
        type = L_DBG_WARN_REQ;
        break;

    case L_DBG_ERR:
        extra = "ERROR: ";
        type = L_DBG_ERR_REQ;
        break;
    default:
        break;
    }

    if (!fp) {

        if (debug_flag > 2) extra = "";

        if (request) {
            uint8_t indent;

            indent = request->log.indent > sizeof(spaces) ?
                     sizeof(spaces) :
                     request->log.indent;
            radlog(type, "(%u) %.*s%s%s", request->number, indent, spaces, extra, buffer);
        } else {
            radlog(type, "%s%s", extra, buffer);
        }
    } else {
        if (request) {
            fprintf(fp, "(%u) %s", request->number, extra);
        }
        fputs(buffer, fp);
        fputc('\n', fp);
        fclose(fp);
    }
}
/*
 *	Allow single attribute values to be retrieved from the cache.
 */
static ssize_t cache_xlat(void *instance, REQUEST *request,
			  char const *fmt, char *out, size_t freespace)
{
	rlm_cache_entry_t 	*c = NULL;
	rlm_cache_t		*inst = instance;
	rlm_cache_handle_t	*handle = NULL;

	VALUE_PAIR		*vp, *vps;
	pair_lists_t		list;
	DICT_ATTR const		*target;
	char const		*p = fmt;
	size_t			len;
	int			ret = 0;

	p += radius_list_name(&list, p, PAIR_LIST_REQUEST);
	if (list == PAIR_LIST_UNKNOWN) {
		REDEBUG("Unknown list qualifier in \"%s\"", fmt);
		ret = -1;
		goto finish;
	}

	target = dict_attrbyname(p);
	if (!target) {
		REDEBUG("Unknown attribute \"%s\"", p);
		return -1;
	}

	if (cache_acquire(&handle, inst, request) < 0) return -1;

	switch (cache_find(&c, inst, request, handle, fmt)) {
	case RLM_MODULE_OK:		/* found */
		break;

	case RLM_MODULE_NOTFOUND:	/* not found */
		*out = '\0';
		return 0;

	default:
		return -1;
	}

	switch (list) {
	case PAIR_LIST_REQUEST:
		vps = c->packet;
		break;

	case PAIR_LIST_REPLY:
		vps = c->reply;
		break;

	case PAIR_LIST_CONTROL:
		vps = c->control;
		break;

	default:
		REDEBUG("Unsupported list \"%s\"", fr_int2str(pair_lists, list, "<UNKNOWN>"));
		ret = -1;
		goto finish;
	}

	vp = pairfind(vps, target->attr, target->vendor, TAG_ANY);
	if (!vp) {
		RDEBUG("No instance of this attribute has been cached");
		*out = '\0';
		goto finish;
	}

	len = vp_prints_value(out, freespace, vp, 0);
	if (is_truncated(len, freespace)) {
		REDEBUG("Insufficient buffer space to write cached value");
		ret = -1;
		goto finish;
	}

finish:
	cache_free(inst, &c);
	cache_release(inst, request, &handle);

	return ret;
}