25 RCSID(
"$Id: 8d333217075dd17dd383f6927421eb0411af0bb7 $")
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) {
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;
488 MEM(part = curl_mime_addpart(mime_body));
490 curl_mime_encoder(part,
"8bit");
491 curl_mime_data_cb(part,
vp->vp_length,
body_source, NULL, NULL, uctx);
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");
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))) {
548 attachments_set +=
str_to_attachments(uctx, mime, vb->vb_strvalue, vb->vb_length, &path_buffer, &m);
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),
1004 .pair.dflt =
"&SMTP-Recipients[*]", .pair.dflt_quote =
T_BARE_WORD },
1006 .pair.dflt =
"&SMTP-TO[*]", .pair.dflt_quote =
T_BARE_WORD },
1008 .pair.dflt =
"&SMTP-CC[*]", .pair.dflt_quote =
T_BARE_WORD },
1010 .pair.dflt =
"&SMTP-BCC[*]", .pair.dflt_quote =
T_BARE_WORD },
1012 .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...
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _ident2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
@ 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_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_item_next(_ci, _prev)
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
#define cf_log_perr(_cf, _fmt,...)
#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.
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.
void *_CONST data
Module instance's parsed configuration.
#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,...)
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
@ FR_TYPE_STRING
String of printable characters.
void * env_data
Per call environment data.
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.
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
void * thread
Thread instance data.
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
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.
Specifies a module method identifier.
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 char const *section_name1, UNUSED char const *section_name2, UNUSED void const *data, 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.
@ MODULE_TYPE_THREAD_SAFE
Module is threadsafe.
#define MODULE_NAME_TERMINATOR
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.
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