/************************************************************************* * * Function: sql_getvpdata * * Purpose: Get any group check or reply pairs * *************************************************************************/ int sql_getvpdata(TALLOC_CTX *ctx, rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle, VALUE_PAIR **pair, char const *query) { rlm_sql_row_t row; int rows = 0; sql_rcode_t rcode; rad_assert(request); rcode = rlm_sql_select_query(inst, request, handle, query); if (rcode != RLM_SQL_OK) return -1; /* error handled by rlm_sql_select_query */ while (rlm_sql_fetch_row(&row, inst, request, handle) == 0) { if (!row) break; if (sql_userparse(ctx, request, pair, row) != 0) { REDEBUG("Error parsing user data from database result"); (inst->module->sql_finish_select_query)(*handle, inst->config); return -1; } rows++; } (inst->module->sql_finish_select_query)(*handle, inst->config); return rows; }
/* * This is a wraper for radius_axlat * Now users are able to get data that is accessible only via xlat * e.g. %{client:...} * Call syntax is radiusd::xlat(string), string will be handled the * same way it is described in EXPANSIONS section of man unlang */ static XS(XS_radiusd_xlat) { dXSARGS; char *in_str; char *expanded; ssize_t slen; SV *rad_requestp_sv; REQUEST *request; if (items != 1) croak("Usage: radiusd::xlat(string)"); rad_requestp_sv = get_sv("RAD___REQUESTP", 0); if (rad_requestp_sv == NULL) croak("Can not evalue xlat, RAD___REQUESTP is not set!"); request = INT2PTR(REQUEST *, SvIV(rad_requestp_sv)); in_str = (char *) SvPV(ST(0), PL_na); expanded = NULL; slen = radius_axlat(&expanded, request, in_str, NULL, NULL); if (slen < 0) { REDEBUG("Error parsing xlat '%s'", in_str); XSRETURN_UNDEF; } XST_mPV(0, expanded); talloc_free(expanded); XSRETURN(1); }
/** Print a list of valuepairs to the request list. * * @param[in] level Debug level (1-4). * @param[in] request to read logging params from. * @param[in] vp to print. */ void rdebug_pair_list(int level, REQUEST *request, VALUE_PAIR *vp) { vp_cursor_t cursor; char buffer[256]; if (!vp || !request || !request->log.func) return; if (!radlog_debug_enabled(L_DBG, level, request)) return; for (vp = fr_cursor_init(&cursor, &vp); vp; vp = fr_cursor_next(&cursor)) { /* * Take this opportunity to verify all the VALUE_PAIRs are still valid. */ if (!talloc_get_type(vp, VALUE_PAIR)) { REDEBUG("Expected VALUE_PAIR pointer got \"%s\"", talloc_get_name(vp)); fr_log_talloc_report(vp); rad_assert(0); } vp_prints(buffer, sizeof(buffer), vp); RDEBUGX(level, "\t%s", buffer); } }
/** Converts a string value into a #VALUE_PAIR * * @param[in,out] ctx to allocate #VALUE_PAIR (s). * @param[out] out where to write the resulting #VALUE_PAIR. * @param[in] request The current request. * @param[in] map to process. * @param[in] uctx The value to parse. * @return * - 0 on success. * - -1 on failure. */ static int _sql_map_proc_get_value(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) { VALUE_PAIR *vp; char const *value = uctx; vp = fr_pair_afrom_da(ctx, map->lhs->tmpl_da); /* * Buffer not always talloced, sometimes it's * just a pointer to a field in a result struct. */ if (fr_pair_value_from_str(vp, value, strlen(value)) < 0) { char *escaped; escaped = fr_asprint(vp, value, talloc_array_length(value), '"'); REDEBUG("Failed parsing value \"%s\" for attribute %s: %s", escaped, map->lhs->tmpl_da->name, fr_strerror()); talloc_free(vp); return -1; } vp->op = map->op; *out = vp; return 0; }
/* * Query the database executing a command with no result rows */ static int sqlippool_command(char const * fmt, rlm_sql_handle_t * handle, rlm_sqlippool_t *data, REQUEST * request, char * param, int param_len) { char query[MAX_QUERY_LEN]; char *expanded = NULL; int ret; /* * If we don't have a command, do nothing. */ if (!*fmt) return 0; /* * @todo this needs to die (should just be done in xlat expansion) */ sqlippool_expand(query, sizeof(query), fmt, data, param, param_len); if (radius_axlat(&expanded, request, query, data->sql_inst->sql_escape_func, data->sql_inst) < 0) { return 0; } ret = data->sql_inst->sql_query(&handle, data->sql_inst, expanded); if (!ret) { REDEBUG("database query error in: '%s'", expanded); talloc_free(expanded); return 0; } talloc_free(expanded); (data->sql_inst->module->sql_finish_query)(handle, data->sql_inst->config); return 0; }
/** * @brief Convert base64 to hex * * Example: "%{base64tohex:Zm9v}" == "666f6f" */ static size_t base64_to_hex_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen) { uint8_t decbuf[1024], *p; ssize_t declen; size_t freespace = outlen; ssize_t len = strlen(fmt); *out = '\0'; declen = fr_base64_decode(fmt, len, decbuf, sizeof(decbuf)); if (declen < 0) { REDEBUG("Base64 string invalid"); 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; }
/** Resolve request to a request. * * Resolve name to a current request. * * @see radius_list * @param[in,out] context Base context to use, and to write the result back to. * @param[in] name (request) to resolve to. * @return 0 if request is valid in this context, else -1. */ int radius_request(REQUEST **context, request_refs_t name) { REQUEST *request = *context; switch (name) { case REQUEST_CURRENT: return 0; case REQUEST_PARENT: /* for future use in request chaining */ case REQUEST_OUTER: if (!request->parent) { REDEBUG("Specified request \"%s\" is not available in this context", fr_int2str(request_refs, name, "<INVALID>")); return -1; } *context = request->parent; break; case REQUEST_UNKNOWN: default: rad_assert(0); return -1; } return 0; }
/* * The xlat function */ static ssize_t perl_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t freespace) { rlm_perl_t *inst = (rlm_perl_t *) instance; char *tmp; char const *p, *q; int count; size_t ret = 0; STRLEN n_a; #ifdef USE_ITHREADS PerlInterpreter *interp; pthread_mutex_lock(&inst->clone_mutex); interp = rlm_perl_clone(inst->perl, inst->thread_key); { dTHXa(interp); PERL_SET_CONTEXT(interp); } pthread_mutex_unlock(&inst->clone_mutex); #else PERL_SET_CONTEXT(inst->perl); #endif { dSP; ENTER;SAVETMPS; PUSHMARK(SP); p = fmt; while ((q = strchr(p, ' '))) { XPUSHs(sv_2mortal(newSVpvn(p, p - q))); p = q + 1; } PUTBACK; count = call_pv(inst->func_xlat, G_SCALAR | G_EVAL); SPAGAIN; if (SvTRUE(ERRSV)) { REDEBUG("Exit %s", SvPV(ERRSV,n_a)); (void)POPs; } else if (count > 0) { tmp = POPp; strlcpy(out, tmp, freespace); ret = strlen(out); RDEBUG("Len is %zu , out is %s freespace is %zu", ret, out, freespace); } PUTBACK ; FREETMPS ; LEAVE ; } return ret; }
static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { rlm_sql_handle_t *handle; rlm_sql_t *inst = instance; rlm_sql_grouplist_t *head, *entry; RDEBUG("sql_groupcmp"); if (!check || !check->length){ RDEBUG("sql_groupcmp: Illegal group name"); return 1; } if (!request){ RDEBUG("sql_groupcmp: NULL request"); return 1; } /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) return 1; /* * Get a socket for this lookup */ handle = sql_get_socket(inst); if (!handle) { return 1; } /* * Get the list of groups this user is a member of */ if (sql_get_grouplist(inst, handle, request, &head) < 0) { REDEBUG("Error getting group membership"); sql_release_socket(inst, handle); return 1; } for (entry = head; entry != NULL; entry = entry->next) { if (strcmp(entry->name, check->vp_strvalue) == 0){ RDEBUG("sql_groupcmp finished: User is a member of group %s", check->vp_strvalue); talloc_free(head); sql_release_socket(inst, handle); return 0; } } /* Free the grouplist */ talloc_free(head); sql_release_socket(inst,handle); RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue); return 1; }
/** Call the driver's sql_select_query method, reconnecting if necessary. * * @note Caller must call ``(inst->module->sql_finish_select_query)(handle, inst->config);`` * after they're done with the result. * * @param inst #rlm_sql_t instance data. * @param request Current request. * @param handle to query the database with. *handle should not be NULL, as this indicates * previous reconnection attempt has failed. * @param query to execute. Should not be zero length. * @return * - #RLM_SQL_OK on success. * - #RLM_SQL_RECONNECT if a new handle is required (also sets *handle = NULL). * - #RLM_SQL_QUERY_INVALID, #RLM_SQL_ERROR on invalid query or connection error. */ sql_rcode_t rlm_sql_select_query(rlm_sql_t *inst, REQUEST *request, rlm_sql_handle_t **handle, char const *query) { int ret = RLM_SQL_ERROR; int i, count; /* Caller should check they have a valid handle */ rad_assert(*handle); /* There's no query to run, return an error */ if (query[0] == '\0') { if (request) REDEBUG("Zero length query"); return RLM_SQL_QUERY_INVALID; } /* * inst->pool may be NULL is this function is called by mod_conn_create. */ count = inst->pool ? fr_connection_get_num(inst->pool) : 0; /* * For sanity, for when no connections are viable, and we can't make a new one */ for (i = 0; i < (count + 1); i++) { ROPTIONAL(RDEBUG2, DEBUG2, "Executing select query: %s", query); ret = (inst->module->sql_select_query)(*handle, inst->config, query); switch (ret) { case RLM_SQL_OK: break; /* * Run through all available sockets until we exhaust all existing * sockets in the pool and fail to establish a *new* connection. */ case RLM_SQL_RECONNECT: *handle = fr_connection_reconnect(inst->pool, *handle); /* Reconnection failed */ if (!*handle) return RLM_SQL_RECONNECT; /* Reconnection succeeded, try again with the new handle */ continue; case RLM_SQL_QUERY_INVALID: case RLM_SQL_ERROR: default: rlm_sql_print_error(inst, request, *handle, false); (inst->module->sql_finish_select_query)(*handle, inst->config); break; } return ret; } ROPTIONAL(RERROR, ERROR, "Hit reconnection limit"); return RLM_SQL_ERROR; }
/* * Query the database expecting a single result row */ static int sqlippool_query1(char *out, int outlen, char const *fmt, rlm_sql_handle_t *handle, rlm_sqlippool_t *data, REQUEST *request, char *param, int param_len) { char query[MAX_QUERY_LEN]; char *expanded = NULL; int rlen, retval; /* * @todo this needs to die (should just be done in xlat expansion) */ sqlippool_expand(query, sizeof(query), fmt, data, param, param_len); rad_assert(request != NULL); *out = '\0'; /* * Do an xlat on the provided string */ if (radius_axlat(&expanded, request, query, data->sql_inst->sql_escape_func, data->sql_inst) < 0) { return 0; } retval = data->sql_inst->sql_select_query(&handle, data->sql_inst, expanded); talloc_free(expanded); if (retval != 0) { REDEBUG("database query error on '%s'", query); return 0; } if (!data->sql_inst->sql_fetch_row(&handle, data->sql_inst)) { if (handle->row) { if (handle->row[0]) { if ((rlen = strlen(handle->row[0])) < outlen) { strcpy(out, handle->row[0]); retval = rlen; } else { RDEBUG("insufficient string space"); } } else { RDEBUG("row[0] returned NULL"); } } else { RDEBUG("SQL query did not return any results"); } } else { RDEBUG("SQL query did not succeed"); } (data->sql_inst->module->sql_finish_select_query)(handle, data->sql_inst->config); return retval; }
/** Perform a search and map the result of the search to server attributes * * @param[in] mod_inst #rlm_csv_t. * @param[in] proc_inst mapping map entries to field numbers. * @param[in,out] request The current request. * @param[in] key key to look for * @param[in] maps Head of the map list. * @return * - #RLM_MODULE_NOOP no rows were returned. * - #RLM_MODULE_UPDATED if one or more #VALUE_PAIR were added to the #REQUEST. * - #RLM_MODULE_FAIL if an error occurred. */ static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST *request, fr_value_box_t **key, vp_map_t const *maps) { rlm_rcode_t rcode = RLM_MODULE_UPDATED; rlm_csv_t *inst = talloc_get_type_abort(mod_inst, rlm_csv_t); rlm_csv_entry_t *e; vp_map_t const *map; if (!*key) { REDEBUG("CSV key cannot be (null)"); return RLM_MODULE_FAIL; } if (fr_value_box_list_concat(request, *key, key, FR_TYPE_STRING, true) < 0) { REDEBUG("Failed concatenating key elements"); return RLM_MODULE_FAIL; } e = rbtree_finddata(inst->tree, &(rlm_csv_entry_t){ .key = (*key)->vb_strvalue });
static ssize_t date_encode_strftime(char **out, size_t outlen, rlm_date_t const *inst, REQUEST *request, time_t date) { struct tm tminfo; if (inst->utc) { if (gmtime_r(&date, &tminfo) == NULL) { REDEBUG("Failed converting time string to gmtime: %s", fr_syserror(errno)); return -1; } } else { if (localtime_r(&date, &tminfo) == NULL) { REDEBUG("Failed converting time string to localtime: %s", fr_syserror(errno)); return -1; } } return strftime(*out, outlen, inst->fmt, &tminfo); }
/* * See if the counter matches. */ static int sqlcounter_cmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *req , VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { rlm_sqlcounter_t *inst = instance; uint64_t counter; char query[MAX_QUERY_LEN], subst[MAX_QUERY_LEN]; char *expanded = NULL; size_t len; /* First, expand %k, %b and %e in query */ if (sqlcounter_expand(subst, sizeof(subst), inst->query, inst) <= 0) { REDEBUG("Insufficient query buffer space"); return RLM_MODULE_FAIL; } /* Then combine that with the name of the module were using to do the query */ len = snprintf(query, sizeof(query), "%%{%s:%s}", inst->sqlmod_inst, subst); if (len >= sizeof(query) - 1) { REDEBUG("Insufficient query buffer space"); return RLM_MODULE_FAIL; } /* Finally, xlat resulting SQL query */ if (radius_axlat(&expanded, request, query, NULL, NULL) < 0) { return RLM_MODULE_FAIL; } if (sscanf(expanded, "%" PRIu64, &counter) != 1) { RDEBUG2("No integer found in string \"%s\"", expanded); } talloc_free(expanded); if (counter < check->vp_integer64) { return -1; } if (counter > check->vp_integer64) { return 1; } return 0; }
static ssize_t date_convert_string(REQUEST *request, char **out, size_t outlen, const char *str, const char *fmt) { struct tm tminfo; time_t date = 0; if (strptime(str, fmt, &tminfo) == NULL) { REDEBUG("Failed to parse time string \"%s\" as format '%s'", str, fmt); return -1; } date = mktime(&tminfo); if (date < 0) { REDEBUG("Failed converting parsed time into unix time"); return -1; } return snprintf(*out, outlen, "%" PRIu64, (uint64_t) date); }
static ssize_t xlat_idna(void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t freespace) { rlm_idn_t *inst = instance; char *idna = NULL; int res; size_t len; int flags = 0; if (inst->use_std3_ascii_rules) { flags |= IDNA_USE_STD3_ASCII_RULES; } if (inst->allow_unassigned) { flags |= IDNA_ALLOW_UNASSIGNED; } res = idna_to_ascii_8z(fmt, &idna, flags); if (res) { if (idna) { free (idna); /* Docs unclear, be safe. */ } REDEBUG("%s", idna_strerror(res)); return -1; } len = strlen(idna); /* 253 is max DNS length */ if (!((len < (freespace - 1)) && (len <= 253))) { /* Never provide a truncated result, as it may be queried. */ REDEBUG("Conversion was truncated"); free(idna); return -1; } strlcpy(out, idna, freespace); free(idna); return len; }
USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ #include <freeradius-devel/radiusd.h> #ifdef WITH_TLS void cbtls_info(SSL const *s, int where, int ret) { char const *str, *state; int w; REQUEST *request = SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST); char buffer[1024]; w = where & ~SSL_ST_MASK; if (w & SSL_ST_CONNECT) str=" TLS_connect"; else if (w & SSL_ST_ACCEPT) str=" TLS_accept"; else str=" (other)"; state = SSL_state_string_long(s); state = state ? state : "NULL"; buffer[0] = '\0'; if (where & SSL_CB_LOOP) { RDEBUG2("%s: %s", str, state); } else if (where & SSL_CB_HANDSHAKE_START) { RDEBUG2("%s: %s", str, state); } else if (where & SSL_CB_HANDSHAKE_DONE) { RDEBUG2("%s: %s", str, state); } else if (where & SSL_CB_ALERT) { str=(where & SSL_CB_READ)?"read":"write"; snprintf(buffer, sizeof(buffer), "TLS Alert %s:%s:%s", str, SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); } else if (where & SSL_CB_EXIT) { if (ret == 0) { snprintf(buffer, sizeof(buffer), "%s: failed in %s", str, state); } else if (ret < 0) { if (SSL_want_read(s)) { RDEBUG2("%s: Need to read more data: %s", str, state); } else { snprintf(buffer, sizeof(buffer), "%s: error in %s", str, state); } } } if (buffer[0] && request) { REDEBUG("SSL says: %s", buffer); } }
/** Generate the HMAC-SHA1 of a string or attribute * * Example: "%{hmacsha1:foo bar}" == "Zm9v" */ static ssize_t hmac_sha1_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen) { uint8_t const *data, *key; char const *p; ssize_t data_len, key_len; uint8_t digest[SHA1_DIGEST_LENGTH]; char data_ref[256]; if (outlen <= (sizeof(digest) * 2)) { REDEBUG("Insufficient space to write digest, needed %zu bytes, have %zu bytes", (sizeof(digest) * 2) + 1, outlen); return -1; } p = strchr(fmt, ' '); if (!p) { REDEBUG("HMAC requires exactly two arguments (&data &key)"); return -1; } if ((size_t)(p - fmt) >= sizeof(data_ref)) { REDEBUG("Insufficient space to store HMAC input data, needed %zu bytes, have %zu bytes", (p - fmt) + 1, sizeof(data_ref)); return -1; } strlcpy(data_ref, fmt, (p - fmt) + 1); data_len = xlat_fmt_to_ref(&data, request, data_ref); if (data_len < 0) return -1; while (isspace(*p) && p++); key_len = xlat_fmt_to_ref(&key, request, p); if (key_len < 0) return -1; fr_hmac_sha1(digest, data, data_len, key, key_len); return fr_bin2hex(out, digest, sizeof(digest)); }
/* * Do xlat of strings. */ static ssize_t exec_xlat(void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { int result; rlm_exec_t *inst = instance; VALUE_PAIR **input_pairs = NULL; char *p; if (!inst->wait) { REDEBUG("'wait' must be enabled to use exec xlat"); *out = '\0'; return -1; } if (inst->input_list) { input_pairs = radius_list(request, inst->input_list); if (!input_pairs) { REDEBUG("Failed to find input pairs for xlat"); *out = '\0'; return -1; } } /* * This function does it's own xlat of the input program * to execute. */ result = radius_exec_program(request, fmt, inst->wait, inst->shell_escape, out, outlen, inst->timeout, input_pairs ? *input_pairs : NULL, NULL); if (result != 0) { out[0] = '\0'; return -1; } for (p = out; *p != '\0'; p++) { if (*p < ' ') *p = ' '; } return strlen(out); }
/** De-queue curl requests and wake up the requests that initiated them * * @param[in] thread holding the requests to re-enliven. * @param[in] mandle to dequeue curl easy handles/responses from. */ static inline void _rest_io_demux(rlm_rest_thread_t *thread, CURLM *mandle) { struct CURLMsg *m; int msg_queued = 0; while ((m = curl_multi_info_read(mandle, &msg_queued))) { switch (m->msg) { case CURLMSG_DONE: { REQUEST *request = NULL; CURL *candle = m->easy_handle; CURLcode ret; rad_assert(candle); thread->transfers--; ret = curl_easy_getinfo(candle, CURLINFO_PRIVATE, &request); if (!fr_cond_assert_msg(ret == CURLE_OK, "Failed retrieving request data from CURL easy handle (candle)")) { curl_multi_remove_handle(mandle, candle); return; } REQUEST_VERIFY(request); /* * If the request failed, say why... */ if (m->data.result != CURLE_OK) { REDEBUG("REST request failed: %s (%i)", curl_easy_strerror(m->data.result), m->data.result); } /* * Looks like this needs to be done last, * else m->data.result ends up being junk. */ curl_multi_remove_handle(mandle, candle); unlang_interpret_resumable(request); } break; default: #ifndef NDEBUG DEBUG4("Got unknown msg (%i) when dequeueing curl responses", msg_queued); #endif break; } } }
int compute_scalar_element(REQUEST *request, pwd_session_t *session, BN_CTX *bn_ctx) { BIGNUM *mask = NULL; int ret = -1; MEM(session->private_value = BN_new()); MEM(session->my_element = EC_POINT_new(session->group)); MEM(session->my_scalar = BN_new()); MEM(mask = BN_new()); if (BN_rand_range(session->private_value, session->order) != 1) { REDEBUG("Unable to get randomness for private_value"); goto error; } if (BN_rand_range(mask, session->order) != 1) { REDEBUG("Unable to get randomness for mask"); goto error; } BN_add(session->my_scalar, session->private_value, mask); BN_mod(session->my_scalar, session->my_scalar, session->order, bn_ctx); if (!EC_POINT_mul(session->group, session->my_element, NULL, session->pwe, mask, bn_ctx)) { REDEBUG("Server element allocation failed"); goto error; } if (!EC_POINT_invert(session->group, session->my_element, bn_ctx)) { REDEBUG("Server element inversion failed"); goto error; } ret = 0; error: BN_clear_free(mask); return ret; }
static ssize_t xlat_date_convert(UNUSED TALLOC_CTX *ctx, char **out, size_t outlen, void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { rlm_date_t const *inst = mod_inst; struct tm tminfo; struct timeval now; VALUE_PAIR *vp; memset(&tminfo, 0, sizeof(tminfo)); if (strcmp(fmt, "request") == 0) { return date_encode_strftime(out, outlen, inst, request, request->packet->timestamp.tv_sec); } if (strcmp(fmt, "now") == 0) { gettimeofday(&now, NULL); return date_encode_strftime(out, outlen, inst, request, now.tv_sec); } if ((xlat_fmt_get_vp(&vp, request, fmt) < 0) || !vp) return 0; switch (vp->vp_type) { /* * These are 'to' types, i.e. we'll convert the integers * to a time structure, and then output it in the specified * format as a string. */ case FR_TYPE_DATE: return date_encode_strftime(out, outlen, inst, request, vp->vp_date); case FR_TYPE_UINT32: case FR_TYPE_UINT64: return date_encode_strftime(out, outlen, inst, request, (time_t) vp->vp_uint32); /* * These are 'from' types, i.e. we'll convert the input string * into a time structure, and then output it as an integer * unix timestamp. */ case FR_TYPE_STRING: return date_convert_string(request, out, outlen, vp->vp_strvalue, inst->fmt); default: REDEBUG("Can't convert type %s into date", fr_int2str(fr_value_box_type_names, vp->da->type, "<INVALID>")); } return -1; }
/** Called when the timeout has expired * * Marks the request as resumable, and prints the delayed delay time. * * @param[in] request The current request. * @param[in] instance This instance of the delay module. * @param[in] thread Thread specific module instance. * @param[in] ctx Scheduled end of the delay. * @param[in] fired When request processing was resumed. */ static void _delay_done(REQUEST *request, UNUSED void *instance, UNUSED void *thread, void *ctx, struct timeval *fired) { struct timeval *yielded = talloc_get_type_abort(ctx, struct timeval); RDEBUG2("Delay done"); /* * timeout should never be *before* the scheduled time, * if it is, something is very broken. */ if (!fr_cond_assert(fr_timeval_cmp(fired, yielded) >= 0)) REDEBUG("Unexpected resume time"); unlang_resumable(request); }
/* * Add raw hex data to the reply. */ void eap_add_reply(REQUEST *request, char const *name, uint8_t const *value, int len) { VALUE_PAIR *vp; vp = pair_make_reply(name, NULL, T_OP_EQ); if (!vp) { REDEBUG("Did not create attribute %s: %s\n", name, fr_strerror()); return; } fr_pair_value_memcpy(vp, value, len); }
/* * * Verify that a Perl SV is a string and save it in FreeRadius * Value Pair Format * */ static void pairadd_sv(TALLOC_CTX *ctx, REQUEST *request, VALUE_PAIR **vps, char *key, SV *sv, FR_TOKEN op, const char *hash_name, const char *list_name) { char *val = NULL; VALUE_PAIR *vp; STRLEN len; if (!SvOK(sv)) { REDEBUG("Internal failure creating pair &%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens, op, "<INVALID>"), hash_name, key, (val ? val : "undef")); return; } val = SvPV(sv, len); vp = fr_pair_make(ctx, vps, key, NULL, op); if (!vp) { fail: REDEBUG("Failed to create pair - %s", fr_strerror()); REDEBUG(" &%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens, op, "<INVALID>"), hash_name, key, (val ? val : "undef")); return; } switch (vp->da->type) { case PW_TYPE_STRING: fr_pair_value_bstrncpy(vp, val, len); break; default: VERIFY_VP(vp); if (fr_pair_value_from_str(vp, val, len) < 0) goto fail; } RDEBUG("&%s:%s %s $%s{'%s'} -> '%s'", list_name, key, fr_int2str(fr_tokens, op, "<INVALID>"), hash_name, key, val); }
/* * send an initial eap-leap request * ie access challenge to the user/peer. * Frame eap reply packet. * len = header + type + leap_methoddata * leap_methoddata = value_size + value */ static int CC_HINT(nonnull) mod_session_init(UNUSED void *instance, eap_handler_t *handler) { REQUEST *request = handler->request; leap_session_t *session; leap_packet_t *reply; RDEBUG2("Stage 2"); /* * LEAP requires a User-Name attribute */ if (!handler->request->username) { REDEBUG("User-Name is required for EAP-LEAP authentication"); return 0; } reply = eapleap_initiate(request, handler->eap_ds, handler->request->username); if (!reply) { return 0; } eapleap_compose(request, handler->eap_ds, reply); handler->opaque = session = talloc(handler, leap_session_t); if (!handler->opaque) { talloc_free(reply); return 0; } /* * Remember which stage we're in, and which challenge * we sent to the AP. The later stages will take care * of filling in the peer response. */ handler->free_opaque = NULL; session->stage = 4; /* the next stage we're in */ memcpy(session->peer_challenge, reply->challenge, reply->count); REDEBUG2("Successfully initiated"); /* * The next stage to process the packet. */ handler->stage = PROCESS; talloc_free(reply); return 1; }
/** Process the exit code returned by one of the exec functions * * @param request Current request. * @param answer Output string from exec call. * @param len length of data in answer. * @param status code returned by exec call. * @return One of the RLM_MODULE_* values. */ static rlm_rcode_t rlm_exec_status2rcode(REQUEST *request, char *answer, size_t len, int status) { if (status < 0) { return RLM_MODULE_FAIL; } /* * Exec'd programs are meant to return exit statuses that correspond * to the standard RLM_MODULE_* + 1. * * This frees up 0, for success where it'd normally be reject. */ if (status == 0) { RDEBUG("Program executed successfully"); return RLM_MODULE_OK; } if (status > RLM_MODULE_NUMCODES) { REDEBUG("Program returned invalid code (greater than max rcode) (%i > %i): %s", status, RLM_MODULE_NUMCODES, answer); goto fail; return RLM_MODULE_FAIL; } status--; /* Lets hope no one ever re-enumerates RLM_MODULE_* */ if (status == RLM_MODULE_FAIL) { fail: if (len > 0) { char *p = &answer[len - 1]; /* * Trim off trailing returns */ while((p > answer) && ((*p == '\r') || (*p == '\n'))) { *p-- = '\0'; } module_failure_msg(request, "%s", answer); } return RLM_MODULE_FAIL; } return status; }
/** Convert base64 to hex * * Example: "%{base64tohex:Zm9v}" == "666f6f" */ static ssize_t base64_to_hex_xlat(UNUSED void *instance, UNUSED REQUEST *request, char const *fmt, char *out, size_t outlen) { uint8_t decbuf[1024]; ssize_t declen; ssize_t len = strlen(fmt); *out = '\0'; declen = fr_base64_decode(decbuf, sizeof(decbuf), fmt, len); if (declen < 0) { REDEBUG("Base64 string invalid"); return -1; } if ((size_t)((declen * 2) + 1) > outlen) { REDEBUG("Base64 conversion failed, output buffer exhausted, needed %zd bytes, have %zd bytes", (declen * 2) + 1, outlen); return -1; } return fr_bin2hex(out, decbuf, declen); }
/** Search for and apply an LDAP profile * * LDAP profiles are mapped using the same attribute map as user objects, they're used to add common sets of attributes * to the request. * * @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] dn of profile object to apply. * @param[in] expanded Structure containing a list of xlat expanded attribute names and mapping information. * @return One of the RLM_MODULE_* values. */ rlm_rcode_t rlm_ldap_map_profile(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char const *dn, rlm_ldap_map_xlat_t const *expanded) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; LDAPMessage *result = NULL, *entry = NULL; int ldap_errno; LDAP *handle = (*pconn)->handle; char filter[LDAP_MAX_FILTER_STR_LEN]; rad_assert(inst->profile_filter); /* We always have a default filter set */ if (!dn || !*dn) { return RLM_MODULE_OK; } strlcpy(filter, inst->profile_filter, sizeof(filter)); status = rlm_ldap_search(inst, request, pconn, dn, LDAP_SCOPE_BASE, filter, expanded->attrs, &result); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_NO_RESULT: RDEBUG("Profile object \"%s\" not found", dn); return RLM_MODULE_NOTFOUND; default: return RLM_MODULE_FAIL; } rad_assert(*pconn); rad_assert(result); entry = ldap_first_entry(handle, result); if (!entry) { ldap_get_option(handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); rcode = RLM_MODULE_NOTFOUND; goto free_result; } rlm_ldap_map_do(inst, request, handle, expanded, entry); free_result: ldap_msgfree(result); return rcode; }
rlm_rcode_t rlm_yubikey_validate(rlm_yubikey_t *inst, REQUEST *request, char const *passcode) { rlm_rcode_t rcode = RLM_MODULE_OK; ykclient_rc status; ykclient_handle_t *yandle; yandle = fr_connection_get(inst->pool); if (!yandle) return RLM_MODULE_FAIL; /* * The libcurl multi-handle interface will tear down the TCP sockets for any partially completed * requests when their easy handle is removed from the multistack. * * For performance reasons ykclient will stop processing the request immediately after receiving * a response from one of the servers. If we then immediately call ykclient_handle_cleanup * the connections are destroyed and will need to be re-established the next time the handle * is used. * * To try and prevent this from happening, we leave cleanup until the *next* time * the handle is used, by which time the requests will of hopefully completed and the connections * can be re-used. * */ ykclient_handle_cleanup(yandle); status = ykclient_request_process(inst->ykc, yandle, passcode); if (status != YKCLIENT_OK) { REDEBUG("%s", ykclient_strerror(status)); switch (status) { case YKCLIENT_BAD_OTP: case YKCLIENT_REPLAYED_OTP: rcode = RLM_MODULE_REJECT; break; case YKCLIENT_NO_SUCH_CLIENT: rcode = RLM_MODULE_NOTFOUND; break; default: rcode = RLM_MODULE_FAIL; } } fr_connection_release(inst->pool, yandle); return rcode; }