/* * First, look for Exec-Program && Exec-Program-Wait. * * Then, call exec_dispatch. */ static int exec_accounting(void *instance, REQUEST *request) { int result; int exec_wait = 0; VALUE_PAIR *vp; rlm_exec_t *inst = (rlm_exec_t *) instance; /* * The "bare" exec module takes care of handling * Exec-Program and Exec-Program-Wait. */ if (!inst->bare) return exec_dispatch(instance, request); vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0); if (vp) { exec_wait = 0; } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0)) != NULL) { exec_wait = 1; } if (!vp) return RLM_MODULE_NOOP; result = radius_exec_program(vp->vp_strvalue, request, exec_wait, NULL, 0, request->packet->vps, NULL, inst->shell_escape); if (result != 0) { return RLM_MODULE_REJECT; } return RLM_MODULE_OK; }
/* * First, look for Exec-Program && Exec-Program-Wait. * * Then, call exec_dispatch. */ static rlm_rcode_t mod_accounting(void *instance, REQUEST *request) { rlm_exec_t *inst = (rlm_exec_t *) instance; int status; char out[1024]; bool we_wait = false; VALUE_PAIR *vp; /* * The "bare" exec module takes care of handling * Exec-Program and Exec-Program-Wait. */ if (!inst->bare) { return exec_dispatch(instance, request); } vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY); if (vp) { we_wait = true; } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) { we_wait = false; } if (!vp) { return RLM_MODULE_NOOP; } status = radius_exec_program(request, vp->vp_strvalue, we_wait, inst->shell_escape, out, sizeof(out), inst->timeout, request->packet->vps, NULL); return rlm_exec_status2rcode(request, out, strlen(out), status); }
/* * Do xlat of strings. */ static size_t exec_xlat(void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { int result; rlm_exec_t *inst = instance; VALUE_PAIR **input_pairs; char *p; input_pairs = decode_string(request, inst->input); if (!input_pairs) { radlog(L_ERR, "rlm_exec (%s): Failed to find input pairs for xlat", inst->xlat_name); out[0] = '\0'; return 0; } /* * FIXME: Do xlat of program name? */ RDEBUG2("Executing %s", fmt); result = radius_exec_program(fmt, request, inst->wait, out, outlen, *input_pairs, NULL, inst->shell_escape); RDEBUG2("result %d", result); if (result != 0) { out[0] = '\0'; return 0; } for (p = out; *p != '\0'; p++) { if (*p < ' ') *p = ' '; } return strlen(out); }
static char *radius_expand_tmpl(REQUEST *request, value_pair_tmpl_t const *vpt) { char *buffer = NULL; VALUE_PAIR *vp; rad_assert(vpt->type != VPT_TYPE_LIST); switch (vpt->type) { case VPT_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); buffer = talloc_strdup(request, vpt->name); break; case VPT_TYPE_EXEC: EVAL_DEBUG("TMPL EXEC"); buffer = talloc_array(request, char, 1024); if (radius_exec_program(vpt->name, request, 1, buffer, 1024, NULL, NULL, 0) != 0) { talloc_free(buffer); return NULL; } break; case VPT_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); if (strchr(vpt->name, '%') == NULL) { buffer = talloc_strdup(request, vpt->name); break; } /* FALL-THROUGH */ case VPT_TYPE_XLAT: EVAL_DEBUG("TMPL XLAT"); buffer = NULL; if (radius_axlat(&buffer, request, vpt->name, NULL, NULL) == 0) { return NULL; } break; case VPT_TYPE_ATTR: EVAL_DEBUG("TMPL ATTR"); vp = radius_vpt_get_vp(request, vpt); if (!vp) return NULL; buffer = vp_aprint(request, vp); break; case VPT_TYPE_DATA: rad_assert(0 == 1); /* FALL-THROUGH */ default: buffer = NULL; break; } EVAL_DEBUG("Expand tmpl --> %s", buffer); return buffer; }
/* * First, look for Exec-Program && Exec-Program-Wait. * * Then, call exec_dispatch. */ static rlm_rcode_t exec_postauth(void *instance, REQUEST *request) { int result; int exec_wait = 0; VALUE_PAIR *vp, *tmp; rlm_exec_t *inst = (rlm_exec_t *) instance; vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY); if (vp) { exec_wait = 0; } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) { exec_wait = 1; } if (!vp) { if (!inst->program) return RLM_MODULE_NOOP; return exec_dispatch(instance, request); } tmp = NULL; result = radius_exec_program(vp->vp_strvalue, request, exec_wait, NULL, 0, request->packet->vps, &tmp, inst->shell_escape); /* * Always add the value-pairs to the reply. */ pairmove(&request->reply->vps, &tmp); pairfree(&tmp); if (result < 0) { RDEBUG2("%s", module_failure_msg(request, "rlm_exec (%s): " "Login incorrect (external " "check failed)", inst->xlat_name)); request->reply->code = PW_AUTHENTICATION_REJECT; return RLM_MODULE_REJECT; } if (result > 0) { /* * Reject. radius_exec_program() returns >0 * if the exec'ed program had a non-zero * exit status. */ request->reply->code = PW_AUTHENTICATION_REJECT; RDEBUG2("%s", module_failure_msg(request, "rlm_exec (%s): " "Login incorrect (external " "check said so)", inst->xlat_name)); return RLM_MODULE_REJECT; } return RLM_MODULE_OK; }
/* * First, look for Exec-Program && Exec-Program-Wait. * * Then, call exec_dispatch. */ static int exec_postauth(void *instance, REQUEST *request) { int result; int exec_wait = 0; VALUE_PAIR *vp, *tmp; rlm_exec_t *inst = (rlm_exec_t *) instance; vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0); if (vp) { exec_wait = 0; } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0)) != NULL) { exec_wait = 1; } if (!vp) { if (!inst->program) return RLM_MODULE_NOOP; return exec_dispatch(instance, request); } tmp = NULL; result = radius_exec_program(vp->vp_strvalue, request, exec_wait, NULL, 0, request->packet->vps, &tmp, inst->shell_escape); /* * Always add the value-pairs to the reply. */ pairmove(&request->reply->vps, &tmp); pairfree(&tmp); if (result < 0) { /* * Error. radius_exec_program() returns -1 on * fork/exec errors. */ tmp = pairmake("Reply-Message", "Access denied (external check failed)", T_OP_SET); pairadd(&request->reply->vps, tmp); RDEBUG2("Login incorrect (external check failed)"); request->reply->code = PW_AUTHENTICATION_REJECT; return RLM_MODULE_REJECT; } if (result > 0) { /* * Reject. radius_exec_program() returns >0 * if the exec'ed program had a non-zero * exit status. */ request->reply->code = PW_AUTHENTICATION_REJECT; RDEBUG2("Login incorrect (external check said so)"); return RLM_MODULE_REJECT; } return RLM_MODULE_OK; }
/* * First, look for Exec-Program && Exec-Program-Wait. * * Then, call exec_dispatch. */ static rlm_rcode_t mod_post_auth(void *instance, REQUEST *request) { rlm_exec_t *inst = (rlm_exec_t *) instance; rlm_rcode_t rcode; int status; char out[1024]; bool we_wait = false; VALUE_PAIR *vp, *tmp; vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM, 0, TAG_ANY); if (vp) { we_wait = false; } else if ((vp = pairfind(request->reply->vps, PW_EXEC_PROGRAM_WAIT, 0, TAG_ANY)) != NULL) { we_wait = true; } if (!vp) { if (!inst->program) { return RLM_MODULE_NOOP; } rcode = exec_dispatch(instance, request); goto finish; } tmp = NULL; status = radius_exec_program(request, vp->vp_strvalue, we_wait, inst->shell_escape, out, sizeof(out), inst->timeout, request->packet->vps, &tmp); rcode = rlm_exec_status2rcode(request, out, strlen(out), status); /* * Always add the value-pairs to the reply. */ pairmove(request->reply, &request->reply->vps, &tmp); pairfree(&tmp); finish: switch (rcode) { case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_REJECT: request->reply->code = PW_CODE_AUTHENTICATION_REJECT; break; default: break; } return rcode; }
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; }
/* * 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); }
/* * Dispatch an exec method */ static int exec_dispatch(void *instance, REQUEST *request) { int result; VALUE_PAIR **input_pairs, **output_pairs; VALUE_PAIR *answer = NULL; rlm_exec_t *inst = (rlm_exec_t *) instance; /* * We need a program to execute. */ if (!inst->program) { radlog(L_ERR, "rlm_exec (%s): We require a program to execute", inst->xlat_name); return RLM_MODULE_FAIL; } /* * See if we're supposed to execute it now. */ if (!((inst->packet_code == 0) || (request->packet->code == inst->packet_code) || (request->reply->code == inst->packet_code) #ifdef WITH_PROXY || (request->proxy && (request->proxy->code == inst->packet_code)) || (request->proxy_reply && (request->proxy_reply->code == inst->packet_code)) #endif )) { RDEBUG2("Packet type is not %s. Not executing.", inst->packet_type); return RLM_MODULE_NOOP; } /* * Decide what input/output the program takes. */ input_pairs = decode_string(request, inst->input); output_pairs = decode_string(request, inst->output); if (!input_pairs) { RDEBUG2("WARNING: Possible parse error in %s", inst->input); return RLM_MODULE_NOOP; } /* * It points to the attribute list, but the attribute * list is empty. */ if (!*input_pairs) { RDEBUG2("WARNING! Input pairs are empty. No attributes will be passed to the script"); } /* * This function does it's own xlat of the input program * to execute. * * FIXME: if inst->program starts with %{, then * do an xlat ourselves. This will allow us to do * program = %{Exec-Program}, which this module * xlat's into it's string value, and then the * exec program function xlat's it's string value * into something else. */ result = radius_exec_program(inst->program, request, inst->wait, NULL, 0, *input_pairs, &answer, inst->shell_escape); if (result < 0) { radlog(L_ERR, "rlm_exec (%s): External script failed", inst->xlat_name); return RLM_MODULE_FAIL; } /* * Move the answer over to the output pairs. * * If we're not waiting, then there are no output pairs. */ if (output_pairs) pairmove(output_pairs, &answer); pairfree(&answer); if (result == 0) { return RLM_MODULE_OK; } if (result > RLM_MODULE_NUMCODES) { return RLM_MODULE_FAIL; } return result-1; }
/** Process map which has exec as a src * * Evaluate maps which specify exec as a src. This may be used by various sorts of update sections, and so * has been broken out into it's own function. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc). * @param[in] map the map. The LHS (dst) must be VPT_TYPE_ATTR or VPT_TYPE_LIST. The RHS (src) must be VPT_TYPE_EXEC. * @return -1 on failure, 0 on success. */ int radius_mapexec(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map) { int result; char *expanded = NULL; char answer[1024]; VALUE_PAIR **input_pairs = NULL; VALUE_PAIR **output_pairs = NULL; *out = NULL; rad_assert(map->src->type == VPT_TYPE_EXEC); rad_assert((map->dst->type == VPT_TYPE_ATTR) || (map->dst->type == VPT_TYPE_LIST)); /* * We always put the request pairs into the environment */ input_pairs = radius_list(request, PAIR_LIST_REQUEST); /* * Automagically switch output type depending on our destination * If dst is a list, then we create attributes from the output of the program * if dst is an attribute, then we create an attribute of that type and then * call pairparsevalue on the output of the script. */ out[0] = '\0'; result = radius_exec_program(request, map->src->name, true, true, answer, sizeof(answer), input_pairs ? *input_pairs : NULL, (map->dst->type == VPT_TYPE_LIST) ? output_pairs : NULL); talloc_free(expanded); if (result != 0) { REDEBUG("%s", answer); talloc_free(output_pairs); return -1; } switch (map->dst->type) { case VPT_TYPE_LIST: if (!output_pairs) { return -2; } *out = *output_pairs; return 0; case VPT_TYPE_ATTR: { VALUE_PAIR *vp; vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; if (!pairparsevalue(vp, answer)) { pairfree(&vp); return -2; } *out = vp; return 0; } default: rad_assert(0); } return -1; }
/* * 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; }
/* * Attach the EAP-TLS module. */ static int eaptls_attach(CONF_SECTION *cs, void **instance) { EAP_TLS_CONF *conf; eap_tls_t *inst; /* Store all these values in the data structure for later references */ inst = (eap_tls_t *)malloc(sizeof(*inst)); if (!inst) { radlog(L_ERR, "rlm_eap_tls: out of memory"); return -1; } memset(inst, 0, sizeof(*inst)); /* * Parse the config file & get all the configured values */ conf = (EAP_TLS_CONF *)malloc(sizeof(*conf)); if (conf == NULL) { free(inst); radlog(L_ERR, "rlm_eap_tls: out of memory"); return -1; } memset(conf, 0, sizeof(*conf)); inst->conf = conf; if (cf_section_parse(cs, conf, module_config) < 0) { eaptls_detach(inst); return -1; } /* * The EAP RFC's say 1020, but we're less picky. */ if (conf->fragment_size < 100) { radlog(L_ERR, "rlm_eap_tls: Fragment size is too small."); eaptls_detach(inst); return -1; } /* * The maximum size for a RADIUS packet is 4096, * minus the header (20), Message-Authenticator (18), * and State (18), etc. results in about 4000 bytes of data * that can be devoted *solely* to EAP. */ if (conf->fragment_size > 4000) { radlog(L_ERR, "rlm_eap_tls: Fragment size is too large."); eaptls_detach(inst); return -1; } /* * Account for the EAP header (4), and the EAP-TLS header * (6), as per Section 4.2 of RFC 2716. What's left is * the maximum amount of data we read from a TLS buffer. */ conf->fragment_size -= 10; /* * This magic makes the administrators life HUGELY easier * on initial deployments. * * If the server starts up in debugging mode, AND the * bootstrap command is configured, AND it exists, AND * there is no server certificate */ if (conf->make_cert_command && (debug_flag >= 2)) { struct stat buf; if ((stat(conf->make_cert_command, &buf) == 0) && (stat(conf->certificate_file, &buf) < 0) && (errno == ENOENT) && (radius_exec_program(conf->make_cert_command, NULL, 1, NULL, 0, NULL, NULL, 0) != 0)) { eaptls_detach(inst); return -1; } } /* * Initialize TLS */ inst->ctx = init_tls_ctx(conf); if (inst->ctx == NULL) { eaptls_detach(inst); return -1; } #ifdef HAVE_OPENSSL_OCSP_H /* * Initialize OCSP Revocation Store */ if (conf->ocsp_enable) { inst->store = init_revocation_store(conf); if (inst->store == NULL) { eaptls_detach(inst); return -1; } } #endif HAVE_OPENSSL_OCSP_H if (load_dh_params(inst->ctx, conf->dh_file) < 0) { eaptls_detach(inst); return -1; } if (generate_eph_rsa_key(inst->ctx) < 0) { eaptls_detach(inst); return -1; } if (conf->verify_tmp_dir) { if (chmod(conf->verify_tmp_dir, S_IRWXU) < 0) { radlog(L_ERR, "rlm_eap_tls: Failed changing permissions on %s: %s", conf->verify_tmp_dir, strerror(errno)); eaptls_detach(inst); return -1; } } if (conf->verify_client_cert_cmd && !conf->verify_tmp_dir) { radlog(L_ERR, "rlm_eap_tls: You MUST set the verify directory in order to use verify_client_cmd"); eaptls_detach(inst); return -1; } *instance = inst; return 0; }
/** Expand the RHS of a template * * @note Length of expanded string can be found with talloc_array_length(*out) - 1 * * @param out where to write a pointer to the newly allocated buffer. * @param request Current request. * @param vpt to evaluate. * @return -1 on error, else 0. */ int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR *vp; *out = NULL; rad_assert(vpt->type != TMPL_TYPE_LIST); switch (vpt->type) { case TMPL_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); *out = talloc_typed_strdup(request, vpt->name); break; case TMPL_TYPE_EXEC: EVAL_DEBUG("TMPL EXEC"); *out = talloc_array(request, char, 1024); if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) { TALLOC_FREE(*out); return -1; } break; case TMPL_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case TMPL_TYPE_XLAT: EVAL_DEBUG("TMPL XLAT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case TMPL_TYPE_XLAT_STRUCT: EVAL_DEBUG("TMPL XLAT_STRUCT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat_struct(out, request, vpt->tmpl_xlat, NULL, NULL) < 0) { rad_assert(!*out); return -1; } RDEBUG2("EXPAND %s", vpt->name); /* xlat_struct doesn't do this */ RDEBUG2(" --> %s", *out); break; case TMPL_TYPE_ATTR: { int ret; EVAL_DEBUG("TMPL ATTR"); ret = tmpl_find_vp(&vp, request, vpt); if (ret < 0) return -2; *out = vp_aprint_value(request, vp, false); if (!*out) return -1; } break; case TMPL_TYPE_DATA: case TMPL_TYPE_REGEX_STRUCT: rad_assert(0 == 1); /* FALL-THROUGH */ default: break; } EVAL_DEBUG("Expand tmpl --> %s", *out); return 0; }
/* * Do the MS-CHAP stuff. * * This function is here so that all of the MS-CHAP related * authentication is in one place, and we can perhaps later replace * it with code to call winbindd, or something similar. */ static int do_mschap(rlm_mschap_t *inst, REQUEST *request, VALUE_PAIR *password, uint8_t *challenge, uint8_t *response, uint8_t *nthashhash, int do_ntlm_auth) { uint8_t calculated[24]; /* * Do normal authentication. */ if (!do_ntlm_auth) { /* * No password: can't do authentication. */ if (!password) { RDEBUG2("FAILED: No NT/LM-Password. Cannot perform authentication."); return -1; } smbdes_mschap(password->vp_strvalue, challenge, calculated); if (rad_digest_cmp(response, calculated, 24) != 0) { return -1; } /* * If the password exists, and is an NT-Password, * then calculate the hash of the NT hash. Doing this * here minimizes work for later. */ if (password && (password->attribute == PW_NT_PASSWORD)) { fr_md4_calc(nthashhash, password->vp_octets, 16); } else { memset(nthashhash, 0, 16); } } else { /* run ntlm_auth */ int result; char buffer[256]; memset(nthashhash, 0, 16); /* * Run the program, and expect that we get 16 */ result = radius_exec_program(inst->ntlm_auth, request, TRUE, /* wait */ buffer, sizeof(buffer), NULL, NULL, 1); if (result != 0) { char *p; VALUE_PAIR *vp = NULL; RDEBUG2("External script failed."); vp = pairmake("Module-Failure-Message", "", T_OP_EQ); if (!vp) { radlog_request(L_ERR, 0, request, "No memory to allocate Module-Failure-Message"); return RLM_MODULE_FAIL; } p = strchr(buffer, '\n'); if (p) *p = '\0'; snprintf(vp->vp_strvalue, sizeof(vp->vp_strvalue), "%s: External script says %s", inst->xlat_name, buffer); vp->length = strlen(vp->vp_strvalue); pairadd(&request->packet->vps, vp); return -1; } /* * Parse the answer as an nthashhash. * * ntlm_auth currently returns: * NT_KEY: 000102030405060708090a0b0c0d0e0f */ if (memcmp(buffer, "NT_KEY: ", 8) != 0) { RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY"); return -1; } /* * Check the length. It should be at least 32, * with an LF at the end. */ if (strlen(buffer + 8) < 32) { RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length"); return -1; } /* * Update the NT hash hash, from the NT key. */ if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) { RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values"); return -1; } } return 0; }
void exec_trigger(REQUEST *request, CONF_SECTION *cs, const char *name) { CONF_SECTION *subcs; CONF_ITEM *ci; CONF_PAIR *cp; const char *attr; const char *value; VALUE_PAIR *vp; /* * Use global "trigger" section if no local config is given. */ if (!cs) { cs = mainconfig.config; attr = name; } else { /* * Try to use pair name, rather than reference. */ attr = strrchr(name, '.'); if (attr) { attr++; } else { attr = name; } } /* * Find local "trigger" subsection. If it isn't found, * try using the global "trigger" section, and reset the * reference to the full path, rather than the sub-path. */ subcs = cf_section_sub_find(cs, "trigger"); if (!subcs && (cs != mainconfig.config)) { subcs = cf_section_sub_find(mainconfig.config, "trigger"); attr = name; } if (!subcs) { DEBUG3("No trigger subsection: ignoring trigger %s", name); return; } ci = cf_reference_item(subcs, mainconfig.config, attr); if (!ci) { DEBUG3("No such item in trigger section: %s", attr); return; } if (!cf_item_is_pair(ci)) { DEBUG2("Trigger is not a configuration variable: %s", attr); return; } cp = cf_itemtopair(ci); if (!cp) return; value = cf_pair_value(cp); if (!value) { DEBUG2("Trigger has no value: %s", name); return; } /* * May be called for Status-Server packets. */ vp = NULL; if (request && request->packet) vp = request->packet->vps; DEBUG("Trigger %s -> %s", name, value); radius_exec_program(value, request, 0, NULL, 0, vp, NULL, 1); }
/** Execute a trigger - call an executable to process an event * * @param request The current request. * @param cs to search for triggers in. If not NULL, only the portion after the last '.' * in name is used for the trigger. If cs is NULL, the entire name is used to find * the trigger in the global trigger section. * @param name the path relative to the global trigger section ending in the trigger name * e.g. module.ldap.pool.start. * @param quench whether to rate limit triggers. */ void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, bool quench) { CONF_SECTION *subcs; CONF_ITEM *ci; CONF_PAIR *cp; char const *attr; char const *value; VALUE_PAIR *vp; bool alloc = false; /* * Use global "trigger" section if no local config is given. */ if (!cs) { cs = exec_trigger_main; attr = name; } else { /* * Try to use pair name, rather than reference. */ attr = strrchr(name, '.'); if (attr) { attr++; } else { attr = name; } } /* * Find local "trigger" subsection. If it isn't found, * try using the global "trigger" section, and reset the * reference to the full path, rather than the sub-path. */ subcs = cf_section_sub_find(cs, "trigger"); if (!subcs && exec_trigger_main && (cs != exec_trigger_main)) { subcs = exec_trigger_subcs; attr = name; } if (!subcs) return; ci = cf_reference_item(subcs, exec_trigger_main, attr); if (!ci) { ERROR("No such item in trigger section: %s", attr); return; } if (!cf_item_is_pair(ci)) { ERROR("Trigger is not a configuration variable: %s", attr); return; } cp = cf_item_to_pair(ci); if (!cp) return; value = cf_pair_value(cp); if (!value) { ERROR("Trigger has no value: %s", name); return; } /* * May be called for Status-Server packets. */ vp = NULL; if (request && request->packet) vp = request->packet->vps; /* * Perform periodic quenching. */ if (quench) { time_t *last_time; last_time = cf_data_find(cs, value); if (!last_time) { last_time = rad_malloc(sizeof(*last_time)); *last_time = 0; if (cf_data_add(cs, value, last_time, time_free) < 0) { free(last_time); last_time = NULL; } } /* * Send the quenched traps at most once per second. */ if (last_time) { time_t now = time(NULL); if (*last_time == now) return; *last_time = now; } } /* * radius_exec_program always needs a request. */ if (!request) { request = request_alloc(NULL); alloc = true; } DEBUG("Trigger %s -> %s", name, value); radius_exec_program(request, NULL, 0, NULL, request, value, vp, false, true, EXEC_TIMEOUT); if (alloc) talloc_free(request); }
/* * Do the MS-CHAP stuff. * * This function is here so that all of the MS-CHAP related * authentication is in one place, and we can perhaps later replace * it with code to call winbindd, or something similar. */ static int do_mschap(rlm_mschap_t *inst, REQUEST *request, VALUE_PAIR *password, uint8_t *challenge, uint8_t *response, uint8_t *nthashhash, int do_ntlm_auth) { uint8_t calculated[24]; /* * Do normal authentication. */ if (!do_ntlm_auth) { /* * No password: can't do authentication. */ if (!password) { RDEBUG2("FAILED: No NT/LM-Password. Cannot perform authentication."); return -1; } smbdes_mschap(password->vp_strvalue, challenge, calculated); if (memcmp(response, calculated, 24) != 0) { return -1; } /* * If the password exists, and is an NT-Password, * then calculate the hash of the NT hash. Doing this * here minimizes work for later. */ if (password && (password->attribute == PW_NT_PASSWORD)) { fr_md4_calc(nthashhash, password->vp_octets, 16); } else { memset(nthashhash, 0, 16); } } else { /* run ntlm_auth */ int result; char buffer[256]; memset(nthashhash, 0, 16); /* * Run the program, and expect that we get 16 */ result = radius_exec_program(inst->ntlm_auth, request, TRUE, /* wait */ buffer, sizeof(buffer), NULL, NULL, 1); if (result != 0) { RDEBUG2("External script failed."); return -1; } /* * Parse the answer as an nthashhash. * * ntlm_auth currently returns: * NT_KEY: 000102030405060708090a0b0c0d0e0f */ if (memcmp(buffer, "NT_KEY: ", 8) != 0) { RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY"); return -1; } /* * Check the length. It should be at least 32, * with an LF at the end. */ if (strlen(buffer + 8) < 32) { RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length"); return -1; } /* * Update the NT hash hash, from the NT key. */ if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) { RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values"); return -1; } } return 0; }
/** Expand the RHS of a template * * @note Length of expanded string can be found with talloc_array_length(*out) - 1 * * @param out where to write a pointer to the newly allocated buffer. * @param request Current request. * @param vpt to evaluate. * @return -1 on error, else 0. */ static int radius_expand_tmpl(char **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR *vp; *out = NULL; rad_assert(vpt->type != VPT_TYPE_LIST); switch (vpt->type) { case VPT_TYPE_LITERAL: EVAL_DEBUG("TMPL LITERAL"); *out = talloc_strdup(request, vpt->name); break; case VPT_TYPE_EXEC: EVAL_DEBUG("TMPL EXEC"); *out = talloc_array(request, char, 1024); if (radius_exec_program(request, vpt->name, true, false, *out, 1024, EXEC_TIMEOUT, NULL, NULL) != 0) { TALLOC_FREE(*out); return -1; } break; case VPT_TYPE_REGEX: EVAL_DEBUG("TMPL REGEX"); if (strchr(vpt->name, '%') == NULL) { *out = talloc_strdup(request, vpt->name); break; } /* FALL-THROUGH */ case VPT_TYPE_XLAT: EVAL_DEBUG("TMPL XLAT"); /* Error in expansion, this is distinct from zero length expansion */ if (radius_axlat(out, request, vpt->name, NULL, NULL) < 0) { rad_assert(!*out); return -1; } break; case VPT_TYPE_ATTR: EVAL_DEBUG("TMPL ATTR"); if (radius_vpt_get_vp(&vp, request, vpt) < 0) { return -2; } *out = vp_aprint(request, vp); if (!*out) { return -1; } break; case VPT_TYPE_DATA: rad_assert(0 == 1); /* FALL-THROUGH */ default: break; } EVAL_DEBUG("Expand tmpl --> %s", *out); return 0; }
/* * Dispatch an exec method */ static rlm_rcode_t exec_dispatch(void *instance, REQUEST *request) { rlm_exec_t *inst = (rlm_exec_t *)instance; rlm_rcode_t rcode; int status; VALUE_PAIR **input_pairs = NULL, **output_pairs = NULL; VALUE_PAIR *answer = NULL; char out[1024]; /* * We need a program to execute. */ if (!inst->program) { ERROR("rlm_exec (%s): We require a program to execute", inst->xlat_name); return RLM_MODULE_FAIL; } /* * See if we're supposed to execute it now. */ if (!((inst->packet_code == 0) || (request->packet->code == inst->packet_code) || (request->reply->code == inst->packet_code) #ifdef WITH_PROXY || (request->proxy && (request->proxy->code == inst->packet_code)) || (request->proxy_reply && (request->proxy_reply->code == inst->packet_code)) #endif )) { RDEBUG2("Packet type is not %s. Not executing.", inst->packet_type); return RLM_MODULE_NOOP; } /* * Decide what input/output the program takes. */ if (inst->input) { input_pairs = radius_list(request, inst->input_list); if (!input_pairs) { return RLM_MODULE_INVALID; } } if (inst->output) { output_pairs = radius_list(request, inst->output_list); if (!output_pairs) { return RLM_MODULE_INVALID; } } /* * This function does it's own xlat of the input program * to execute. */ status = radius_exec_program(request, inst->program, inst->wait, inst->shell_escape, out, sizeof(out), inst->timeout, inst->input ? *input_pairs : NULL, inst->output ? &answer : NULL); rcode = rlm_exec_status2rcode(request, out, strlen(out), status); /* * Move the answer over to the output pairs. * * If we're not waiting, then there are no output pairs. */ if (inst->output) { pairmove(request, output_pairs, &answer); } pairfree(&answer); 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; }
/* * Do the MS-CHAP stuff. * * This function is here so that all of the MS-CHAP related * authentication is in one place, and we can perhaps later replace * it with code to call winbindd, or something similar. */ static int do_mschap(rlm_mschap_t *inst, REQUEST *request, VALUE_PAIR *password, uint8_t *challenge, uint8_t *response, uint8_t *nthashhash) { int do_ntlm_auth = 0; uint8_t calculated[24]; VALUE_PAIR *vp = NULL; /* * If we have ntlm_auth configured, use it unless told * otherwise */ if (inst->ntlm_auth) do_ntlm_auth = 1; /* * If we have an ntlm_auth configuration, then we may * want to use it. */ vp = pairfind(request->config_items, PW_MS_CHAP_USE_NTLM_AUTH); if (vp) do_ntlm_auth = vp->vp_integer; /* * No ntlm_auth configured, attribute to tell us to * use it exists, and we're told to use it. We don't * know what to do... */ if (!inst->ntlm_auth && do_ntlm_auth) { RDEBUG2("Asked to use ntlm_auth, but it was not configured in the mschap{} section."); return -1; } /* * Do normal authentication. */ if (!do_ntlm_auth) { /* * No password: can't do authentication. */ if (!password) { RDEBUG2("FAILED: No NT/LM-Password. Cannot perform authentication."); return -1; } smbdes_mschap(password->vp_strvalue, challenge, calculated); if (memcmp(response, calculated, 24) != 0) { return -1; } /* * If the password exists, and is an NT-Password, * then calculate the hash of the NT hash. Doing this * here minimizes work for later. */ if (password && (password->attribute == PW_NT_PASSWORD)) { fr_md4_calc(nthashhash, password->vp_octets, 16); } else { memset(nthashhash, 0, 16); } } else { /* run ntlm_auth */ int result; char buffer[256]; memset(nthashhash, 0, 16); /* * Run the program, and expect that we get 16 */ result = radius_exec_program(inst->ntlm_auth, request, TRUE, /* wait */ buffer, sizeof(buffer), NULL, NULL, 1); if (result != 0) { RDEBUG2("External script failed."); return -1; } /* * Parse the answer as an nthashhash. * * ntlm_auth currently returns: * NT_KEY: 000102030405060708090a0b0c0d0e0f */ if (memcmp(buffer, "NT_KEY: ", 8) != 0) { RDEBUG2("Invalid output from ntlm_auth: expecting NT_KEY"); return -1; } /* * Check the length. It should be at least 32, * with an LF at the end. */ if (strlen(buffer + 8) < 32) { RDEBUG2("Invalid output from ntlm_auth: NT_KEY has unexpected length"); return -1; } /* * Update the NT hash hash, from the NT key. */ if (fr_hex2bin(buffer + 8, nthashhash, 16) != 16) { RDEBUG2("Invalid output from ntlm_auth: NT_KEY has non-hex values"); return -1; } } return 0; }