/* * Execute postauth_query after authentication */ static int rlm_sql_postauth(void *instance, REQUEST *request) { SQLSOCK *sqlsocket = NULL; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name); if(sql_set_user(inst, request, sqlusername, NULL) < 0) return RLM_MODULE_FAIL; /* If postauth_query is not defined, we stop here */ if (inst->config->postauth_query[0] == '\0') return RLM_MODULE_NOOP; /* Expand variables in the query */ memset(querystr, 0, MAX_QUERY_LEN); radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query, request, sql_escape_func); query_log(request, inst, querystr); DEBUG2("rlm_sql (%s) in sql_postauth: query is %s", inst->config->xlat_name, querystr); /* Initialize the sql socket */ sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return RLM_MODULE_FAIL; /* Process the query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); sql_release_socket(inst, sqlsocket); return RLM_MODULE_FAIL; } (inst->module->sql_finish_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); return RLM_MODULE_OK; }
static const char *expand_string(char *buffer, size_t sizeof_buffer, REQUEST *request, FR_TOKEN value_type, const char *value) { int result; char *p; switch (value_type) { default: case T_BARE_WORD: case T_SINGLE_QUOTED_STRING: return value; case T_BACK_QUOTED_STRING: result = radius_exec_program(value, request, 1, buffer, sizeof_buffer, NULL, NULL, 0); if (result != 0) { return NULL; } /* * The result should be ASCII. */ for (p = buffer; *p != '\0'; p++) { if (*p < ' ' ) { *p = '\0'; return buffer; } } return buffer; case T_DOUBLE_QUOTED_STRING: if (!strchr(value, '%')) return value; radius_xlat(buffer, sizeof_buffer, value, request, NULL, NULL); return buffer; } return NULL; }
/* * Execute postauth_query after authentication */ static rlm_rcode_t rlm_redisn_postauth(void *instance, REQUEST *request) { REDISSOCK *redis_socket = NULL; REDIS_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char redisnusername[MAX_STRING_LEN]; /* If postauth_query is not defined, we stop here */ if (!inst->postauth_query || (inst->postauth_query[0] == '\0')) return RLM_MODULE_NOOP; if(redisn_set_user(inst, request, redisnusername, NULL) < 0) return RLM_MODULE_FAIL; /* Expand variables in the query */ memset(querystr, 0, MAX_QUERY_LEN); radius_xlat(querystr, sizeof(querystr), inst->postauth_query, request, redisn_escape_func, inst); query_log(request, inst, querystr); DEBUG2("rlm_redisn (%s) in redisn_postauth: query is %s", inst->xlat_name, querystr); /* Initialize the redisn socket */ redis_socket = redisn_get_socket(inst); if (redis_socket == NULL) return RLM_MODULE_FAIL; /* Process the query */ if (rlm_redisn_query(inst, redis_socket, querystr)) { radlog(L_ERR, "rlm_redisn (%s) in redisn_postauth: Database query error - %s", inst->xlat_name, querystr); redisn_release_socket(inst, redis_socket); return RLM_MODULE_FAIL; } (inst->redisn_finish_query)(inst, redis_socket); redisn_release_socket(inst, redis_socket); return RLM_MODULE_OK; }
/* * Initiate the EAP-GTC session by sending a challenge to the peer. */ static int gtc_initiate(void *type_data, EAP_HANDLER *handler) { char challenge_str[1024]; int length; EAP_DS *eap_ds = handler->eap_ds; rlm_eap_gtc_t *inst = (rlm_eap_gtc_t *) type_data; if (!radius_xlat(challenge_str, sizeof(challenge_str), inst->challenge, handler->request, NULL, NULL)) { radlog(L_ERR, "rlm_eap_gtc: xlat of \"%s\" failed", inst->challenge); return 0; } length = strlen(challenge_str); /* * We're sending a request... */ eap_ds->request->code = PW_EAP_REQUEST; eap_ds->request->type.data = malloc(length); if (eap_ds->request->type.data == NULL) { radlog(L_ERR, "rlm_eap_gtc: out of memory"); return 0; } memcpy(eap_ds->request->type.data, challenge_str, length); eap_ds->request->type.length = length; /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = AUTHENTICATE; return 1; }
/** * @brief Convert base64 to hex * * Example: "%{base64tohex:Zm9v}" == "666f6f" */ static size_t base64_to_hex_xlat(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { char buffer[1024]; uint8_t decbuf[1024], *p; ssize_t declen; size_t freespace = outlen; size_t len; len = radius_xlat(buffer, sizeof(buffer), fmt, request, NULL, NULL); if (!len) { RDEBUGE("xlat failed."); *out = '\0'; return 0; } declen = fr_base64_decode(buffer, len, decbuf, sizeof(decbuf)); if (declen < 0) { RDEBUGE("base64 string invalid"); *out = '\0'; return 0; } p = decbuf; while ((declen-- > 0) && (--freespace > 0)) { if (freespace < 3) break; snprintf(out, 3, "%02x", *p++); /* Already decremented */ freespace -= 1; out += 2; } return outlen - freespace; }
/* * See if the counter matches. */ static int sqlcounter_cmp(void *instance, REQUEST *req, UNUSED VALUE_PAIR *request, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { rlm_sqlcounter_t *inst = instance; int counter; char querystr[MAX_QUERY_LEN]; char sqlxlat[MAX_QUERY_LEN]; /* first, expand %k, %b and %e in query */ sqlcounter_expand(querystr, MAX_QUERY_LEN, inst->query, inst); /* third, wrap query with sql module call & expand */ snprintf(sqlxlat, sizeof(sqlxlat), "%%{%s:%s}", inst->sqlmod_inst, querystr); /* Finally, xlat resulting SQL query */ radius_xlat(querystr, MAX_QUERY_LEN, sqlxlat, req, NULL, NULL); counter = atoi(querystr); return counter - check->vp_integer; }
/** * @brief Convert a string to uppercase * * Example: "%{uc:Foo}" == "FOO" * * Probably only works for ASCII */ static size_t uc_xlat(UNUSED void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { char *p, *q; char buffer[1024]; if (outlen <= 1) return 0; if (!radius_xlat(buffer, sizeof(buffer), fmt, request, NULL, NULL)) { *out = '\0'; return 0; } for (p = buffer, q = out; *p != '\0'; p++, outlen--) { if (outlen <= 1) break; *(q++) = toupper((int) *p); } *q = '\0'; return strlen(out); }
static int do_acctlog_acct(void *instance, REQUEST *request) { rlm_acctlog_t *inst; VALUE_PAIR *pair; char logstr[1024]; int acctstatustype = 0; inst = (rlm_acctlog_t*) instance; if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) { acctstatustype = pair->vp_integer; } else { radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL); radlog(L_ERR, "rlm_acctlog (%s)", logstr); return RLM_MODULE_INVALID; } switch (acctstatustype) { case PW_STATUS_START: radius_xlat(logstr, sizeof(logstr), inst->acctstart, request, NULL); break; case PW_STATUS_STOP: radius_xlat(logstr, sizeof(logstr), inst->acctstop, request, NULL); break; case PW_STATUS_ALIVE: radius_xlat(logstr, sizeof(logstr), inst->acctupdate, request, NULL); break; case PW_STATUS_ACCOUNTING_ON: radius_xlat(logstr, sizeof(logstr), inst->accton, request, NULL); break; case PW_STATUS_ACCOUNTING_OFF: radius_xlat(logstr, sizeof(logstr), inst->acctoff, request, NULL); break; default: *logstr = 0; } if (*logstr) radlog(L_ACCT,"%s", logstr); return RLM_MODULE_OK; }
/** * @brief Encode string as base64 * * Example: "%{tobase64:foo}" == "Zm9v" */ static size_t base64_xlat(UNUSED void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, UNUSED RADIUS_ESCAPE_STRING func) { size_t len; char buffer[1024]; len = radius_xlat(buffer, sizeof(buffer), fmt, request, func); /* * We can accurately calculate the length of the output string * if it's larger than outlen, the output would be useless so abort. */ if (!len || ((FR_BASE64_ENC_LENGTH(len) + 1) > outlen)) { radlog(L_ERR, "rlm_expr: xlat failed."); *out = '\0'; return 0; } fr_base64_encode((uint8_t *) buffer, len, out, outlen); return strlen(out); }
/* * Add the 'SQL-User-Name' attribute to the packet. */ static int sql_set_user(rlm_sql_log_t *inst, REQUEST *request, char *sqlusername, const char *username) { VALUE_PAIR *vp=NULL; char tmpuser[MAX_STRING_LEN]; tmpuser[0] = '\0'; sqlusername[0] = '\0'; rad_assert(request != NULL); rad_assert(request->packet != NULL); /* Remove any user attr we added previously */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); if (username != NULL) { strlcpy(tmpuser, username, MAX_STRING_LEN); } else if (inst->sql_user_name[0] != '\0') { radius_xlat(tmpuser, sizeof(tmpuser), inst->sql_user_name, request, NULL); } else { return 0; } if (tmpuser[0] != '\0') { strlcpy(sqlusername, tmpuser, sizeof(tmpuser)); RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername); vp = pairmake("SQL-User-Name", sqlusername, 0); if (vp == NULL) { radlog(L_ERR, "%s", fr_strerror()); return -1; } pairadd(&request->packet->vps, vp); return 0; } return -1; }
static int mongo_authorize(void *instance, REQUEST *request) { if (request->username == NULL) return RLM_MODULE_NOOP; rlm_mongo_t *data = (rlm_mongo_t *) instance; char password[MONGO_STRING_LENGTH] = ""; char mac[MONGO_STRING_LENGTH] = ""; if (strcmp(data->mac_field, "") != 0) { char mac_temp[MONGO_STRING_LENGTH] = ""; radius_xlat(mac_temp, MONGO_STRING_LENGTH, "%{Calling-Station-Id}", request, NULL); format_mac(mac_temp, mac); } if (!find_radius_options(data, request->username->vp_strvalue, mac, password)) { return RLM_MODULE_REJECT; } RDEBUG("Authorisation request by username -> \"%s\"\n", request->username->vp_strvalue); RDEBUG("Password found in MongoDB -> \"%s\"\n\n", password); VALUE_PAIR *vp; /* quiet the compiler */ instance = instance; request = request; vp = pairmake("Cleartext-Password", password, T_OP_SET); if (!vp) return RLM_MODULE_FAIL; pairmove(&request->config_items, &vp); pairfree(&vp); return RLM_MODULE_OK; }
/* * Do xlat of strings! */ static size_t expr_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, RADIUS_ESCAPE_STRING func) { int rcode; int64_t result; rlm_expr_t *inst = instance; const char *p; char buffer[256]; inst = inst; /* -Wunused */ /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) { radlog(L_ERR, "rlm_expr: xlat failed."); return 0; } p = buffer; rcode = get_number(request, &p, &result); if (rcode < 0) { return 0; } /* * We MUST have eaten the entire input string. */ if (*p != '\0') { RDEBUG2("Failed at %s", p); return 0; } snprintf(out, outlen, "%ld", (long int) result); return strlen(out); }
/** * @brief Convert a string to uppercase * * Example: "%{uc:Foo}" == "FOO" * * Probably only works for ASCII */ static size_t xlat_uc(UNUSED void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, UNUSED RADIUS_ESCAPE_STRING func) { char *p, *q; char buffer[1024]; if (outlen <= 1) return 0; if (!radius_xlat(buffer, sizeof(buffer), fmt, request, func)) { *out = '\0'; return 0; } for (p = buffer, q = out; *p != '\0'; p++, outlen--) { if (outlen <= 1) break; *(q++) = toupper((int) *p); } *q = '\0'; return strlen(out); }
/* * Generic function for failing between a bunch of queries. * * Uses the same principle as rlm_linelog, expanding the 'reference' config * item using xlat to figure out what query it should execute. * * If the reference matches multiple config items, and a query fails or * doesn't update any rows, the next matching config item is used. * */ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; char const *attr = NULL; char const *value; char path[MAX_STRING_LEN]; char *p = path; char *expanded = NULL; rad_assert(section); if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } item = cf_reference_item(NULL, section->cs, path); if (!item) { rcode = RLM_MODULE_FAIL; goto finish; } if (cf_item_is_section(item)){ REDEBUG("Sections are not supported as references"); rcode = RLM_MODULE_FAIL; goto finish; } pair = cf_itemtopair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = sql_get_socket(inst); if (!handle) { rcode = RLM_MODULE_FAIL; goto finish; } sql_set_user(inst, request, NULL); while (true) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; goto finish; } if (radius_axlat(&expanded, request, value, sql_escape_func, inst) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } if (!*expanded) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; talloc_free(expanded); goto finish; } rlm_sql_query_log(inst, request, section, expanded); /* * If rlm_sql_query cannot use the socket it'll try and * reconnect. Reconnecting will automatically release * the current socket, and try to select a new one. * * If we get RLM_SQL_RECONNECT it means all connections in the pool * were exhausted, and we couldn't create a new connection, * so we do not need to call sql_release_socket. */ sql_ret = rlm_sql_query(&handle, inst, expanded); TALLOC_FREE(expanded); if (sql_ret == RLM_SQL_RECONNECT) { rcode = RLM_MODULE_FAIL; goto finish; } rad_assert(handle); /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. * * @fixme We should actually be able to distinguish between key * constraint violations (which we expect) and other errors. */ if (sql_ret == RLM_SQL_OK) { numaffected = (inst->module->sql_affected_rows)(handle, inst->config); if (numaffected > 0) { break; /* A query succeeded, were done! */ } RDEBUG("No records updated"); } (inst->module->sql_finish_query)(handle, inst->config); /* * We assume all entries with the same name form a redundant * set of queries. */ pair = cf_pair_find_next(section->cs, pair, attr); if (!pair) { RDEBUG("No additional queries configured"); rcode = RLM_MODULE_NOOP; goto finish; } RDEBUG("Trying next query..."); } (inst->module->sql_finish_query)(handle, inst->config); finish: talloc_free(expanded); sql_release_socket(inst, handle); return rcode; }
/* * 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 int sqlcounter_authorize(void *instance, REQUEST *request) { rlm_sqlcounter_t *data = (rlm_sqlcounter_t *) instance; int ret=RLM_MODULE_NOOP; int counter=0; int res=0; DICT_ATTR *dattr; VALUE_PAIR *key_vp, *check_vp; VALUE_PAIR *reply_item; char msg[128]; char querystr[MAX_QUERY_LEN]; char responsestr[MAX_QUERY_LEN]; /* quiet the compiler */ instance = instance; request = request; /* * Before doing anything else, see if we have to reset * the counters. */ if (data->reset_time && (data->reset_time <= request->timestamp)) { /* * Re-set the next time and prev_time for this counters range */ data->last_reset = data->reset_time; find_next_reset(data,request->timestamp); } /* * Look for the key. User-Name is special. It means * The REAL username, after stripping. */ DEBUG2("rlm_sqlcounter: Entering module authorize code"); key_vp = (data->key_attr == PW_USER_NAME) ? request->username : pairfind(request->packet->vps, data->key_attr); if (key_vp == NULL) { DEBUG2("rlm_sqlcounter: Could not find Key value pair"); return ret; } /* * Look for the check item */ if ((dattr = dict_attrbyname(data->check_name)) == NULL) { return ret; } /* DEBUG2("rlm_sqlcounter: Found Check item attribute %d", dattr->attr); */ if ((check_vp= pairfind(request->config_items, dattr->attr)) == NULL) { DEBUG2("rlm_sqlcounter: Could not find Check item value pair"); return ret; } /* first, expand %k, %b and %e in query */ sqlcounter_expand(querystr, MAX_QUERY_LEN, data->query, instance); /* second, xlat any request attribs in query */ radius_xlat(responsestr, MAX_QUERY_LEN, querystr, request, sql_escape_func); /* third, wrap query with sql module & expand */ snprintf(querystr, sizeof(querystr), "%%{%%S:%s}", responsestr); sqlcounter_expand(responsestr, MAX_QUERY_LEN, querystr, instance); /* Finally, xlat resulting SQL query */ radius_xlat(querystr, MAX_QUERY_LEN, responsestr, request, sql_escape_func); counter = atoi(querystr); /* * Check if check item > counter */ res=check_vp->lvalue - counter; if (res > 0) { DEBUG2("rlm_sqlcounter: (Check item - counter) is greater than zero"); /* * We are assuming that simultaneous-use=1. But * even if that does not happen then our user * could login at max for 2*max-usage-time Is * that acceptable? */ /* * User is allowed, but set Session-Timeout. * Stolen from main/auth.c */ /* * If we are near a reset then add the next * limit, so that the user will not need to * login again */ if (data->reset_time && ( res >= (data->reset_time - request->timestamp))) { res = data->reset_time - request->timestamp; res += check_vp->lvalue; } if ((reply_item = pairfind(request->reply->vps, PW_SESSION_TIMEOUT)) != NULL) { if (reply_item->lvalue > res) reply_item->lvalue = res; } else { if ((reply_item = paircreate(PW_SESSION_TIMEOUT, PW_TYPE_INTEGER)) == NULL) { radlog(L_ERR|L_CONS, "no memory"); return RLM_MODULE_NOOP; } reply_item->lvalue = res; pairadd(&request->reply->vps, reply_item); } ret=RLM_MODULE_OK; DEBUG2("rlm_sqlcounter: Authorized user %s, check_item=%d, counter=%d", key_vp->strvalue,check_vp->lvalue,counter); DEBUG2("rlm_sqlcounter: Sent Reply-Item for user %s, Type=Session-Timeout, value=%d", key_vp->strvalue,reply_item->lvalue); } else{ char module_fmsg[MAX_STRING_LEN]; VALUE_PAIR *module_fmsg_vp; DEBUG2("rlm_sqlcounter: (Check item - counter) is less than zero"); /* * User is denied access, send back a reply message */ snprintf(msg, sizeof(msg), "Your maximum %s usage time has been reached", data->reset); reply_item=pairmake("Reply-Message", msg, T_OP_EQ); pairadd(&request->reply->vps, reply_item); snprintf(module_fmsg, sizeof(module_fmsg), "rlm_sqlcounter: Maximum %s usage time reached", data->reset); module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ); pairadd(&request->packet->vps, module_fmsg_vp); ret=RLM_MODULE_REJECT; DEBUG2("rlm_sqlcounter: Rejected user %s, check_item=%d, counter=%d", key_vp->strvalue,check_vp->lvalue,counter); } return ret; }
void radlog_request(int lvl, int priority, REQUEST *request, const char *msg, ...) { size_t len = 0; const char *filename = request_log_file; FILE *fp = NULL; va_list ap; char buffer[1024]; va_start(ap, msg); /* * Debug messages get treated specially. */ if (lvl == L_DBG) { /* * There is log function, but the debug level * isn't high enough. OR, we're in debug mode, * and the debug level isn't high enough. Return. */ if ((request && request->radlog && (priority > request->options)) || ((debug_flag != 0) && (priority > debug_flag))) { va_end(ap); return; } /* * Use the debug output file, if specified, * otherwise leave it as "request_log_file". */ filename = debug_log_file; if (!filename) filename = request_log_file; /* * Debug messages get mashed to L_INFO for * radius.log. */ if (!filename) lvl = L_INFO; } if (request && filename) { char *p; radlog_func_t rl = request->radlog; request->radlog = NULL; /* * This is SLOW! Doing it for every log message * in every request is NOT recommended! */ radius_xlat(buffer, sizeof(buffer), filename, request, NULL); /* FIXME: escape chars! */ request->radlog = rl; p = strrchr(buffer, FR_DIR_SEP); if (p) { *p = '\0'; if (rad_mkdir(buffer, S_IRWXU) < 0) { radlog(L_ERR, "Failed creating %s: %s", buffer,strerror(errno)); va_end(ap); return; } *p = FR_DIR_SEP; } fp = fopen(buffer, "a"); } /* * Print timestamps to the file. */ if (fp) { char *s; time_t timeval; timeval = time(NULL); CTIME_R(&timeval, buffer + len, sizeof(buffer) - len - 1); s = strrchr(buffer, '\n'); if (s) { s[0] = ' '; s[1] = '\0'; } s = fr_int2str(levels, (lvl & ~L_CONS), ": "); strcat(buffer, s); len = strlen(buffer); } if (request && request->module[0]) { snprintf(buffer + len, sizeof(buffer) + len, "[%s] ", request->module); len = strlen(buffer); } vsnprintf(buffer + len, sizeof(buffer) - len, msg, ap); if (!fp) { if (request) { radlog(lvl, "(%u) %s", request->number, buffer); } else { radlog(lvl, "%s", buffer); } } else { if (request) fprintf(fp, "(%u) ", request->number); fputs(buffer, fp); fputc('\n', fp); fclose(fp); } va_end(ap); }
static int do_linelog(void *instance, REQUEST *request) { int fd = -1; char buffer[4096]; char line[1024]; rlm_linelog_t *inst = (rlm_linelog_t*) instance; const char *value = inst->line; if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; radius_xlat(line + 1, sizeof(line) - 2, inst->reference, request, linelog_escape_func); line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_itemtopair(ci); value = cf_pair_value(cp); if (!value) { RDEBUG2("Entry \"%s\" has no value", line); goto do_log; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (strcmp(inst->filename, "syslog") != 0) { radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL); fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, 0600); if (fd == -1) { radlog(L_ERR, "rlm_linelog: Failed to open %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } } /* * FIXME: Check length. */ radius_xlat(line, sizeof(line) - 1, value, request, linelog_escape_func); if (fd >= 0) { strcat(line, "\n"); write(fd, line, strlen(line)); close(fd); #ifdef HAVE_SYSLOG_H } else { syslog(LOG_INFO, "%s", line); #endif } return RLM_MODULE_OK; }
/* * Do caching checks. Since we can update ANY VP list, we do * exactly the same thing for all sections (autz / auth / etc.) * * If you want to cache something different in different sections, * configure another cache module. */ static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(void *instance, REQUEST *request) { rlm_cache_entry_t *c; rlm_cache_t *inst = instance; rlm_cache_handle_t *handle; vp_cursor_t cursor; VALUE_PAIR *vp; char buffer[1024]; rlm_rcode_t rcode; int ttl = inst->ttl; if (radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL) < 0) return RLM_MODULE_FAIL; if (buffer[0] == '\0') { REDEBUG("Zero length key string is invalid"); return RLM_MODULE_INVALID; } if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL; rcode = cache_find(&c, inst, request, &handle, buffer); if (rcode == RLM_MODULE_FAIL) goto finish; rad_assert(handle); /* * If Cache-Status-Only == yes, only return whether we found a * valid cache entry */ vp = pairfind(request->config_items, PW_CACHE_STATUS_ONLY, 0, TAG_ANY); if (vp && vp->vp_integer) { rcode = c ? RLM_MODULE_OK: RLM_MODULE_NOTFOUND; goto finish; } /* * Update the expiry time based on the TTL. * A TTL of 0 means "delete from the cache". * A TTL < 0 means "delete from the cache and recreate the entry". */ vp = pairfind(request->config_items, PW_CACHE_TTL, 0, TAG_ANY); if (vp) ttl = vp->vp_signed; /* * If there's no existing cache entry, go and create a new one. */ if (!c) { if (ttl <= 0) ttl = inst->ttl; goto insert; } /* * Expire the entry if requested to do so */ if (vp) { if (ttl == 0) { cache_expire(inst, request, &handle, &c); RDEBUG("Forcing expiry of entry"); rcode = RLM_MODULE_OK; goto finish; } if (ttl < 0) { RDEBUG("Forcing expiry of existing entry"); cache_expire(inst, request, &handle, &c); ttl *= -1; goto insert; } c->expires = request->timestamp + ttl; RDEBUG("Setting TTL to %d", ttl); } /* * Cache entry was still valid, so we merge it into the request * and return. No need to add a new entry. */ cache_merge(inst, request, c); rcode = RLM_MODULE_UPDATED; goto finish; insert: /* * If Cache-Read-Only == yes, then we only allow already cached entries * to be merged into the request */ vp = pairfind(request->config_items, PW_CACHE_READ_ONLY, 0, TAG_ANY); if (vp && vp->vp_integer) { rcode = RLM_MODULE_NOTFOUND; goto finish; } /* * Create a new entry. */ rcode = cache_insert(inst, request, &handle, buffer, ttl); rad_assert(handle); finish: cache_free(inst, &c); cache_release(inst, request, &handle); /* * Clear control attributes */ for (vp = fr_cursor_init(&cursor, &request->config_items); vp; vp = fr_cursor_next(&cursor)) { if (vp->da->vendor == 0) switch (vp->da->attr) { case PW_CACHE_TTL: case PW_CACHE_STATUS_ONLY: case PW_CACHE_READ_ONLY: case PW_CACHE_MERGE: vp = fr_cursor_remove(&cursor); talloc_free(vp); break; } } return rcode; }
/** * @brief Move pairs, replacing/over-writing them, and doing xlat. * * Move attributes from one list to the other * if not already present. */ void pairxlatmove(REQUEST *req, VALUE_PAIR **to, VALUE_PAIR **from) { VALUE_PAIR **tailto, *i, *j, *next; VALUE_PAIR *tailfrom = NULL; VALUE_PAIR *found; /* * Point "tailto" to the end of the "to" list. */ tailto = to; for(i = *to; i; i = i->next) { tailto = &i->next; } /* * Loop over the "from" list. */ for(i = *from; i; i = next) { next = i->next; /* * Don't move 'fallthrough' over. */ if (i->attribute == PW_FALL_THROUGH) { tailfrom = i; continue; } /* * We've got to xlat the string before moving * it over. */ if (i->flags.do_xlat) { int rcode; char buffer[sizeof(i->vp_strvalue)]; i->flags.do_xlat = 0; rcode = radius_xlat(buffer, sizeof(buffer), i->vp_strvalue, req, NULL); /* * Parse the string into a new value. */ pairparsevalue(i, buffer); } found = pairfind(*to, i->attribute, i->vendor); switch (i->operator) { /* * If a similar attribute is found, * delete it. */ case T_OP_SUB: /* -= */ if (found) { if (!i->vp_strvalue[0] || (strcmp((char *)found->vp_strvalue, (char *)i->vp_strvalue) == 0)){ pairdelete(to, found->attribute, found->vendor); /* * 'tailto' may have been * deleted... */ tailto = to; for(j = *to; j; j = j->next) { tailto = &j->next; } } } tailfrom = i; continue; break; /* * Add it, if it's not already there. */ case T_OP_EQ: /* = */ if (found) { tailfrom = i; continue; /* with the loop */ } break; /* * If a similar attribute is found, * replace it with the new one. Otherwise, * add the new one to the list. */ case T_OP_SET: /* := */ if (found) { VALUE_PAIR *vp; vp = found->next; memcpy(found, i, sizeof(*found)); found->next = vp; tailfrom = i; continue; } break; /* * FIXME: Add support for <=, >=, <, > * * which will mean (for integers) * 'make the attribute the smaller, etc' */ /* * Add the new element to the list, even * if similar ones already exist. */ default: case T_OP_ADD: /* += */ break; } if (tailfrom) tailfrom->next = next; else *from = next; /* * If ALL of the 'to' attributes have been deleted, * then ensure that the 'tail' is updated to point * to the head. */ if (!*to) { tailto = to; } *tailto = i; if (i) { i->next = NULL; tailto = &i->next; } } /* loop over the 'from' list */ }
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; int ldap_errno; int i; ldap_instance_t *inst = instance; struct berval **values; VALUE_PAIR *vp; ldap_handle_t *conn; LDAPMessage *result, *entry; char const *dn = NULL; rlm_ldap_map_xlat_t expanded; /* faster than mallocing every time */ if (!request->username) { RDEBUG2("Attribute \"User-Name\" is required for authorization"); return RLM_MODULE_NOOP; } /* * Check for valid input, zero length names not permitted */ if (request->username->vp_length == 0) { RDEBUG2("Zero length username not permitted"); return RLM_MODULE_INVALID; } if (rlm_ldap_map_xlat(request, inst->user_map, &expanded) < 0) { return RLM_MODULE_FAIL; } conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; /* * Add any additional attributes we need for checking access, memberships, and profiles */ if (inst->userobj_access_attr) { expanded.attrs[expanded.count++] = inst->userobj_access_attr; } if (inst->userobj_membership_attr && (inst->cacheable_group_dn || inst->cacheable_group_name)) { expanded.attrs[expanded.count++] = inst->userobj_membership_attr; } if (inst->profile_attr) { expanded.attrs[expanded.count++] = inst->profile_attr; } if (inst->valuepair_attr) { expanded.attrs[expanded.count++] = inst->valuepair_attr; } expanded.attrs[expanded.count] = NULL; dn = rlm_ldap_find_user(inst, request, &conn, expanded.attrs, true, &result, &rcode); if (!dn) { goto finish; } entry = ldap_first_entry(conn->handle, result); if (!entry) { ldap_get_option(conn->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); goto finish; } /* * Check for access. */ if (inst->userobj_access_attr) { rcode = rlm_ldap_check_access(inst, request, conn, entry); if (rcode != RLM_MODULE_OK) { goto finish; } } /* * Check if we need to cache group memberships */ if (inst->cacheable_group_dn || inst->cacheable_group_name) { if (inst->userobj_membership_attr) { rcode = rlm_ldap_cacheable_userobj(inst, request, &conn, entry, inst->userobj_membership_attr); if (rcode != RLM_MODULE_OK) { goto finish; } } rcode = rlm_ldap_cacheable_groupobj(inst, request, &conn); if (rcode != RLM_MODULE_OK) { goto finish; } } #ifdef WITH_EDIR /* * We already have a Cleartext-Password. Skip edir. */ if (pairfind(request->config_items, PW_CLEARTEXT_PASSWORD, 0, TAG_ANY)) { goto skip_edir; } /* * Retrieve Universal Password if we use eDirectory */ if (inst->edir) { int res = 0; char password[256]; size_t pass_size = sizeof(password); /* * Retrive universal password */ res = nmasldap_get_password(conn->handle, dn, password, &pass_size); if (res != 0) { REDEBUG("Failed to retrieve eDirectory password: (%i) %s", res, edir_errstr(res)); rcode = RLM_MODULE_FAIL; goto finish; } /* * Add Cleartext-Password attribute to the request */ vp = radius_paircreate(request, &request->config_items, PW_CLEARTEXT_PASSWORD, 0); pairstrcpy(vp, password); vp->vp_length = pass_size; if (RDEBUG_ENABLED3) { RDEBUG3("Added eDirectory password. control:%s += '%s'", vp->da->name, vp->vp_strvalue); } else { RDEBUG2("Added eDirectory password"); } if (inst->edir_autz) { RDEBUG2("Binding as user for eDirectory authorization checks"); /* * Bind as the user */ conn->rebound = true; status = rlm_ldap_bind(inst, request, &conn, dn, vp->vp_strvalue, true); switch (status) { case LDAP_PROC_SUCCESS: rcode = RLM_MODULE_OK; RDEBUG("Bind as user '%s' was successful", dn); break; case LDAP_PROC_NOT_PERMITTED: rcode = RLM_MODULE_USERLOCK; goto finish; case LDAP_PROC_REJECT: rcode = RLM_MODULE_REJECT; goto finish; case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; goto finish; case LDAP_PROC_NO_RESULT: rcode = RLM_MODULE_NOTFOUND; goto finish; default: rcode = RLM_MODULE_FAIL; goto finish; }; } } skip_edir: #endif /* * Apply ONE user profile, or a default user profile. */ if (inst->default_profile) { char profile[1024]; if (radius_xlat(profile, sizeof(profile), request, inst->default_profile, NULL, NULL) < 0) { REDEBUG("Failed creating default profile string"); rcode = RLM_MODULE_INVALID; goto finish; } rlm_ldap_map_profile(inst, request, &conn, profile, &expanded); } /* * Apply a SET of user profiles. */ if (inst->profile_attr) { values = ldap_get_values_len(conn->handle, entry, inst->profile_attr); if (values != NULL) { for (i = 0; values[i] != NULL; i++) { char *value; value = rlm_ldap_berval_to_string(request, values[i]); rlm_ldap_map_profile(inst, request, &conn, value, &expanded); talloc_free(value); } ldap_value_free_len(values); } } if (inst->user_map || inst->valuepair_attr) { RDEBUG("Processing user attributes"); RINDENT(); rlm_ldap_map_do(inst, request, conn->handle, &expanded, entry); REXDENT(); rlm_ldap_check_reply(inst, request); } finish: rlm_ldap_map_xlat_free(&expanded); if (result) ldap_msgfree(result); mod_conn_release(inst, conn); return rcode; }
/** Expand values in an attribute map where needed * */ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded) { value_pair_map_t const *map; unsigned int total = 0; VALUE_PAIR *found, **from = NULL; REQUEST *context; for (map = maps; map != NULL; map = map->next) { switch (map->src->type) { case VPT_TYPE_XLAT: { ssize_t len; char *exp = NULL; len = radius_xlat(exp, 0, request, map->src->name, NULL, NULL); if (len < 0) { RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name); goto error; } expanded->attrs[total++] = exp; break; } case VPT_TYPE_ATTR: context = request; if (radius_request(&context, map->src->vpt_request) == 0) { from = radius_list(context, map->src->vpt_list); } if (!from) continue; found = pairfind(*from, map->src->vpt_da->attr, map->src->vpt_da->vendor, TAG_ANY); if (!found) continue; expanded->attrs[total++] = talloc_typed_strdup(request, found->vp_strvalue); break; case VPT_TYPE_EXEC: { char answer[1024]; VALUE_PAIR **input_pairs = NULL; int result; input_pairs = radius_list(request, PAIR_LIST_REQUEST); result = radius_exec_program(request, map->src->name, true, true, answer, sizeof(answer), EXEC_TIMEOUT, input_pairs ? *input_pairs : NULL, NULL); if (result != 0) { return -1; } expanded->attrs[total++] = talloc_typed_strdup(request, answer); } break; case VPT_TYPE_LITERAL: expanded->attrs[total++] = map->src->name; break; default: rad_assert(0); error: expanded->attrs[total] = NULL; rlm_ldap_map_xlat_free(expanded); return -1; } } rad_assert(total < LDAP_MAX_ATTRMAP); expanded->attrs[total] = NULL; expanded->count = total; expanded->maps = maps; return 0; }
/* * Make sure user/pass are clean * and then log them */ static int rad_authlog(const char *msg, REQUEST *request, int goodpass) { int logit; const char *extra_msg = NULL; char clean_password[1024]; char clean_username[1024]; char buf[1024]; char extra[1024]; VALUE_PAIR *username = NULL; if (!request->root->log_auth) { return 0; } /* * Get the correct username based on the configured value */ if (log_stripped_names == 0) { username = pairfind(request->packet->vps, PW_USER_NAME); } else { username = request->username; } /* * Clean up the username */ if (username == NULL) { strcpy(clean_username, "<no User-Name attribute>"); } else { fr_print_string((char *)username->vp_strvalue, username->length, clean_username, sizeof(clean_username)); } /* * Clean up the password */ if (request->root->log_auth_badpass || request->root->log_auth_goodpass) { if (!request->password) { VALUE_PAIR *auth_type; auth_type = pairfind(request->config_items, PW_AUTH_TYPE); if (auth_type) { snprintf(clean_password, sizeof(clean_password), "<via Auth-Type = %s>", dict_valnamebyattr(PW_AUTH_TYPE, auth_type->vp_integer)); } else { strcpy(clean_password, "<no User-Password attribute>"); } } else if (pairfind(request->packet->vps, PW_CHAP_PASSWORD)) { strcpy(clean_password, "<CHAP-Password>"); } else { fr_print_string((char *)request->password->vp_strvalue, request->password->length, clean_password, sizeof(clean_password)); } } if (goodpass) { logit = request->root->log_auth_goodpass; extra_msg = request->root->auth_goodpass_msg; } else { logit = request->root->log_auth_badpass; extra_msg = request->root->auth_badpass_msg; } if (extra_msg) { extra[0] = ' '; radius_xlat(extra + 1, sizeof(extra) - 1, extra_msg, request, NULL); } else { *extra = '\0'; } radlog_request(L_AUTH, 0, request, "%s: [%s%s%s] (%s)%s", msg, clean_username, logit ? "/" : "", logit ? clean_password : "", auth_name(buf, sizeof(buf), request, 1), extra); return 0; }
/** 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_rcode_t status; 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)) { bool 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_axlat(&exp, 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 = mod_conn_get(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; } status = rlm_ldap_modify(inst, request, &conn, dn, modify); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_REJECT: case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; break; default: rcode = RLM_MODULE_FAIL; break; }; release: error: /* * Free up any buffers we allocated for xlat expansion */ for (i = 0; i < last_exp; i++) { talloc_free(expanded[i]); } mod_conn_release(inst, conn); return rcode; }
static rlm_rcode_t do_linelog(void *instance, REQUEST *request) { int fd = -1; char buffer[4096]; char *p; char line[1024]; rlm_linelog_t *inst = (rlm_linelog_t*) instance; const char *value = inst->line; #ifdef HAVE_GRP_H gid_t gid; struct group *grp; char *endptr; #endif if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; radius_xlat(line + 1, sizeof(line) - 2, inst->reference, request, linelog_escape_func, NULL); line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_itemtopair(ci); value = cf_pair_value(cp); if (!value) { RDEBUG2("Entry \"%s\" has no value", line); goto do_log; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (strcmp(inst->filename, "syslog") != 0) { radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL, NULL); /* check path and eventually create subdirs */ p = strrchr(buffer,'/'); if (p) { *p = '\0'; if (rad_mkdir(buffer, 0700) < 0) { radlog_request(L_ERR, 0, request, "rlm_linelog: Failed to create directory %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } *p = '/'; } fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, inst->permissions); if (fd == -1) { radlog(L_ERR, "rlm_linelog: Failed to open %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } #ifdef HAVE_GRP_H if (inst->group != NULL) { gid = strtol(inst->group, &endptr, 10); if (*endptr != '\0') { grp = getgrnam(inst->group); if (grp == NULL) { RDEBUG2("Unable to find system group \"%s\"", inst->group); goto skip_group; } gid = grp->gr_gid; } if (chown(buffer, -1, gid) == -1) { RDEBUG2("Unable to change system group of \"%s\"", buffer); } } #endif } skip_group: /* * FIXME: Check length. */ radius_xlat(line, sizeof(line) - 1, value, request, linelog_escape_func, NULL); if (fd >= 0) { strcat(line, "\n"); write(fd, line, strlen(line)); close(fd); #ifdef HAVE_SYSLOG_H } else { syslog(inst->facility, "%s", line); #endif } return RLM_MODULE_OK; }
/** * @brief 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 req Current request * @param request request valuepairs * @param check check/control valuepairs * @param[in,out] reply reply value pairs * * @return 0 on match. */ int paircompare(REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR **reply) { VALUE_PAIR *check_item; VALUE_PAIR *auth_item; int result = 0; int compare; int other; for (check_item = check; check_item != NULL; check_item = check_item->next) { /* * 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->operator == T_OP_SET) || (check_item->operator == T_OP_ADD)) { continue; } switch (check_item->attribute) { /* * 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->operator == T_OP_CMP_EQ) { DEBUG("WARNING: Found User-Password == \"...\"."); DEBUG("WARNING: Are you sure you don't mean Cleartext-Password?"); DEBUG("WARNING: See \"man rlm_pap\" for more information."); } if (pairfind(request, PW_USER_PASSWORD, 0) == NULL) { continue; } break; } /* * See if this item is present in the request. */ other = otherattr(check_item->attribute); auth_item = request; try_again: if (other >= 0) { for (; auth_item != NULL; auth_item = auth_item->next) { if (auth_item->attribute == (unsigned int) other || other == 0) break; } } /* * 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->operator == 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->operator == T_OP_CMP_FALSE) return -1; /* * We've got to xlat the string before doing * the comparison. */ if (check_item->flags.do_xlat) { int rcode; char buffer[sizeof(check_item->vp_strvalue)]; check_item->flags.do_xlat = 0; rcode = radius_xlat(buffer, sizeof(buffer), check_item->vp_strvalue, req, NULL); /* * Parse the string into a new value. */ pairparsevalue(check_item, buffer); } /* * OK it is present now compare them. */ compare = radius_callback_compare(req, auth_item, check_item, check, reply); switch (check_item->operator) { case T_OP_EQ: default: radlog(L_INFO, "Invalid operator for item %s: " "reverting to '=='", check_item->name); /*FALLTHRU*/ case T_OP_CMP_TRUE: /* compare always == 0 */ case T_OP_CMP_FALSE: /* compare always == 1 */ 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) && (other >= 0)) { auth_item = auth_item->next; result = 0; goto try_again; } } /* for every entry in the check item list */ return result; }
/* * Before trusting a certificate, you must make sure that the * certificate is 'valid'. There are several steps that your * application can take in determining if a certificate is * valid. Commonly used steps are: * * 1.Verifying the certificate's signature, and verifying that * the certificate has been issued by a trusted Certificate * Authority. * * 2.Verifying that the certificate is valid for the present date * (i.e. it is being presented within its validity dates). * * 3.Verifying that the certificate has not been revoked by its * issuing Certificate Authority, by checking with respect to a * Certificate Revocation List (CRL). * * 4.Verifying that the credentials presented by the certificate * fulfill additional requirements specific to the application, * such as with respect to access control lists or with respect * to OCSP (Online Certificate Status Processing). * * NOTE: This callback will be called multiple times based on the * depth of the root certificate chain */ static int cbtls_verify(int ok, X509_STORE_CTX *ctx) { char subject[1024]; /* Used for the subject name */ char issuer[1024]; /* Used for the issuer name */ char common_name[1024]; char cn_str[1024]; char buf[64]; EAP_HANDLER *handler = NULL; X509 *client_cert; X509 *issuer_cert; SSL *ssl; int err, depth, lookup; EAP_TLS_CONF *conf; int my_ok = ok; REQUEST *request; ASN1_INTEGER *sn = NULL; ASN1_TIME *asn_time = NULL; #ifdef HAVE_OPENSSL_OCSP_H X509_STORE *ocsp_store = NULL; #endif client_cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); lookup = depth; /* * Log client/issuing cert. If there's an error, log * issuing cert. */ if ((lookup > 1) && !my_ok) lookup = 1; /* * Retrieve the pointer to the SSL of the connection currently treated * and the application specific data stored into the SSL object. */ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); handler = (EAP_HANDLER *)SSL_get_ex_data(ssl, 0); request = handler->request; conf = (EAP_TLS_CONF *)SSL_get_ex_data(ssl, 1); #ifdef HAVE_OPENSSL_OCSP_H ocsp_store = (X509_STORE *)SSL_get_ex_data(ssl, 2); #endif /* * Get the Serial Number */ buf[0] = '\0'; sn = X509_get_serialNumber(client_cert); /* * For this next bit, we create the attributes *only* if * we're at the client or issuing certificate. */ if ((lookup <= 1) && sn && (sn->length < (sizeof(buf) / 2))) { char *p = buf; int i; for (i = 0; i < sn->length; i++) { sprintf(p, "%02x", (unsigned int)sn->data[i]); p += 2; } pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_SERIAL][lookup], buf, T_OP_SET)); } /* * Get the Expiration Date */ buf[0] = '\0'; asn_time = X509_get_notAfter(client_cert); if ((lookup <= 1) && asn_time && (asn_time->length < MAX_STRING_LEN)) { memcpy(buf, (char*) asn_time->data, asn_time->length); buf[asn_time->length] = '\0'; pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_EXPIRATION][lookup], buf, T_OP_SET)); } /* * Get the Subject & Issuer */ subject[0] = issuer[0] = '\0'; X509_NAME_oneline(X509_get_subject_name(client_cert), subject, sizeof(subject)); subject[sizeof(subject) - 1] = '\0'; if ((lookup <= 1) && subject[0] && (strlen(subject) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_SUBJECT][lookup], subject, T_OP_SET)); } X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), issuer, sizeof(issuer)); issuer[sizeof(issuer) - 1] = '\0'; if ((lookup <= 1) && issuer[0] && (strlen(issuer) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_ISSUER][lookup], issuer, T_OP_SET)); } /* * Get the Common Name */ X509_NAME_get_text_by_NID(X509_get_subject_name(client_cert), NID_commonName, common_name, sizeof(common_name)); common_name[sizeof(common_name) - 1] = '\0'; if ((lookup <= 1) && common_name[0] && (strlen(common_name) < MAX_STRING_LEN)) { pairadd(&handler->certs, pairmake(cert_attr_names[EAPTLS_CN][lookup], common_name, T_OP_SET)); } /* * If the CRL has expired, that might still be OK. */ if (!my_ok && (conf->allow_expired_crl) && (err == X509_V_ERR_CRL_HAS_EXPIRED)) { my_ok = 1; X509_STORE_CTX_set_error( ctx, 0 ); } if (!my_ok) { const char *p = X509_verify_cert_error_string(err); radlog(L_ERR,"--> verify error:num=%d:%s\n",err, p); radius_pairmake(request, &request->packet->vps, "Module-Failure-Message", p, T_OP_SET); return my_ok; } switch (ctx->error) { case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: radlog(L_ERR, "issuer= %s\n", issuer); break; case X509_V_ERR_CERT_NOT_YET_VALID: case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: radlog(L_ERR, "notBefore="); #if 0 ASN1_TIME_print(bio_err, X509_get_notBefore(ctx->current_cert)); #endif break; case X509_V_ERR_CERT_HAS_EXPIRED: case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: radlog(L_ERR, "notAfter="); #if 0 ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert)); #endif break; } /* * If we're at the actual client cert, apply additional * checks. */ if (depth == 0) { /* * If the conf tells us to, check cert issuer * against the specified value and fail * verification if they don't match. */ if (conf->check_cert_issuer && (strcmp(issuer, conf->check_cert_issuer) != 0)) { radlog(L_AUTH, "rlm_eap_tls: Certificate issuer (%s) does not match specified value (%s)!", issuer, conf->check_cert_issuer); my_ok = 0; } /* * If the conf tells us to, check the CN in the * cert against xlat'ed value, but only if the * previous checks passed. */ if (my_ok && conf->check_cert_cn) { if (!radius_xlat(cn_str, sizeof(cn_str), conf->check_cert_cn, handler->request, NULL)) { radlog(L_ERR, "rlm_eap_tls (%s): xlat failed.", conf->check_cert_cn); /* if this fails, fail the verification */ my_ok = 0; } else { RDEBUG2("checking certificate CN (%s) with xlat'ed value (%s)", common_name, cn_str); if (strcmp(cn_str, common_name) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) does not match specified value (%s)!", common_name, cn_str); my_ok = 0; } } } /* check_cert_cn */ #ifdef HAVE_OPENSSL_OCSP_H if (my_ok && conf->ocsp_enable){ RDEBUG2("--> Starting OCSP Request"); if(X509_STORE_CTX_get1_issuer(&issuer_cert, ctx, client_cert)!=1) { radlog(L_ERR, "Error: Couldn't get issuer_cert for %s", common_name); } my_ok = ocsp_check(ocsp_store, issuer_cert, client_cert, conf); } #endif while (conf->verify_client_cert_cmd) { char filename[256]; int fd; FILE *fp; snprintf(filename, sizeof(filename), "%s/%s.client.XXXXXXXX", conf->verify_tmp_dir, progname); fd = mkstemp(filename); if (fd < 0) { RDEBUG("Failed creating file in %s: %s", conf->verify_tmp_dir, strerror(errno)); break; } fp = fdopen(fd, "w"); if (!fp) { RDEBUG("Failed opening file %s: %s", filename, strerror(errno)); break; } if (!PEM_write_X509(fp, client_cert)) { fclose(fp); RDEBUG("Failed writing certificate to file"); goto do_unlink; } fclose(fp); if (!radius_pairmake(request, &request->packet->vps, "TLS-Client-Cert-Filename", filename, T_OP_SET)) { RDEBUG("Failed creating TLS-Client-Cert-Filename"); goto do_unlink; } RDEBUG("Verifying client certificate: %s", conf->verify_client_cert_cmd); if (radius_exec_program(conf->verify_client_cert_cmd, request, 1, NULL, 0, request->packet->vps, NULL, 1) != 0) { radlog(L_AUTH, "rlm_eap_tls: Certificate CN (%s) fails external verification!", common_name); my_ok = 0; } else { RDEBUG("Client certificate CN %s passed external validation", common_name); } do_unlink: unlink(filename); break; } } /* depth == 0 */ if (debug_flag > 0) { RDEBUG2("chain-depth=%d, ", depth); RDEBUG2("error=%d", err); RDEBUG2("--> User-Name = %s", handler->identity); RDEBUG2("--> BUF-Name = %s", common_name); RDEBUG2("--> subject = %s", subject); RDEBUG2("--> issuer = %s", issuer); RDEBUG2("--> verify return:%d", my_ok); } return my_ok; }
/** * @brief Compares check and vp by value. Does not call any per-attribute * comparison function, but does honour check.operator * * This function basically does "vp.value check.op check.value" * * @param request Current request * @param check rvalue, and operator * @param vp lvalue */ int radius_compare_vps(REQUEST *request, VALUE_PAIR *check, VALUE_PAIR *vp) { int ret = -2; /* * Check for =* and !* and return appropriately */ if( check->operator == T_OP_CMP_TRUE ) return 0; if( check->operator == T_OP_CMP_FALSE ) return 1; #ifdef HAVE_REGEX_H if (check->operator == T_OP_REG_EQ) { int i, compare; regex_t reg; char name[1024]; char value[1024]; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; snprintf(name, sizeof(name), "%%{%s}", check->name); radius_xlat(value, sizeof(value), name, request, NULL); /* * Include substring matches. */ compare = regcomp(®, check->vp_strvalue, REG_EXTENDED); if (compare != 0) { char buffer[256]; regerror(compare, ®, buffer, sizeof(buffer)); RDEBUG("Invalid regular expression %s: %s", check->vp_strvalue, buffer); return -1; } compare = regexec(®, value, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); /* * Add %{0}, %{1}, etc. */ for (i = 0; i <= REQUEST_MAX_REGEX; i++) { char *p; char buffer[sizeof(check->vp_strvalue)]; /* * Didn't match: delete old * match, if it existed. */ if ((compare != 0) || (rxmatch[i].rm_so == -1)) { p = request_data_get(request, request, REQUEST_DATA_REGEX | i); if (p) { free(p); continue; } /* * No previous match * to delete, stop. */ break; } /* * Copy substring into buffer. */ memcpy(buffer, value + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); buffer[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; /* * Copy substring, and add it to * the request. * * Note that we don't check * for out of memory, which is * the only error we can get... */ p = strdup(buffer); request_data_add(request, request, REQUEST_DATA_REGEX | i, p, free); } if (compare == 0) return 0; return -1; } if (check->operator == T_OP_REG_NE) { int compare; regex_t reg; char name[1024]; char value[1024]; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; snprintf(name, sizeof(name), "%%{%s}", check->name); radius_xlat(value, sizeof(value), name, request, NULL); /* * Include substring matches. */ compare = regcomp(®, (char *)check->vp_strvalue, REG_EXTENDED); if (compare != 0) { char buffer[256]; regerror(compare, ®, buffer, sizeof(buffer)); RDEBUG("Invalid regular expression %s: %s", check->vp_strvalue, buffer); return -1; } compare = regexec(®, value, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); if (compare != 0) return 0; return -1; } #endif /* * Tagged attributes are equal if and only if both the * tag AND value match. */ if (check->flags.has_tag) { ret = ((int) vp->flags.tag) - ((int) check->flags.tag); if (ret != 0) return ret; } /* * Not a regular expression, compare the types. */ switch(check->type) { #ifdef ASCEND_BINARY /* * Ascend binary attributes can be treated * as opaque objects, I guess... */ case PW_TYPE_ABINARY: #endif case PW_TYPE_OCTETS: if (vp->length != check->length) { ret = 1; /* NOT equal */ break; } ret = memcmp(vp->vp_strvalue, check->vp_strvalue, vp->length); break; case PW_TYPE_STRING: ret = strcmp((char *)vp->vp_strvalue, (char *)check->vp_strvalue); break; case PW_TYPE_BYTE: case PW_TYPE_SHORT: case PW_TYPE_INTEGER: ret = vp->vp_integer - check->vp_integer; break; case PW_TYPE_INTEGER64: /* * Don't want integer overflow! */ if (vp->vp_integer64 < check->vp_integer64) { ret = -1; } else if (vp->vp_integer64 > check->vp_integer64) { ret = +1; } else { ret = 0; } break; case PW_TYPE_DATE: ret = vp->vp_date - check->vp_date; break; case PW_TYPE_IPADDR: ret = ntohl(vp->vp_ipaddr) - ntohl(check->vp_ipaddr); break; case PW_TYPE_IPV6ADDR: ret = memcmp(&vp->vp_ipv6addr, &check->vp_ipv6addr, sizeof(vp->vp_ipv6addr)); break; case PW_TYPE_IPV6PREFIX: ret = memcmp(&vp->vp_ipv6prefix, &check->vp_ipv6prefix, sizeof(vp->vp_ipv6prefix)); break; case PW_TYPE_IFID: ret = memcmp(&vp->vp_ifid, &check->vp_ifid, sizeof(vp->vp_ifid)); break; default: break; } return ret; }
/* * Allocate an IP number from the pool. */ static int sqlippool_postauth(void *instance, REQUEST * request) { rlm_sqlippool_t * data = (rlm_sqlippool_t *) instance; char allocation[MAX_STRING_LEN]; int allocation_len; uint32_t ip_allocation; VALUE_PAIR * vp; SQLSOCK * sqlsocket; fr_ipaddr_t ipaddr; char logstr[MAX_STRING_LEN]; char sqlusername[MAX_STRING_LEN]; /* * If there is a Framed-IP-Address attribute in the reply do nothing */ if (pairfind(request->reply->vps, PW_FRAMED_IP_ADDRESS, 0) != NULL) { /* We already have a Framed-IP-Address */ radius_xlat(logstr, sizeof(logstr), data->log_exists, request, NULL, NULL); RDEBUG("Framed-IP-Address already exists"); return do_logging(logstr, RLM_MODULE_NOOP); } if (pairfind(request->config_items, PW_POOL_NAME, 0) == NULL) { RDEBUG("No Pool-Name defined."); radius_xlat(logstr, sizeof(logstr), data->log_nopool, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOOP); } sqlsocket = data->sql_inst->sql_get_socket(data->sql_inst); if (sqlsocket == NULL) { RDEBUG("cannot allocate sql connection"); return RLM_MODULE_FAIL; } if (data->sql_inst->sql_set_user(data->sql_inst, request, sqlusername, NULL) < 0) { return RLM_MODULE_FAIL; } /* * BEGIN */ sqlippool_command(data->allocate_begin, sqlsocket, data, request, (char *) NULL, 0); /* * CLEAR */ sqlippool_command(data->allocate_clear, sqlsocket, data, request, (char *) NULL, 0); /* * FIND */ allocation_len = sqlippool_query1(allocation, sizeof(allocation), data->allocate_find, sqlsocket, data, request, (char *) NULL, 0); /* * Nothing found... */ if (allocation_len == 0) { /* * COMMIT */ sqlippool_command(data->allocate_commit, sqlsocket, instance, request, (char *) NULL, 0); /* * Should we perform pool-check ? */ if (data->pool_check && *data->pool_check) { /* * Ok, so the allocate-find query found nothing ... * Let's check if the pool exists at all */ allocation_len = sqlippool_query1(allocation, sizeof(allocation), data->pool_check, sqlsocket, data, request, (char *) NULL, 0); data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); if (allocation_len) { /* * Pool exists after all... So, * the failure to allocate the IP * address was most likely due to * the depletion of the pool. In * that case, we should return * NOTFOUND */ RDEBUG("pool appears to be full"); radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOTFOUND); } /* * Pool doesn't exist in the table. It * may be handled by some other instance of * sqlippool, so we should just ignore this * allocation failure and return NOOP */ RDEBUG("IP address could not be allocated as no pool exists with that name."); return RLM_MODULE_NOOP; } data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); RDEBUG("IP address could not be allocated."); radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOOP); } /* * FIXME: Make it work with the ipv6 addresses */ if ((ip_hton(allocation, AF_INET, &ipaddr) < 0) || ((ip_allocation = ipaddr.ipaddr.ip4addr.s_addr) == INADDR_NONE)) { /* * COMMIT */ sqlippool_command(data->allocate_commit, sqlsocket, instance, request, (char *) NULL, 0); RDEBUG("Invalid IP number [%s] returned from database query.", allocation); data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); radius_xlat(logstr, sizeof(logstr), data->log_failed, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_NOOP); } /* * UPDATE */ sqlippool_command(data->allocate_update, sqlsocket, data, request, allocation, allocation_len); RDEBUG("Allocated IP %s [%08x]", allocation, ip_allocation); vp = radius_paircreate(request, &request->reply->vps, PW_FRAMED_IP_ADDRESS, 0, PW_TYPE_IPADDR); vp->vp_ipaddr = ip_allocation; /* * COMMIT */ sqlippool_command(data->allocate_commit, sqlsocket, data, request, (char *) NULL, 0); data->sql_inst->sql_release_socket(data->sql_inst, sqlsocket); radius_xlat(logstr, sizeof(logstr), data->log_success, request, NULL, NULL); return do_logging(logstr, RLM_MODULE_OK); }
/* * Common code called by everything below. */ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const *filename, fr_hash_table_t *ht, VALUE_PAIR *request_pairs, VALUE_PAIR **reply_pairs) { char const *name, *match; VALUE_PAIR *check_tmp; VALUE_PAIR *reply_tmp; const PAIR_LIST *user_pl, *default_pl; int found = 0; PAIR_LIST my_pl; char buffer[256]; if (!inst->key) { VALUE_PAIR *namepair; namepair = request->username; name = namepair ? namepair->vp_strvalue : "NONE"; } else { int len; len = radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL); if (len < 0) { return RLM_MODULE_FAIL; } name = len ? buffer : "NONE"; } if (!ht) return RLM_MODULE_NOOP; my_pl.name = name; user_pl = fr_hash_table_finddata(ht, &my_pl); my_pl.name = "DEFAULT"; default_pl = fr_hash_table_finddata(ht, &my_pl); /* * Find the entry for the user. */ while (user_pl || default_pl) { const PAIR_LIST *pl; if (!default_pl && user_pl) { pl = user_pl; match = name; user_pl = user_pl->next; } else if (!user_pl && default_pl) { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } else if (user_pl->order < default_pl->order) { pl = user_pl; match = name; user_pl = user_pl->next; } else { pl = default_pl; match = "DEFAULT"; default_pl = default_pl->next; } if (paircompare(request, request_pairs, pl->check, reply_pairs) == 0) { RDEBUG2("%s: Matched entry %s at line %d", filename, match, pl->lineno); found = 1; check_tmp = paircopy(request, pl->check); /* ctx may be reply or proxy */ reply_tmp = paircopy(request, pl->reply); radius_xlat_move(request, reply_pairs, &reply_tmp); pairmove(request, &request->config_items, &check_tmp); pairfree(&reply_tmp); pairfree(&check_tmp); /* * Fallthrough? */ if (!fallthrough(pl->reply)) break; } } /* * Remove server internal parameters. */ pairdelete(reply_pairs, PW_FALL_THROUGH, 0, TAG_ANY); /* * See if we succeeded. */ if (!found) return RLM_MODULE_NOOP; /* on to the next module */ return RLM_MODULE_OK; }
/* * This function is called when the first EAP_IDENTITY_RESPONSE message * was received. * * Initiates the EPA_TNC session by sending the first EAP_TNC_RESPONSE * to the peer. The packet has the Start-Bit set and contains no data. * * 0 1 2 3 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Code | Identifier | Length | * | | | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Type | Flags | Ver | * | |0 0 1 0 0|0 0 1| * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * * For this package, only 'Identifier' has to be set dynamically. Any * other information is static. */ static int tnc_initiate(void *instance, eap_handler_t *handler) { rlm_eap_tnc_t *inst = instance; REQUEST *request = NULL; char buff[71]; ssize_t len = 0; TNC_Result result; TNC_ConnectionID conn_id; TNC_BufferReference eap_tnc_request; TNC_BufferReference eap_tnc_user; VALUE_PAIR *username; /* * Check if we run inside a secure EAP method. * FIXME check concrete outer EAP method. */ if (!handler->request || !handler->request->parent) { ERROR("rlm_eap_tnc: EAP_TNC must only be used as an " "inner method within a protected tunneled EAP created " "by an outer EAP method"); return 0; } request = handler->request->parent; /* * Build the connection string */ len = radius_xlat(buff, sizeof(buff), request, inst->connection_string, NULL, NULL); if (len < 0){ return 0; } RDEBUG("Getting connection from NAA-EAP"); /* * Get connection (uses a function from the NAA-EAP-library) */ result = getConnection(buff, &conn_id); if (result != TNC_RESULT_SUCCESS) { ERROR("rlm_eap_tnc: NAA-EAP getConnection returned an " "error code"); return 0; } /* * Previous code manually parsed the EAP identity response * this was wrong. rlm_eap will *always* create the Username * from the EAP Identity response. * * Something has gone very wrong if the User-Name doesn't exist. */ username = pairfind(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); RDEBUG("Username for TNC connection: %s", username->vp_strvalue); /* * Stores the username associated with the connection * * What becomes of username? Who knows... but we don't free it * so not safe to use talloc. */ MEM(eap_tnc_user = (TNC_BufferReference) strdup(username->vp_strvalue)); result = storeUsername(conn_id, eap_tnc_user, username->length); if (result != TNC_RESULT_SUCCESS) { ERROR("rlm_eap_tnc: NAA-EAP storeUsername returned an " "error code"); return 0; } /* * Set connection ID */ handler->opaque = talloc(handler, TNC_ConnectionID); memcpy(handler->opaque, &conn_id, sizeof(TNC_ConnectionID)); handler->free_opaque = tnc_free; /* * Bild first EAP TNC request */ MEM(eap_tnc_request = talloc_array(handler->eap_ds->request, uint8_t, 1)); *eap_tnc_request = SET_START(1); handler->eap_ds->request->code = PW_EAP_REQUEST; handler->eap_ds->request->type.num = PW_EAP_TNC; handler->eap_ds->request->type.length = 1; talloc_free(handler->eap_ds->request->type.data); handler->eap_ds->request->type.data = eap_tnc_request; /* * We don't need to authorize the user at this point. * * We also don't need to keep the challenge, as it's * stored in 'handler->eap_ds', which will be given back * to us... */ handler->stage = AUTHENTICATE; return 1; }