25 RCSID(
"$Id: c29d039f246b55c2f29abd5e6220f2b7f02c3d6f $")
27 #include <freeradius-devel/curl/base.h>
28 #include <freeradius-devel/server/base.h>
29 #include <freeradius-devel/server/cf_priv.h>
30 #include <freeradius-devel/server/global_lib.h>
31 #include <freeradius-devel/server/module_rlm.h>
32 #include <freeradius-devel/server/tmpl_dcursor.h>
33 #include <freeradius-devel/util/slab.h>
34 #include <freeradius-devel/util/token.h>
36 #include <freeradius-devel/unlang/call_env.h>
165 *
out = curl_slist_append(*
out,
vp->vp_strvalue);
170 if (!elems_added)
RDEBUG3(
"There were no %s elements found", dict_attr->name);
180 fr_value_box_list_t
const *list = lists;
183 size_t i, list_count = talloc_array_length(lists);
185 for (i = 0; i < list_count; i++) {
186 while ((vb = fr_value_box_list_next(list, vb))) {
187 *
out = curl_slist_append(*
out, vb->vb_strvalue);
215 fr_value_box_list_t
const *list = lists;
216 size_t i, list_count = talloc_array_length(lists);
218 fr_sbuff_init_talloc(
uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
221 for (i = 0; i < list_count; i++) {
222 while ((vb = fr_value_box_list_next(list, vb))) {
233 RERROR(
"Failed casting %pV to string", vb);
244 RDEBUG2(
"No values to add to \"%s\" header", prefix);
268 RDEBUG2(
"Trying to set attachment: %s", str);
270 if (strncmp(str,
"/", 1) == 0) {
271 RDEBUG2(
"File attachments cannot be an absolute path");
275 if (strncmp(str,
"..", 2) == 0) {
276 RDEBUG2(
"Cannot access values outside of template_directory");
282 RDEBUG2(
"Cannot copy filename");
287 part = curl_mime_addpart(mime);
288 curl_mime_encoder(part,
"base64");
289 if (curl_mime_filedata(part, path_buffer->buff) != CURLE_OK) {
290 REDEBUG2(
"Cannot add file attachment");
310 fr_sbuff_init_talloc(
uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
317 strlen(
inst->envelope_address)) < 0)) {
322 *
out = curl_slist_append(*
out, sbuff.buff);
336 int recipients_set = 0;
348 if (recipients_set) {
349 RDEBUG2(
"Recipients were generated from \"&SMTP-Recipients\" and/or recipients in the config");
350 return recipients_set;
352 RDEBUG2(
"No addresses were found in \"&SMTP-Recipients\"");
369 RDEBUG2(
"%d recipients set", recipients_set);
370 return recipients_set;
385 if (fr_value_box_list_initialised(&call_env->
headers)) {
386 while ((vb = fr_value_box_list_next(&call_env->
headers, vb))) {
387 RDEBUG2(
"Adding header \"%pV\"", vb);
388 uctx->header = curl_slist_append(
uctx->header, vb->vb_strvalue);
394 RDEBUG2(
"From: header could not be added");
409 RDEBUG2(
"Header elements could not be added");
414 if (
inst->set_date) {
417 uctx->header = curl_slist_append(
uctx->header,
uctx->time_str);
420 RDEBUG2(
"Finished generating the curl_slist for the header elements");
427 static size_t body_source(
char *ptr,
size_t size,
size_t nmemb,
void *mail_ctx)
438 RDEBUG2(
"vp could not be found for the body element");
474 curl_mime *mime_body;
476 int body_elements = 0;
479 MEM(mime_body = curl_mime_init(
uctx->randle->candle));
488 MEM(part = curl_mime_addpart(mime_body));
490 curl_mime_encoder(part,
"8bit");
495 RDEBUG2(
"initialized %d body element part(s)", body_elements);
503 MEM(part = curl_mime_addpart(mime));
504 curl_mime_subparts(part, mime_body);
505 MEM(curl_mime_type(part,
"multipart/mixed") == CURLE_OK);
506 uctx->body_header = curl_slist_append(NULL,
"Content-Disposition: inline");
507 curl_mime_headers(part,
uctx->body_header, 1);
509 return body_elements;
518 int attachments_set = 0;
523 fr_value_box_list_t
const *list = call_env->
attachments;
524 size_t i, list_count = talloc_array_length(call_env->
attachments);
528 if (!
inst->template_dir)
return 0;
531 fr_sbuff_init_talloc(
uctx, &path_buffer, &sbuff_ctx, talloc_array_length(
inst->template_dir) + 128, SIZE_MAX);
537 if (
inst->template_dir[talloc_array_length(
inst->template_dir)-2] !=
'/'){
538 RDEBUG2(
"Adding / to end of template_dir");
543 fr_sbuff_marker(&m, &path_buffer);
546 for (i = 0; i < list_count; i++) {
547 while ((vb = fr_value_box_list_next(list, vb))) {
555 return attachments_set;
564 RDEBUG2(
"Forcefully cancelling pending SMTP request");
567 if (ret != CURLM_OK) {
568 RERROR(
"Failed removing curl handle from multi-handle: %s (%i)", curl_multi_strerror(ret), ret);
572 smtp_slab_release(randle);
593 curl_out_valid = curl_easy_getinfo(randle->
candle, CURLINFO_SSL_VERIFYRESULT, &curl_out);
594 if (curl_out_valid == CURLE_OK){
595 RDEBUG2(
"server certificate %s verified", curl_out ?
"was" :
"not");
597 RDEBUG2(
"server certificate result not found");
600 if (randle->
result != CURLE_OK) {
601 CURLcode result = randle->
result;
602 smtp_slab_release(randle);
604 case CURLE_PEER_FAILED_VERIFICATION:
605 case CURLE_LOGIN_DENIED:
613 smtp_slab_release(randle);
647 RDEBUG2(
"Attribute \"smtp-body\" is required for smtp");
652 RDEBUG2(
"At least one of \"sender_address\" or \"envelope_address\" in the config, or \"SMTP-Sender-Address\" in the request is needed");
654 if (randle) smtp_slab_release(randle);
667 RDEBUG2(
"A handle could not be allocated for the request");
676 .mime = curl_mime_init(randle->
candle),
685 if (call_env->
username.vb_strvalue) {
688 if (!call_env->
password.vb_strvalue)
goto skip_auth;
691 RDEBUG2(
"Username and password set");
697 (
inst->envelope_address ?
inst->envelope_address :
702 REDEBUG(
"At least one recipient is required to send an email");
709 REDEBUG(
"The header slist could not be generated");
722 REDEBUG(
"The body could not be generated");
728 RDEBUG2(
"No files were attached to the email");
760 smtp_slab_release(randle);
781 inst->conn_config.reuse.num_children = 1;
787 #define SMTP_COMMON_CLEANUP \
788 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t); \
789 if (mail_ctx->mime) curl_mime_free(mail_ctx->mime); \
790 if (mail_ctx->header) curl_slist_free_all(mail_ctx->header); \
791 if (mail_ctx->recipients) curl_slist_free_all(mail_ctx->recipients)
816 randle->
uctx = mail_ctx;
835 mail_ctx->
randle = randle;
836 randle->
uctx = mail_ctx;
837 randle->
candle = curl_easy_init();
851 #if CURL_AT_LEAST_VERSION(7,45,0)
855 #if CURL_AT_LEAST_VERSION(7,85,0)
877 randle->
candle = curl_easy_init();
910 inst,
false,
false))) {
911 ERROR(
"Connection handle pool instantiation failed");
916 inst,
false,
true))) {
917 ERROR(
"Connection handle pool instantiation failed");
922 if (!mhandle)
return -1;
973 &
FR_SBUFF_IN(to_parse, talloc_array_length(to_parse) - 1),
1005 .pair.dflt =
"&SMTP-Recipients[*]", .pair.dflt_quote =
T_BARE_WORD },
1007 .pair.dflt =
"&SMTP-TO[*]", .pair.dflt_quote =
T_BARE_WORD },
1009 .pair.dflt =
"&SMTP-CC[*]", .pair.dflt_quote =
T_BARE_WORD },
1011 .pair.dflt =
"&SMTP-BCC[*]", .pair.dflt_quote =
T_BARE_WORD },
1013 .pair.dflt =
"&SMTP-Attachments[*]", .pair.dflt_quote =
T_BARE_WORD },
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr)
Remove a call_env_parsed_t from the list of parsed call envs.
call_env_parsed_t * call_env_parsed_add(TALLOC_CTX *ctx, call_env_parsed_head_t *head, call_env_parser_t const *rule)
Allocate a new call_env_parsed_t structure and add it to the list of parsed call envs.
void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t const *tmpl)
Assign a tmpl to a call_env_parsed_t.
#define CALL_ENV_TERMINATOR
#define FR_CALL_ENV_PARSE_OFFSET(_name, _cast_type, _flags, _struct, _field, _parse_field)
Specify a call_env_parser_t which writes out runtime results and the result of the parsing phase to t...
#define FR_CALL_ENV_METHOD_OUT(_inst)
Helper macro for populating the size/type fields of a call_env_method_t from the output structure typ...
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl must contain an attribute reference.
@ CALL_ENV_FLAG_SECRET
The value is a secret, and should not be logged.
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
#define FR_CALL_ENV_OFFSET(_name, _cast_type, _flags, _struct, _field)
Specify a call_env_parser_t which writes out runtime results to the specified field.
#define CONF_PARSER_TERMINATOR
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Defines a CONF_PAIR to C data type mapping.
Common header for all CONF_* types.
Configuration AVP similar to a fr_pair_t.
A section grouping multiple CONF_PAIR.
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
#define cf_log_err(_cf, _fmt,...)
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
#define cf_log_perr(_cf, _fmt,...)
#define cf_item_next(_ci, _curr)
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, bool multiplex)
CURLcode result
Result of executing the request.
#define FR_CURL_SET_OPTION(_x, _y)
uint64_t transfers
How many transfers are current in progress.
CURLM * mandle
The multi handle.
void * uctx
Private data for the module using the API.
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *creq)
Sends a request using libcurl.
CURL * candle
Request specific handle.
Uctx data for timer and I/O functions.
Structure representing an individual request being passed to curl for processing.
#define fr_dbuff_used(_dbuff_or_marker)
Return the number of bytes remaining between the start of the dbuff or marker and the current positio...
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
#define fr_dbuff_remaining(_dbuff_or_marker)
Return the number of bytes remaining between the dbuff or marker and the end of the buffer.
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
fr_dcursor_eval_t void const * uctx
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
static fr_time_delta_t timeout
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Specifies an attribute which must be present for the module to function.
Specifies a dictionary which must be loaded/loadable for the module to function.
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
#define GLOBAL_LIB_TERMINATOR
Structure to define how to initialise libraries with global configuration.
int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle)
int fr_curl_easy_tls_init(fr_curl_io_request_t *randle, fr_curl_tls_t const *conf)
global_lib_autoinst_t fr_curl_autoinst
conf_parser_t fr_curl_conn_config[]
conf_parser_t fr_curl_tls_config[]
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
#define REDEBUG2(fmt,...)
@ FR_TYPE_STRING
String of printable characters.
void * env_data
Per call environment data.
module_instance_t const * mi
Instance of the module being instantiated.
void * thread
Thread specific instance data.
void * rctx
Resume ctx that a module previously set.
fr_event_list_t * el
Event list to register any IO handlers and timers against.
void * thread
Thread instance data.
module_instance_t const * mi
Instance of the module being instantiated.
module_instance_t * mi
Instance of the module being instantiated.
Temporary structure to hold arguments for module calls.
Temporary structure to hold arguments for instantiation calls.
Temporary structure to hold arguments for thread_instantiation calls.
module_t common
Common fields presented by all modules.
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
#define RETURN_MODULE_REJECT
#define RETURN_MODULE_INVALID
rlm_rcode_t
Return codes indicating the result of the module call.
static const call_env_method_t auth_env
struct curl_slist * recipients
static int header_source(fr_mail_ctx_t *uctx, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
fr_value_box_list_t * bcc_addrs
The address(es) used for the Bcc: header.
global_lib_autoinst_t const *const rlm_smtp_lib[]
static unlang_action_t mod_mail(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
static int generate_from_header(fr_mail_ctx_t *uctx, struct curl_slist **out, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
Generate the From: header.
static int value_box_list_to_slist(struct curl_slist **out, fr_value_box_list_t const *lists)
Transform an array of value box lists to entries in a CURL slist.
static int smtp_persist_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
static int value_box_list_to_header(fr_mail_ctx_t *uctx, struct curl_slist **out, fr_value_box_list_t const *lists, const char *prefix)
Converts an array of value box lists to a curl_slist with a prefix.
fr_value_box_t username
Value to use for user name.
static int attachments_source(fr_mail_ctx_t *uctx, curl_mime *mime, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
static int smtp_persist_conn_init(fr_curl_io_request_t *randle, void *uctx)
smtp_slab_list_t * slab_persist
Slab list for persistent connections.
static int smtp_conn_common_init(fr_curl_io_request_t *randle, rlm_smtp_t const *inst)
fr_curl_io_request_t * randle
static fr_dict_attr_t const * attr_smtp_body
static const call_env_method_t method_env
static int smtp_onetime_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
static fr_dict_t const * dict_freeradius
fr_dict_attr_autoload_t rlm_smtp_dict_attr[]
fr_time_delta_t timeout
Timeout for connection and server response.
static int recipients_source(fr_mail_ctx_t *uctx, rlm_smtp_env_t const *call_env)
fr_dict_autoload_t rlm_smtp_dict[]
fr_curl_conn_config_t conn_config
Reusable CURL handle config.
tmpl_t * username_tmpl
tmpl expanded to populate username
static void smtp_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
char const * uri
URI of smtp server.
static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
Parse the header section into tmpls for producing email headers.
fr_value_box_t username
User to authenticate as when sending emails.
fr_value_box_t password
Value to use for password.
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
struct curl_slist * header
static unlang_action_t smtp_io_module_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Callback to process response of SMTP server.
tmpl_t * password_tmpl
tmpl expanded to populate password
static fr_dict_attr_t const * attr_smtp_header
static int smtp_persist_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
fr_value_box_list_t * cc_addrs
The address(es) used for the Cc: header.
fr_value_box_list_t * to_addrs
The address(es) used for the To: header.
#define SMTP_COMMON_CLEANUP
static int body_init(fr_mail_ctx_t *uctx, curl_mime *mime)
fr_value_box_list_t headers
Entries to add to email header.
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
struct curl_slist * body_header
static int smtp_onetime_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
fr_value_box_t password
Password for authenticated mails.
char const * envelope_address
The address used to send the message.
static int str_to_attachments(fr_mail_ctx_t *uctx, curl_mime *mime, char const *str, size_t len, fr_sbuff_t *path_buffer, fr_sbuff_marker_t *m)
fr_value_box_list_t * recipient_addrs
The address(es) used as recipients. Overrides elements in to, cc and bcc.
static int smtp_onetime_conn_init(fr_curl_io_request_t *randle, void *uctx)
tmpl_t * username_tmpl
The tmpl used to produce the above.
static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
char const * template_dir
The directory that contains all email attachments.
fr_curl_tls_t tls
Used for handled all tls specific curl components.
static int da_to_slist(fr_mail_ctx_t *uctx, struct curl_slist **out, const fr_dict_attr_t *dict_attr)
static const conf_parser_t module_config[]
static int smtp_mail_ctx_free(fr_mail_ctx_t *mail_ctx)
fr_value_box_list_t * attachments
List of files to attach.
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
static int mod_instantiate(module_inst_ctx_t const *mctx)
fr_curl_handle_t * mhandle
Thread specific multi handle.
smtp_slab_list_t * slab_onetime
Slab list for onetime use connections.
fr_value_box_list_t * sender_address
The address(es) used to generate the From: header.
Call environment for SMTP authentication.
Call environment for sending emails.
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
ssize_t fr_sbuff_in_bstrncpy(fr_sbuff_t *sbuff, char const *str, size_t len)
Copy bytes into the sbuff up to the first \0.
ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
#define fr_sbuff_set(_dst, _src)
#define FR_SBUFF_IN(_start, _len_or_end)
#define fr_sbuff_buff(_sbuff_or_marker)
#define FR_SBUFF_OUT(_start, _len_or_end)
#define fr_sbuff_in_char(_sbuff,...)
Talloc sbuff extension structure.
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
void * data
Module's instance data.
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Named methods exported by a module.
int tmpl_resolve(tmpl_t *vpt, tmpl_res_rules_t const *tr_rules))
Attempt to resolve functions and attributes in xlats and attribute references.
ssize_t tmpl_afrom_substr(TALLOC_CTX *ctx, tmpl_t **out, fr_sbuff_t *in, fr_token_t quote, fr_sbuff_parse_rules_t const *p_rules, tmpl_rules_t const *t_rules))
Convert an arbitrary string into a tmpl_t.
#define tmpl_is_data(vpt)
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
#define tmpl_needs_resolving(vpt)
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Optional arguments passed to vp_tmpl functions.
#define FR_SLAB_FUNCS(_name, _type)
Define type specific wrapper functions for slabs and slab elements.
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
#define fr_time()
Allow us to arbitrarily manipulate time.
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Stores an attribute, a value and various bits of other data.
#define talloc_get_type_abort_const
size_t fr_time_strftime_local(fr_sbuff_t *out, fr_time_t time, char const *fmt)
Copy a time string (local timezone) to an sbuff.
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
A time delta, a difference in time measured in nanoseconds.
#define fr_pair_dcursor_by_da_init(_cursor, _list, _da)
Initialise a cursor that will return only attributes matching the specified fr_dict_attr_t.
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
int fr_value_box_cast_in_place(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv)
Convert one type of fr_value_box_t to another in place.
static size_t char ** out