The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
rlm_smtp.c
Go to the documentation of this file.
1/*
2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: c29d039f246b55c2f29abd5e6220f2b7f02c3d6f $
19 * @file rlm_smtp.c
20 * @brief smtp server authentication.
21 *
22 * @copyright 2020 The FreeRADIUS server project
23 * @copyright 2020 Network RADIUS SAS (legal@networkradius.com)
24 */
25RCSID("$Id: c29d039f246b55c2f29abd5e6220f2b7f02c3d6f $")
26
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>
35
36#include <freeradius-devel/unlang/call_env.h>
37
39
42 { .out = &dict_freeradius, .proto = "freeradius"},
43 { NULL }
44};
45
48
51 { .out = &attr_smtp_header, .name = "SMTP-Mail-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
52 { .out = &attr_smtp_body, .name = "SMTP-Mail-Body", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
53 { NULL },
54};
55
56extern global_lib_autoinst_t const * const rlm_smtp_lib[];
61
62/** Call environment for sending emails.
63*/
64typedef struct {
65 fr_value_box_t username; //!< User to authenticate as when sending emails.
66 tmpl_t *username_tmpl; //!< The tmpl used to produce the above.
67 fr_value_box_t password; //!< Password for authenticated mails.
68 fr_value_box_list_t *sender_address; //!< The address(es) used to generate the From: header
69 fr_value_box_list_t *recipient_addrs; //!< The address(es) used as recipients. Overrides elements in to, cc and bcc
70 fr_value_box_list_t *to_addrs; //!< The address(es) used for the To: header.
71 fr_value_box_list_t *cc_addrs; //!< The address(es) used for the Cc: header.
72 fr_value_box_list_t *bcc_addrs; //!< The address(es) used for the Bcc: header.
73 fr_value_box_list_t *attachments; //!< List of files to attach.
74 fr_value_box_list_t headers; //!< Entries to add to email header.
76
77/** Call environment for SMTP authentication
78 */
79typedef struct {
80 fr_value_box_t username; //!< Value to use for user name
81 tmpl_t *username_tmpl; //!< tmpl expanded to populate username
82 fr_value_box_t password; //!< Value to use for password
83 tmpl_t *password_tmpl; //!< tmpl expanded to populate password
85
86typedef struct {
87 char const *uri; //!< URI of smtp server
88 char const *template_dir; //!< The directory that contains all email attachments
89 char const *envelope_address; //!< The address used to send the message
90
91 fr_time_delta_t timeout; //!< Timeout for connection and server response
92 fr_curl_tls_t tls; //!< Used for handled all tls specific curl components
93
95
96 fr_curl_conn_config_t conn_config; //!< Reusable CURL handle config
98
99/*
100 * A mapping of configuration file names to internal variables.
101 */
102static const conf_parser_t module_config[] = {
103 { FR_CONF_OFFSET("uri", rlm_smtp_t, uri) },
104 { FR_CONF_OFFSET("template_directory", rlm_smtp_t, template_dir) },
105 { FR_CONF_OFFSET("envelope_address", rlm_smtp_t, envelope_address) },
106 { FR_CONF_OFFSET("timeout", rlm_smtp_t, timeout) },
107 { FR_CONF_OFFSET("set_date", rlm_smtp_t, set_date), .dflt = "yes" },
108 { FR_CONF_OFFSET_SUBSECTION("tls", 0, rlm_smtp_t, tls, fr_curl_tls_config) },//!<loading the tls values
109 { FR_CONF_OFFSET_SUBSECTION("connection", 0, rlm_smtp_t, conn_config, fr_curl_conn_config) },
111};
112
113/*
114 * Two types of SMTP connections are used:
115 * - persistent - where the connection can be left established as the same
116 * authentication is used for all mails sent.
117 * - onetime - where the connection is torn down after each use, since
118 * different authentication is needed each time.
119 *
120 * Memory for the handles for each is stored in slabs.
121 */
122
125
126typedef struct {
127 fr_curl_handle_t *mhandle; //!< Thread specific multi handle. Serves as the dispatch and
128 ///< coralling structure for smtp requests
129 smtp_slab_list_t *slab_persist; //!< Slab list for persistent connections.
130 smtp_slab_list_t *slab_onetime; //!< Slab list for onetime use connections.
132
133/*
134 * Holds the context for parsing the email elements
135 */
136typedef struct {
142
143 struct curl_slist *recipients;
144 struct curl_slist *header;
145 struct curl_slist *body_header;
146
148 char time_str[60];
149 curl_mime *mime;
151
152/*
153 * Adds every element associated with a dict_attr to a curl_slist
154 */
155static int da_to_slist(fr_mail_ctx_t *uctx, struct curl_slist **out, const fr_dict_attr_t *dict_attr)
156{
157 request_t *request = ((fr_mail_ctx_t *)uctx)->request;
158 fr_pair_t *vp;
159 int elems_added = 0;
160
161 /* Iterate over the VP and add the string value to the curl_slist */
162 vp = fr_pair_dcursor_by_da_init(&uctx->cursor, &uctx->request->request_pairs, dict_attr);
163
164 while (vp) {
165 *out = curl_slist_append(*out, vp->vp_strvalue);
166 vp = fr_dcursor_next(&uctx->cursor);
167 elems_added++;
168 }
169
170 if (!elems_added) RDEBUG3("There were no %s elements found", dict_attr->name);
171
172 return elems_added;
173}
174
175/** Transform an array of value box lists to entries in a CURL slist
176 *
177 */
178static int value_box_list_to_slist(struct curl_slist **out, fr_value_box_list_t const *lists)
179{
180 fr_value_box_list_t const *list = lists;
181 fr_value_box_t *vb = NULL;
182 int count = 0;
183 size_t i, list_count = talloc_array_length(lists);
184
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);
188 count ++;
189 }
190 list++;
191 }
192
193 return count;
194}
195
196/** Converts an array of value box lists to a curl_slist with a prefix
197 *
198 * @param uctx Mail context.
199 * @param out CURL slist to write to.
200 * @param lists Array of value box lists to copy.
201 * @param prefix to prepend to the output slist
202 * @return
203 * - 1 on success
204 * - 0 if no slist entry was created
205 * - -1 on failure
206 */
207static int value_box_list_to_header(fr_mail_ctx_t *uctx, struct curl_slist **out, fr_value_box_list_t const *lists,
208 const char *prefix)
209{
210 request_t *request = uctx->request;
211 fr_sbuff_t sbuff;
212 fr_sbuff_uctx_talloc_t sbuff_ctx;
213 bool added = false;
214 fr_value_box_t *vb = NULL;
215 fr_value_box_list_t const *list = lists;
216 size_t i, list_count = talloc_array_length(lists);
217
218 fr_sbuff_init_talloc(uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
219 (void) fr_sbuff_in_strcpy(&sbuff, prefix);
220
221 for (i = 0; i < list_count; i++) {
222 while ((vb = fr_value_box_list_next(list, vb))) {
223 /* If there are already values added, add the comma separator */
224 if (added) {
225 if (unlikely(fr_sbuff_in_strcpy(&sbuff, ", ") < 0)) {
226 error:
227 talloc_free(fr_sbuff_buff(&sbuff));
228 return -1;
229 }
230 }
231
232 if ((vb->type != FR_TYPE_STRING) && (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0)) {
233 RERROR("Failed casting %pV to string", vb);
234 goto error;
235 }
236
237 if (unlikely(fr_sbuff_in_strcpy(&sbuff, vb->vb_strvalue) < 0)) goto error;
238 added = true;
239 }
240 list++;
241 }
242
243 if (!added) {
244 RDEBUG2("No values to add to \"%s\" header", prefix);
245 talloc_free(fr_sbuff_buff(&sbuff));
246 return 0;
247 }
248
249 /* Add the generated buffer the the curl_slist */
250 *out = curl_slist_append(*out, fr_sbuff_buff(&sbuff));
251 talloc_free(fr_sbuff_buff(&sbuff));
252 return 1;
253}
254
255/*
256 * Takes a string value and adds it as a file path to upload as an attachment
257 */
258static int str_to_attachments(fr_mail_ctx_t *uctx, curl_mime *mime, char const * str, size_t len,
259 fr_sbuff_t *path_buffer, fr_sbuff_marker_t *m)
260{
261 request_t *request = uctx->request;
262 curl_mimepart *part;
263
264 /* Move to the end of the template directory filepath */
265 fr_sbuff_set(path_buffer, m);
266
267 /* Check to see if the file attachment is valid, skip it if not */
268 RDEBUG2("Trying to set attachment: %s", str);
269
270 if (strncmp(str, "/", 1) == 0) {
271 RDEBUG2("File attachments cannot be an absolute path");
272 return 0;
273 }
274
275 if (strncmp(str, "..", 2) == 0) {
276 RDEBUG2("Cannot access values outside of template_directory");
277 return 0;
278 }
279
280 /* Copy the filename into the buffer */
281 if (fr_sbuff_in_bstrncpy(path_buffer, str, len) < 0) {
282 RDEBUG2("Cannot copy filename");
283 return 0;
284 }
285
286 /* Add the file attachment as a mime encoded part */
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");
291 return 0;
292 }
293
294 return 1;
295}
296
297/** Generate the `From:` header
298 *
299 * Defaults to using `sender_address` values, falls back to `envelope_address`
300 */
301static int generate_from_header(fr_mail_ctx_t *uctx, struct curl_slist **out, rlm_smtp_t const *inst,
302 rlm_smtp_env_t const *call_env)
303{
304 fr_sbuff_t sbuff;
305 fr_sbuff_uctx_talloc_t sbuff_ctx;
306
307 if (call_env->sender_address) return value_box_list_to_header(uctx, &uctx->header, call_env->sender_address, "From: ");
308
309 /* Initialize the buffer for the recipients. Used for FROM */
310 fr_sbuff_init_talloc(uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
311
312 /* Add the preposition for the header element */
313 (void) fr_sbuff_in_strcpy(&sbuff, "From: ");
314
315 /* Copy the envelope address as the From: source */
316 if (unlikely(fr_sbuff_in_bstrncpy(&sbuff, inst->envelope_address,
317 strlen(inst->envelope_address)) < 0)) {
318 talloc_free(fr_sbuff_buff(&sbuff));
319 return -1;
320 }
321
322 *out = curl_slist_append(*out, sbuff.buff);
323
324 /* Free the buffer used to generate the FROM header */
325 talloc_free(fr_sbuff_buff(&sbuff));
326
327 return 0;
328}
329
330/*
331 * Generates a curl_slist of recipients
332 */
333static int recipients_source(fr_mail_ctx_t *uctx, rlm_smtp_env_t const *call_env)
334{
335 request_t *request = uctx->request;
336 int recipients_set = 0;
337
338 /*
339 * Try to load the recipients into the envelope recipients if they are set
340 */
341 if(call_env->recipient_addrs) {
342 recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->recipient_addrs);
343 }
344
345 /*
346 * If any recipients were found, ignore to cc and bcc, return the amount added.
347 **/
348 if (recipients_set) {
349 RDEBUG2("Recipients were generated from \"&SMTP-Recipients\" and/or recipients in the config");
350 return recipients_set;
351 }
352 RDEBUG2("No addresses were found in \"&SMTP-Recipients\"");
353
354 /*
355 * Try to load the to: addresses into the envelope recipients if they are set
356 */
357 if (call_env->to_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->to_addrs);
358
359 /*
360 * Try to load the cc: addresses into the envelope recipients if they are set
361 */
362 if (call_env->cc_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->cc_addrs);
363
364 /*
365 * Try to load the bcc: addresses into the envelope recipients if they are set
366 */
367 if (call_env->bcc_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->bcc_addrs);
368
369 RDEBUG2("%d recipients set", recipients_set);
370 return recipients_set;
371}
372
373/*
374 * Generates a curl_slist of header elements header elements
375 */
376static int header_source(fr_mail_ctx_t *uctx, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
377{
378 fr_sbuff_t time_out;
379 request_t *request = uctx->request;
380 fr_value_box_t *vb = NULL;
381
382 /*
383 * Load in all of the header elements supplied in the config
384 */
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);
389 }
390 }
391
392 /* Add the From: line */
393 if (unlikely(generate_from_header(uctx, &uctx->header, inst, call_env) < 0)) {
394 RDEBUG2("From: header could not be added");
395 return -1;
396 }
397
398 /* Add the TO: line if there is one provided in the request by SMTP-TO */
399 value_box_list_to_header(uctx, &uctx->header, call_env->to_addrs, "To: ");
400
401 /* Add the CC: line if there is one provided in the request by SMTP-CC */
402 value_box_list_to_header(uctx, &uctx->header, call_env->cc_addrs, "Cc: ");
403
404 /* Add all the generic header elements in the request */
405 da_to_slist(uctx, &uctx->header, attr_smtp_header);
406
407 /* If no header elements could be found, there is an error */
408 if (!uctx->header) {
409 RDEBUG2("Header elements could not be added");
410 return -1;
411 }
412
413 /* Set the DATE: to the time that the request was received */
414 if (inst->set_date) {
415 time_out = FR_SBUFF_OUT(uctx->time_str, sizeof(uctx->time_str));
416 fr_time_strftime_local(&time_out, fr_time(), "DATE: %a, %d %b %Y %T %z, (%Z) \r\n");
417 uctx->header = curl_slist_append(uctx->header, uctx->time_str);
418 }
419
420 RDEBUG2("Finished generating the curl_slist for the header elements");
421 return 0;
422}
423
424/*
425 * Add the Body elements to the email
426 */
427static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
428{
429 fr_mail_ctx_t *uctx = mail_ctx;
431 request_t *request = uctx->request;
432 fr_pair_t *vp;
433
434 fr_dbuff_init(&out, (uint8_t *)ptr, (size * nmemb)); /* Wrap the output buffer so we can track our position easily */
435
437 if (!vp) {
438 RDEBUG2("vp could not be found for the body element");
439 return 0;
440 }
441
442 /*
443 * Copy the vp into the email. If it cannot all be
444 * loaded, return the amount of memory that was loaded
445 * and get called again.
446 */
447 if (fr_dbuff_in_memcpy_partial(&out, &uctx->vp_in, SIZE_MAX) < fr_dbuff_remaining(&uctx->vp_in)) {
448 RDEBUG2("%zu bytes used (partial copy)", fr_dbuff_used(&out));
449 return fr_dbuff_used(&out);
450 }
451
452 /*
453 * Once this value pair is fully copied, prepare for the next element
454 */
456 if (vp) {
457 fr_dbuff_init(&uctx->vp_in, (uint8_t const *)vp->vp_strvalue, vp->vp_length);
458
459 }
460
461 RDEBUG2("%zu bytes used (full copy)", fr_dbuff_used(&out));
462 return fr_dbuff_used(&out);
463}
464
465/*
466 * Initialize all the body elements to be uploaded later
467 */
468static int body_init(fr_mail_ctx_t *uctx, curl_mime *mime)
469{
470 fr_pair_t *vp;
471 request_t *request = uctx->request;
472
473 curl_mimepart *part;
474 curl_mime *mime_body;
475
476 int body_elements = 0;
477
478 /* Initialize a second mime to apply special conditions to the body elements */
479 MEM(mime_body = curl_mime_init(uctx->randle->candle));
480
481 /* initialize the cursor used by the body_source function*/
482 vp = fr_pair_dcursor_by_da_init(&uctx->body_cursor, &uctx->request->request_pairs, attr_smtp_body);
483 fr_dbuff_init(&uctx->vp_in, (uint8_t const *)vp->vp_strvalue, vp->vp_length);
484
485 /* Add a mime part to mime_body for every body element */
486 while (vp) {
487 body_elements++;
488 MEM(part = curl_mime_addpart(mime_body));
489
490 curl_mime_encoder(part, "8bit");
491 curl_mime_data_cb(part, vp->vp_length, body_source, NULL, NULL, uctx);
492
494 }
495 RDEBUG2("initialized %d body element part(s)", body_elements);
496
497 /* Re-initialize the cursor for use when uploading the data to curl */
498 fr_pair_dcursor_by_da_init(&uctx->body_cursor, &uctx->request->request_pairs, attr_smtp_body);
499
500 /*
501 * Add body_mime as a subpart of the mime request with a local content-disposition
502 */
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"); /* Initialize the body_header curl_slist */
507 curl_mime_headers(part, uctx->body_header, 1);
508
509 return body_elements;
510}
511
512/*
513 * Adds every SMTP_Attachments file to the email as a MIME part
514 */
515static int attachments_source(fr_mail_ctx_t *uctx, curl_mime *mime, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
516{
517 request_t *request = uctx->request;
518 int attachments_set = 0;
519 fr_sbuff_uctx_talloc_t sbuff_ctx;
520 fr_sbuff_t path_buffer;
522 fr_value_box_t *vb = NULL;
523 fr_value_box_list_t const *list = call_env->attachments;
524 size_t i, list_count = talloc_array_length(call_env->attachments);
525
526
527 /* Make sure that a template directory is provided */
528 if (!inst->template_dir) return 0;
529
530 /* Initialize the buffer to write the file path */
531 fr_sbuff_init_talloc(uctx, &path_buffer, &sbuff_ctx, talloc_array_length(inst->template_dir) + 128, SIZE_MAX);
532
533 /* Write the initial path to the buffer */
534 fr_sbuff_in_bstrcpy_buffer(&path_buffer, inst->template_dir);
535
536 /* Make sure the template_directory path ends in a "/" */
537 if (inst->template_dir[talloc_array_length(inst->template_dir)-2] != '/'){
538 RDEBUG2("Adding / to end of template_dir");
539 (void) fr_sbuff_in_char(&path_buffer, '/');
540 }
541
542 /* Mark the buffer so we only re-write after the template_dir component */
543 fr_sbuff_marker(&m, &path_buffer);
544
545 /* Add the attachments to the email */
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);
549 }
550 list++;
551 }
552
553 /* Check for any file attachments */
554 talloc_free(path_buffer.buff);
555 return attachments_set;
556}
557
558static void smtp_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
559{
560 fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
561 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
562 CURLMcode ret;
563
564 RDEBUG2("Forcefully cancelling pending SMTP request");
565
566 ret = curl_multi_remove_handle(t->mhandle->mandle, randle->candle); /* Gracefully terminate the request */
567 if (ret != CURLM_OK) {
568 RERROR("Failed removing curl handle from multi-handle: %s (%i)", curl_multi_strerror(ret), ret);
569 /* Not much we can do */
570 }
571 t->mhandle->transfers--;
572 smtp_slab_release(randle);
573}
574
575/** Callback to process response of SMTP server
576 *
577 * It checks if the response was CURLE_OK
578 * If it was, it tries to extract the certificate attributes
579 * If the response was not OK, we REJECT the request
580 * When responding to requests initiated by mod_authenticate this is simply
581 * a check on the username and password.
582 * When responding to requests initiated by mod_mail this indicates
583 * the mail has been queued.
584 */
586{
588 fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
589 fr_curl_tls_t const *tls = &inst->tls;
590 long curl_out;
591 long curl_out_valid;
592
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");
596 } else {
597 RDEBUG2("server certificate result not found");
598 }
599
600 if (randle->result != CURLE_OK) {
601 CURLcode result = randle->result;
602 smtp_slab_release(randle);
603 switch (result) {
604 case CURLE_PEER_FAILED_VERIFICATION:
605 case CURLE_LOGIN_DENIED:
607 default:
609 }
610 }
611
612 if (tls->extract_cert_attrs) fr_curl_response_certinfo(request, randle);
613 smtp_slab_release(randle);
614
616}
617
618/*
619 * Checks that there is a User-Name and User-Password field in the request
620 * As well as all of the required SMTP elements
621 * Sets the: username, password
622 * website URI
623 * timeout information
624 * TLS information
625 * Sender and recipient information
626 * Email header and body
627 * File attachments
628 *
629 * Then it queues the request and yields until a response is given
630 * When it responds, smtp_io_module_resume is called.
631 */
632static unlang_action_t CC_HINT(nonnull) mod_mail(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
633{
635 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
636 rlm_smtp_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_smtp_env_t);
637 fr_curl_io_request_t *randle = NULL;
638 fr_mail_ctx_t *mail_ctx;
639
640 fr_pair_t const *smtp_body;
641
642 /* Elements provided by the request */
643 smtp_body = fr_pair_find_by_da(&request->request_pairs, NULL, attr_smtp_body);
644
645 /* Make sure all of the essential email components are present and possible*/
646 if (!smtp_body) {
647 RDEBUG2("Attribute \"smtp-body\" is required for smtp");
649 }
650
651 if (!call_env->sender_address && !inst->envelope_address) {
652 RDEBUG2("At least one of \"sender_address\" or \"envelope_address\" in the config, or \"SMTP-Sender-Address\" in the request is needed");
653 error:
654 if (randle) smtp_slab_release(randle);
656 }
657
658 /*
659 * If the username is defined and is not static data
660 * a onetime connection is used, otherwise a persistent one
661 * can be used.
662 */
663 randle = (call_env->username_tmpl &&
664 !tmpl_is_data(call_env->username_tmpl)) ? smtp_slab_reserve(t->slab_onetime) :
665 smtp_slab_reserve(t->slab_persist);
666 if (!randle) {
667 RDEBUG2("A handle could not be allocated for the request");
669 }
670
671 /* Initialize the uctx to perform the email */
672 mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
673 *mail_ctx = (fr_mail_ctx_t) {
674 .request = request,
675 .randle = randle,
676 .mime = curl_mime_init(randle->candle),
677 .time = fr_time(), /* time the request was received. Used to set DATE: */
678 .recipients = NULL,
679 .header = NULL
680 };
681
682 FR_CURL_REQUEST_SET_OPTION(CURLOPT_UPLOAD, 1L);
683
684 /* Set the username and password if they have been provided */
685 if (call_env->username.vb_strvalue) {
686 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, call_env->username.vb_strvalue);
687
688 if (!call_env->password.vb_strvalue) goto skip_auth;
689
690 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, call_env->password.vb_strvalue);
691 RDEBUG2("Username and password set");
692 }
693
694skip_auth:
695 /* Set the envelope mail from address */
696 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_FROM,
697 (inst->envelope_address ? inst->envelope_address :
698 fr_value_box_list_head(call_env->sender_address)->vb_strvalue));
699
700 /* Set the recipients */
701 if (recipients_source(mail_ctx, call_env) <= 0) {
702 REDEBUG("At least one recipient is required to send an email");
703 goto error;
704 }
705 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_RCPT, mail_ctx->recipients);
706
707 /* Set the header elements */
708 if (header_source(mail_ctx, inst, call_env) != 0) {
709 REDEBUG("The header slist could not be generated");
710 goto error;
711 }
712
713 /*
714 * CURLOPT_HTTPHEADER is the option that they use for the header in the curl example
715 *
716 * https://curl.haxx.se/libcurl/c/smtp-mime.html
717 */
718 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTPHEADER, mail_ctx->header);
719
720 /* Initialize the body elements to be uploaded */
721 if (body_init(mail_ctx, mail_ctx->mime) == 0) {
722 REDEBUG("The body could not be generated");
723 goto error;
724 }
725
726 /* Initialize the attachments if there are any*/
727 if (attachments_source(mail_ctx, mail_ctx->mime, inst, call_env) == 0){
728 RDEBUG2("No files were attached to the email");
729 }
730
731 /* Add the mime endoced elements to the curl request */
732 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MIMEPOST, mail_ctx->mime);
733
735
737}
738
739/*
740 * Sets the: username, password
741 * website URI
742 * timeout information
743 * and TLS information
744 *
745 * Then it queues the request and yields until a response is given
746 * When it responds, smtp_io_module_resume is called.
747 */
748static unlang_action_t CC_HINT(nonnull(1,2)) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
749{
750 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
751 rlm_smtp_auth_env_t *env_data = talloc_get_type_abort(mctx->env_data, rlm_smtp_auth_env_t);
752 fr_curl_io_request_t *randle;
753
754 randle = smtp_slab_reserve(t->slab_onetime);
755 if (!randle) RETURN_MODULE_FAIL;
756
757 if (env_data->username.type != FR_TYPE_STRING || (env_data->username.vb_length == 0)) {
758 RWARN("\"%s\" is required for authentication", env_data->username_tmpl->name);
759 error:
760 smtp_slab_release(randle);
762 }
763
764 if (env_data->password.type != FR_TYPE_STRING || (env_data->password.vb_length == 0)) {
765 RWARN("\"%s\" is required for authentication", env_data->password_tmpl->name);
766 goto error;
767 }
768
769 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, env_data->username.vb_strvalue);
770 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, env_data->password.vb_strvalue);
771
773
775}
776
777static int mod_instantiate(module_inst_ctx_t const *mctx)
778{
779 rlm_smtp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_smtp_t);
780
781 inst->conn_config.reuse.num_children = 1;
782 inst->conn_config.reuse.child_pool_size = sizeof(fr_mail_ctx_t);
783
784 return 0;
785}
786
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)
792
794{
796
797 if (randle->candle) curl_easy_cleanup(randle->candle);
798
799 return 0;
800}
801
803{
805
806 if (randle->candle) curl_easy_reset(randle->candle);
807
808 return 0;
809}
810
812{
813 fr_mail_ctx_t *mail_ctx = NULL;
814
815 MEM(mail_ctx = talloc_zero(randle, fr_mail_ctx_t));
816 randle->uctx = mail_ctx;
817
818 smtp_slab_element_set_destructor(randle, smtp_onetime_request_cleanup, NULL);
819
820 return 0;
821}
822
824{
825 if (mail_ctx->randle && mail_ctx->randle->candle) curl_easy_cleanup(mail_ctx->randle->candle);
826
827 return 0;
828}
829
831{
832 fr_mail_ctx_t *mail_ctx = NULL;
833
834 MEM(mail_ctx = talloc_zero(randle, fr_mail_ctx_t));
835 mail_ctx->randle = randle;
836 randle->uctx = mail_ctx;
837 randle->candle = curl_easy_init();
838 if (unlikely(!randle->candle)) {
839 fr_strerror_printf("Unable to initialise CURL handle");
840 return -1;
841 }
842 talloc_set_destructor(mail_ctx, smtp_mail_ctx_free);
843
844 smtp_slab_element_set_destructor(randle, smtp_persist_request_cleanup, NULL);
845
846 return 0;
847}
848
850{
851#if CURL_AT_LEAST_VERSION(7,45,0)
852 FR_CURL_SET_OPTION(CURLOPT_DEFAULT_PROTOCOL, "smtp");
853#endif
854 FR_CURL_SET_OPTION(CURLOPT_URL, inst->uri);
855#if CURL_AT_LEAST_VERSION(7,85,0)
856 FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS_STR, "smtp,smtps");
857#else
858 FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS, CURLPROTO_SMTP | CURLPROTO_SMTPS);
859#endif
860 FR_CURL_SET_OPTION(CURLOPT_CONNECTTIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
861 FR_CURL_SET_OPTION(CURLOPT_TIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
862
863 if (DEBUG_ENABLED3) FR_CURL_SET_OPTION(CURLOPT_VERBOSE, 1L);
864
865 if (fr_curl_easy_tls_init(randle, &inst->tls) != 0) goto error;
866
867 return 0;
868error:
869 return -1;
870}
871
872static int smtp_onetime_conn_init(fr_curl_io_request_t *randle, void *uctx)
873{
874 rlm_smtp_t const *inst = talloc_get_type_abort(uctx, rlm_smtp_t);
875 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
876
877 randle->candle = curl_easy_init();
878 if (unlikely(!randle->candle)) {
879 fr_strerror_printf("Unable to initialise CURL handle");
880 return -1;
881 }
882
883 memset(mail_ctx, 0, sizeof(fr_mail_ctx_t));
884
885 return smtp_conn_common_init(randle, inst);
886}
887
888
889static int smtp_persist_conn_init(fr_curl_io_request_t *randle, void *uctx)
890{
891 rlm_smtp_t const *inst = talloc_get_type_abort(uctx, rlm_smtp_t);
892 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
893
894 memset(mail_ctx, 0, sizeof(fr_mail_ctx_t));
895
896 return smtp_conn_common_init(randle, inst);
897}
898
899/*
900 * Initialize a new thread with a curl instance
901 */
903{
904 rlm_smtp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_smtp_t);
905 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
906 fr_curl_handle_t *mhandle;
907
908 if (!(t->slab_onetime = smtp_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
910 inst, false, false))) {
911 ERROR("Connection handle pool instantiation failed");
912 return -1;
913 }
914 if (!(t->slab_persist = smtp_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
916 inst, false, true))) {
917 ERROR("Connection handle pool instantiation failed");
918 return -1;
919 }
920
921 mhandle = fr_curl_io_init(t, mctx->el, false);
922 if (!mhandle) return -1;
923
924 t->mhandle = mhandle;
925 return 0;
926}
927
928/*
929 * Close the thread and free the memory
930 */
932{
933 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
934
938 return 0;
939}
940
941/** Parse the header section into tmpls for producing email headers
942 *
943 */
944static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
945 CONF_ITEM *ci,
946 UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
947{
948 CONF_SECTION const *cs = cf_item_to_section(ci);
949 CONF_ITEM const *item = NULL;
950 CONF_PAIR const *cp;
951 call_env_parsed_t *parsed_env;
952 tmpl_t *parsed_tmpl;
953 ssize_t slen;
954
955 while ((item = cf_item_next(cs, item))) {
956 char *to_parse;
957
958 if (!cf_item_is_pair(item)) {
959 cf_log_err(item, "Entry is not in \"header = value\" format");
960 return -1;
961 }
962 cp = cf_item_to_pair(item);
963
964 MEM(parsed_env = call_env_parsed_add(ctx, out,
966
967 /*
968 * Turn the conf pair `attr = value` to email header format
969 */
970 to_parse = talloc_asprintf(NULL, "%s: %s", cf_pair_attr(cp), cf_pair_value(cp));
971
972 slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
973 &FR_SBUFF_IN(to_parse, talloc_array_length(to_parse) - 1),
975 t_rules);
976 talloc_free(to_parse);
977
978 if (slen <= 0) {
979 cf_canonicalize_error(cp, slen, "Failed parsing header", cf_pair_value(cp));
980 error:
981 call_env_parsed_free(out, parsed_env);
982 return -1;
983 }
984
985 if (tmpl_needs_resolving(parsed_tmpl) &&
986 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = t_rules->attr.dict_def }) < 0)) {
987 cf_log_perr(cp, "Failed resolving header");
988 goto error;
989 }
990
991 call_env_parsed_set_tmpl(parsed_env, parsed_tmpl);
992 }
993
994 return 0;
995}
996
999 .env = (call_env_parser_t[]) {
1001 .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
1003 { FR_CALL_ENV_OFFSET("sender_address", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, rlm_smtp_env_t, sender_address) },
1004 { FR_CALL_ENV_OFFSET("recipients", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE, rlm_smtp_env_t, recipient_addrs),
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 },
1015
1017 }
1018};
1019
1022 .env = (call_env_parser_t[]) {
1023 { FR_CALL_ENV_PARSE_OFFSET("username_attribute", FR_TYPE_STRING,
1025 rlm_smtp_auth_env_t, username, username_tmpl), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD },
1026 { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING,
1028 rlm_smtp_auth_env_t, password, password_tmpl), .pair.dflt = "&User-Password", .pair.dflt_quote = T_BARE_WORD },
1030 }
1031};
1032
1033/*
1034 * The module name should be the only globally exported symbol.
1035 * That is, everything else should be 'static'.
1036 *
1037 * If the module needs to temporarily modify it's instantiation
1038 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1039 * The server will then take care of ensuring that the module
1040 * is single-threaded.
1041 */
1042extern module_rlm_t rlm_smtp;
1044 .common = {
1045 .magic = MODULE_MAGIC_INIT,
1046 .name = "smtp",
1047 .inst_size = sizeof(rlm_smtp_t),
1048 .thread_inst_size = sizeof(rlm_smtp_thread_t),
1049 .config = module_config,
1050 .instantiate = mod_instantiate,
1051 .thread_instantiate = mod_thread_instantiate,
1052 .thread_detach = mod_thread_detach,
1053 },
1054 .method_group = {
1055 .bindings = (module_method_binding_t[]){
1056 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &auth_env },
1057 { .section = SECTION_NAME("mail", CF_IDENT_ANY), .method = mod_mail, .method_env = &method_env },
1058
1060 }
1061 }
1062};
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
#define RCSID(id)
Definition build.h:483
#define unlikely(_x)
Definition build.h:381
#define UNUSED
Definition build.h:315
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.
Definition call_env.c:718
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.
Definition call_env.c:631
void call_env_parsed_set_tmpl(call_env_parsed_t *parsed, tmpl_t const *tmpl)
Assign a tmpl to a call_env_parsed_t.
Definition call_env.c:660
#define CALL_ENV_TERMINATOR
Definition call_env.h:231
#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...
Definition call_env.h:360
#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...
Definition call_env.h:235
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:242
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition call_env.h:76
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
Definition call_env.h:87
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl must contain an attribute reference.
Definition call_env.h:86
@ CALL_ENV_FLAG_SECRET
The value is a secret, and should not be logged.
Definition call_env.h:91
@ CALL_ENV_FLAG_NONE
Definition call_env.h:74
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition call_env.h:80
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition call_env.h:407
#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.
Definition call_env.h:335
Per method call config.
Definition call_env.h:175
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:642
#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
Definition cf_parse.h:268
#define FR_CONF_OFFSET_SUBSECTION(_name, _flags, _struct, _field, _subcs)
conf_parser_t which populates a sub-struct using a CONF_SECTION
Definition cf_parse.h:297
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:579
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:70
A section grouping multiple CONF_PAIR.
Definition cf_priv.h:101
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition cf_util.c:632
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:684
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1638
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:664
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1594
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1578
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:289
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:367
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:296
#define cf_item_next(_ci, _curr)
Definition cf_util.h:92
#define CF_IDENT_ANY
Definition cf_util.h:78
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, bool multiplex)
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
Definition base.h:67
CURLcode result
Result of executing the request.
Definition base.h:103
#define FR_CURL_SET_OPTION(_x, _y)
Definition base.h:45
uint64_t transfers
How many transfers are current in progress.
Definition base.h:94
bool extract_cert_attrs
Definition base.h:119
CURLM * mandle
The multi handle.
Definition base.h:95
void * uctx
Private data for the module using the API.
Definition base.h:105
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.
Definition io.c:482
CURL * candle
Request specific handle.
Definition base.h:102
Uctx data for timer and I/O functions.
Definition base.h:91
Structure representing an individual request being passed to curl for processing.
Definition base.h:101
#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...
Definition dbuff.h:767
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:354
#define fr_dbuff_remaining(_dbuff_or_marker)
Return the number of bytes remaining between the dbuff or marker and the end of the buffer.
Definition dbuff.h:743
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
Definition dbuff.h:1438
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition dcursor.h:337
#define MEM(x)
Definition debug.h:36
#define ERROR(fmt,...)
Definition dhcpclient.c:41
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition dl_module.h:63
#define GLOBAL_LIB_TERMINATOR
Definition global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition global_lib.h:38
int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle)
Definition base.c:170
int fr_curl_easy_tls_init(fr_curl_io_request_t *randle, fr_curl_tls_t const *conf)
Definition base.c:139
global_lib_autoinst_t fr_curl_autoinst
Definition base.c:387
conf_parser_t fr_curl_conn_config[]
Definition base.c:97
conf_parser_t fr_curl_tls_config[]
Definition base.c:68
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RWARN(fmt,...)
Definition log.h:297
#define RERROR(fmt,...)
Definition log.h:298
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:259
#define REDEBUG2(fmt,...)
Definition log.h:372
talloc_free(reap)
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:122
@ FR_TYPE_STRING
String of printable characters.
long int ssize_t
unsigned char uint8_t
void * env_data
Per call environment data.
Definition module_ctx.h:44
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:42
void * thread
Thread specific instance data.
Definition module_ctx.h:43
void * rctx
Resume ctx that a module previously set.
Definition module_ctx.h:45
fr_event_list_t * el
Event list to register any IO handlers and timers against.
Definition module_ctx.h:68
void * thread
Thread instance data.
Definition module_ctx.h:67
module_instance_t const * mi
Instance of the module being instantiated.
Definition module_ctx.h:64
module_instance_t * mi
Instance of the module being instantiated.
Definition module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition module_ctx.h:50
Temporary structure to hold arguments for thread_instantiation calls.
Definition module_ctx.h:63
module_t common
Common fields presented by all modules.
Definition module_rlm.h:39
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.
Definition pair.c:693
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RDEBUG2(fmt,...)
Definition radclient.h:54
#define RETURN_MODULE_REJECT
Definition rcode.h:55
#define RETURN_MODULE_INVALID
Definition rcode.h:59
#define RETURN_MODULE_OK
Definition rcode.h:57
#define RETURN_MODULE_FAIL
Definition rcode.h:56
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
username
static const call_env_method_t auth_env
Definition rlm_smtp.c:1020
struct curl_slist * recipients
Definition rlm_smtp.c:143
static int header_source(fr_mail_ctx_t *uctx, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
Definition rlm_smtp.c:376
fr_time_t time
Definition rlm_smtp.c:147
fr_value_box_list_t * bcc_addrs
The address(es) used for the Bcc: header.
Definition rlm_smtp.c:72
global_lib_autoinst_t const *const rlm_smtp_lib[]
Definition rlm_smtp.c:57
static unlang_action_t mod_mail(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_smtp.c:632
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.
Definition rlm_smtp.c:301
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.
Definition rlm_smtp.c:178
static int smtp_persist_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:830
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.
Definition rlm_smtp.c:207
fr_value_box_t username
Value to use for user name.
Definition rlm_smtp.c:80
static int attachments_source(fr_mail_ctx_t *uctx, curl_mime *mime, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
Definition rlm_smtp.c:515
static int smtp_persist_conn_init(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_smtp.c:889
fr_dcursor_t cursor
Definition rlm_smtp.c:139
smtp_slab_list_t * slab_persist
Slab list for persistent connections.
Definition rlm_smtp.c:129
static int smtp_conn_common_init(fr_curl_io_request_t *randle, rlm_smtp_t const *inst)
Definition rlm_smtp.c:849
fr_curl_io_request_t * randle
Definition rlm_smtp.c:138
static fr_dict_attr_t const * attr_smtp_body
Definition rlm_smtp.c:47
static const call_env_method_t method_env
Definition rlm_smtp.c:997
static int smtp_onetime_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:793
static fr_dict_t const * dict_freeradius
Definition rlm_smtp.c:38
fr_dict_attr_autoload_t rlm_smtp_dict_attr[]
Definition rlm_smtp.c:50
fr_time_delta_t timeout
Timeout for connection and server response.
Definition rlm_smtp.c:91
static int recipients_source(fr_mail_ctx_t *uctx, rlm_smtp_env_t const *call_env)
Definition rlm_smtp.c:333
fr_dict_autoload_t rlm_smtp_dict[]
Definition rlm_smtp.c:41
fr_curl_conn_config_t conn_config
Reusable CURL handle config.
Definition rlm_smtp.c:96
tmpl_t * username_tmpl
tmpl expanded to populate username
Definition rlm_smtp.c:81
static void smtp_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_smtp.c:558
char const * uri
URI of smtp server.
Definition rlm_smtp.c:87
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.
Definition rlm_smtp.c:944
fr_value_box_t username
User to authenticate as when sending emails.
Definition rlm_smtp.c:65
fr_dbuff_t vp_in
Definition rlm_smtp.c:141
fr_value_box_t password
Value to use for password.
Definition rlm_smtp.c:82
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_smtp.c:748
struct curl_slist * header
Definition rlm_smtp.c:144
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.
Definition rlm_smtp.c:585
tmpl_t * password_tmpl
tmpl expanded to populate password
Definition rlm_smtp.c:83
static fr_dict_attr_t const * attr_smtp_header
Definition rlm_smtp.c:46
fr_dcursor_t body_cursor
Definition rlm_smtp.c:140
static int smtp_persist_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:802
curl_mime * mime
Definition rlm_smtp.c:149
fr_value_box_list_t * cc_addrs
The address(es) used for the Cc: header.
Definition rlm_smtp.c:71
fr_value_box_list_t * to_addrs
The address(es) used for the To: header.
Definition rlm_smtp.c:70
#define SMTP_COMMON_CLEANUP
Definition rlm_smtp.c:787
request_t * request
Definition rlm_smtp.c:137
static int body_init(fr_mail_ctx_t *uctx, curl_mime *mime)
Definition rlm_smtp.c:468
fr_value_box_list_t headers
Entries to add to email header.
Definition rlm_smtp.c:74
module_rlm_t rlm_smtp
Definition rlm_smtp.c:1043
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Definition rlm_smtp.c:902
struct curl_slist * body_header
Definition rlm_smtp.c:145
bool set_date
Definition rlm_smtp.c:94
static int smtp_onetime_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:811
fr_value_box_t password
Password for authenticated mails.
Definition rlm_smtp.c:67
char const * envelope_address
The address used to send the message.
Definition rlm_smtp.c:89
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)
Definition rlm_smtp.c:258
fr_value_box_list_t * recipient_addrs
The address(es) used as recipients. Overrides elements in to, cc and bcc.
Definition rlm_smtp.c:69
static int smtp_onetime_conn_init(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_smtp.c:872
char time_str[60]
Definition rlm_smtp.c:148
tmpl_t * username_tmpl
The tmpl used to produce the above.
Definition rlm_smtp.c:66
static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
Definition rlm_smtp.c:427
char const * template_dir
The directory that contains all email attachments.
Definition rlm_smtp.c:88
fr_curl_tls_t tls
Used for handled all tls specific curl components.
Definition rlm_smtp.c:92
static int da_to_slist(fr_mail_ctx_t *uctx, struct curl_slist **out, const fr_dict_attr_t *dict_attr)
Definition rlm_smtp.c:155
static const conf_parser_t module_config[]
Definition rlm_smtp.c:102
static int smtp_mail_ctx_free(fr_mail_ctx_t *mail_ctx)
Definition rlm_smtp.c:823
fr_value_box_list_t * attachments
List of files to attach.
Definition rlm_smtp.c:73
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Definition rlm_smtp.c:931
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_smtp.c:777
fr_curl_handle_t * mhandle
Thread specific multi handle.
Definition rlm_smtp.c:127
smtp_slab_list_t * slab_onetime
Slab list for onetime use connections.
Definition rlm_smtp.c:130
fr_value_box_list_t * sender_address
The address(es) used to generate the From: header.
Definition rlm_smtp.c:68
Call environment for SMTP authentication.
Definition rlm_smtp.c:79
Call environment for sending emails.
Definition rlm_smtp.c:64
ssize_t fr_sbuff_in_strcpy(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1454
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.
Definition sbuff.c:1480
ssize_t fr_sbuff_in_bstrcpy_buffer(fr_sbuff_t *sbuff, char const *str)
Copy bytes into the sbuff up to the first \0.
Definition sbuff.c:1502
#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.
Definition sbuff.h:139
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:40
size_t inst_size
Size of the module's instance data.
Definition module.h:203
void * data
Module's instance data.
Definition module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:151
Named methods exported by a module.
Definition module.h:173
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)
Definition tmpl.h:211
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:344
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:228
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:373
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:341
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
#define FR_SLAB_FUNCS(_name, _type)
Define type specific wrapper functions for slabs and slab elements.
Definition slab.h:120
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
Definition slab.h:72
return count
Definition module.c:163
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.
Definition module.c:419
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
#define fr_time()
Allow us to arbitrarily manipulate time.
Definition state_test.c:8
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:285
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_get_type_abort_const
Definition talloc.h:282
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.
Definition time.c:540
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition time.h:637
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
@ T_BARE_WORD
Definition token.h:120
@ T_DOUBLE_QUOTED_STRING
Definition token.h:121
#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.
Definition pair.h:628
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition strerror.h:64
fr_sbuff_parse_rules_t const * value_parse_rules_quoted[T_TOKEN_LAST]
Parse rules for quoted strings.
Definition value.c:606
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.
Definition value.c:3572
int nonnull(2, 5))
static size_t char ** out
Definition value.h:997