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: 65e312f902c4ce66bfc2755c19c5de48b4aca8b3 $
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: 65e312f902c4ce66bfc2755c19c5de48b4aca8b3 $")
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/module_rlm.h>
31#include <freeradius-devel/server/tmpl_dcursor.h>
32#include <freeradius-devel/util/token.h>
33
34#include <freeradius-devel/unlang/xlat_func.h>
35
37
40 { .out = &dict_freeradius, .proto = "freeradius"},
42};
43
46
49 { .out = &attr_smtp_header, .name = "SMTP-Mail-Header", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
50 { .out = &attr_smtp_body, .name = "SMTP-Mail-Body", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
52};
53
54extern global_lib_autoinst_t const * const rlm_smtp_lib[];
59
60/** Call environment for sending emails.
61*/
62typedef struct {
63 fr_value_box_t username; //!< User to authenticate as when sending emails.
64 tmpl_t *username_tmpl; //!< The tmpl used to produce the above.
65 fr_value_box_t password; //!< Password for authenticated mails.
66 fr_value_box_list_t *sender_address; //!< The address(es) used to generate the From: header
67 fr_value_box_list_t *recipient_addrs; //!< The address(es) used as recipients. Overrides elements in to, cc and bcc
68 fr_value_box_list_t *to_addrs; //!< The address(es) used for the To: header.
69 fr_value_box_list_t *cc_addrs; //!< The address(es) used for the Cc: header.
70 fr_value_box_list_t *bcc_addrs; //!< The address(es) used for the Bcc: header.
71 fr_value_box_list_t *attachments; //!< List of files to attach.
72 fr_value_box_list_t headers; //!< Entries to add to email header.
74
75/** Call environment for SMTP authentication
76 */
77typedef struct {
78 fr_value_box_t username; //!< Value to use for user name
79 tmpl_t *username_tmpl; //!< tmpl expanded to populate username
80 fr_value_box_t password; //!< Value to use for password
81 tmpl_t *password_tmpl; //!< tmpl expanded to populate password
83
84/** Call environment for sending simple emails using an xlat.
85*/
86typedef struct {
87 fr_value_box_t username; //!< User to authenticate as when sending emails.
88 tmpl_t *username_tmpl; //!< The tmpl used to produce the above.
89 fr_value_box_t password; //!< Password for authenticated mails.
90 fr_value_box_list_t *sender_address; //!< The address(es) used to generate the From: header
91 fr_value_box_list_t headers; //!< Entries to add to email header.
93
94static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
95 CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
96
109
110typedef struct {
111 char const *uri; //!< URI of smtp server
112 char const *template_dir; //!< The directory that contains all email attachments
113 char const *envelope_address; //!< The address used to send the message
114
115 fr_time_delta_t timeout; //!< Timeout for connection and server response
116 fr_curl_tls_t tls; //!< Used for handled all tls specific curl components
117
119
120 fr_curl_conn_config_t conn_config; //!< Reusable CURL handle config
121} rlm_smtp_t;
122
123/*
124 * A mapping of configuration file names to internal variables.
125 */
126static const conf_parser_t module_config[] = {
127 { FR_CONF_OFFSET("uri", rlm_smtp_t, uri) },
128 { FR_CONF_OFFSET("template_directory", rlm_smtp_t, template_dir) },
129 { FR_CONF_OFFSET("envelope_address", rlm_smtp_t, envelope_address) },
130 { FR_CONF_OFFSET("timeout", rlm_smtp_t, timeout) },
131 { FR_CONF_OFFSET("set_date", rlm_smtp_t, set_date), .dflt = "yes" },
132 { FR_CONF_OFFSET_SUBSECTION("tls", 0, rlm_smtp_t, tls, fr_curl_tls_config) },//!<loading the tls values
133 { FR_CONF_OFFSET_SUBSECTION("connection", 0, rlm_smtp_t, conn_config, fr_curl_conn_config) },
135};
136
137/*
138 * Two types of SMTP connections are used:
139 * - persistent - where the connection can be left established as the same
140 * authentication is used for all mails sent.
141 * - onetime - where the connection is torn down after each use, since
142 * different authentication is needed each time.
143 *
144 * Memory for the handles for each is stored in slabs.
145 */
146
149
150typedef struct {
151 fr_curl_handle_t *mhandle; //!< Thread specific multi handle. Serves as the dispatch and
152 ///< coralling structure for smtp requests
153 smtp_slab_list_t *slab_persist; //!< Slab list for persistent connections.
154 smtp_slab_list_t *slab_onetime; //!< Slab list for onetime use connections.
156
157/*
158 * Holds the context for parsing the email elements
159 */
160typedef struct {
166
167 struct curl_slist *recipients;
168 struct curl_slist *header;
169 struct curl_slist *body_header;
170
172 char time_str[60];
173 curl_mime *mime;
175
176/*
177 * Adds every element associated with a dict_attr to a curl_slist
178 */
179static int da_to_slist(fr_mail_ctx_t *uctx, struct curl_slist **out, const fr_dict_attr_t *dict_attr)
180{
181 request_t *request = ((fr_mail_ctx_t *)uctx)->request;
182 fr_pair_t *vp;
183 int elems_added = 0;
184
185 /* Iterate over the VP and add the string value to the curl_slist */
186 vp = fr_pair_dcursor_by_da_init(&uctx->cursor, &uctx->request->request_pairs, dict_attr);
187
188 while (vp) {
189 *out = curl_slist_append(*out, vp->vp_strvalue);
190 vp = fr_dcursor_next(&uctx->cursor);
191 elems_added++;
192 }
193
194 if (!elems_added) RDEBUG3("There were no %s elements found", dict_attr->name);
195
196 return elems_added;
197}
198
199/** Transform an array of value box lists to entries in a CURL slist
200 *
201 */
202static int value_box_list_to_slist(struct curl_slist **out, fr_value_box_list_t const *lists)
203{
204 fr_value_box_list_t const *list = lists;
205 fr_value_box_t *vb = NULL;
206 int count = 0;
207 size_t i, list_count = talloc_array_length(lists);
208
209 for (i = 0; i < list_count; i++) {
210 while ((vb = fr_value_box_list_next(list, vb))) {
211 *out = curl_slist_append(*out, vb->vb_strvalue);
212 count ++;
213 }
214 list++;
215 }
216
217 return count;
218}
219
220/** Converts an array of value box lists to a curl_slist with a prefix
221 *
222 * @param uctx Mail context.
223 * @param out CURL slist to write to.
224 * @param lists Array of value box lists to copy.
225 * @param prefix to prepend to the output slist
226 * @return
227 * - 1 on success
228 * - 0 if no slist entry was created
229 * - -1 on failure
230 */
231static int value_box_list_to_header(fr_mail_ctx_t *uctx, struct curl_slist **out, fr_value_box_list_t const *lists,
232 const char *prefix)
233{
234 request_t *request = uctx->request;
235 fr_sbuff_t sbuff;
236 fr_sbuff_uctx_talloc_t sbuff_ctx;
237 bool added = false;
238 fr_value_box_t *vb = NULL;
239 fr_value_box_list_t const *list = lists;
240 size_t i, list_count = talloc_array_length(lists);
241
242 fr_sbuff_init_talloc(uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
243 (void) fr_sbuff_in_strcpy(&sbuff, prefix);
244
245 for (i = 0; i < list_count; i++) {
246 while ((vb = fr_value_box_list_next(list, vb))) {
247 /* If there are already values added, add the comma separator */
248 if (added) {
249 if (unlikely(fr_sbuff_in_strcpy(&sbuff, ", ") < 0)) {
250 error:
251 talloc_free(fr_sbuff_buff(&sbuff));
252 return -1;
253 }
254 }
255
256 if ((vb->type != FR_TYPE_STRING) && (fr_value_box_cast_in_place(vb, vb, FR_TYPE_STRING, NULL) < 0)) {
257 RERROR("Failed casting %pV to string", vb);
258 goto error;
259 }
260
261 if (unlikely(fr_sbuff_in_strcpy(&sbuff, vb->vb_strvalue) < 0)) goto error;
262 added = true;
263 }
264 list++;
265 }
266
267 if (!added) {
268 RDEBUG2("No values to add to \"%s\" header", prefix);
269 talloc_free(fr_sbuff_buff(&sbuff));
270 return 0;
271 }
272
273 /* Add the generated buffer the the curl_slist */
274 *out = curl_slist_append(*out, fr_sbuff_buff(&sbuff));
275 talloc_free(fr_sbuff_buff(&sbuff));
276 return 1;
277}
278
279/*
280 * Takes a string value and adds it as a file path to upload as an attachment
281 */
282static int str_to_attachments(fr_mail_ctx_t *uctx, curl_mime *mime, fr_value_box_t *vb,
283 fr_sbuff_t *path_buffer, fr_sbuff_marker_t *m)
284{
285 request_t *request = uctx->request;
286 curl_mimepart *part;
287
288 /* Move to the end of the template directory filepath */
289 fr_sbuff_set(path_buffer, m);
290
291 /* Check to see if the file attachment is valid, skip it if not */
292 RDEBUG2("Trying to set attachment: %pV", vb);
293
294 if (vb->type != FR_TYPE_STRING) {
295 return -1;
296 }
297
299 if (rad_filename_box_make_safe(vb, NULL) < 0) {
300 RPEDEBUG2("Failed escaping path");
301 return -1;
302 }
303 }
304
305 if (fr_sbuff_in_bstrncpy(path_buffer, vb->vb_strvalue, vb->vb_length) < 0) {
306 RPEDEBUG2("Path is too long");
307 return -1;
308 }
309
310 /* Add the file attachment as a mime encoded part */
311 part = curl_mime_addpart(mime);
312 curl_mime_encoder(part, "base64");
313 if (curl_mime_filedata(part, path_buffer->buff) != CURLE_OK) {
314 REDEBUG2("Cannot add file attachment");
315 return -1;
316 }
317
318 return 0;
319}
320
321/** Generate the `From:` header
322 *
323 * Defaults to using `sender_address` values, falls back to `envelope_address`
324 */
325static int generate_from_header(fr_mail_ctx_t *uctx, struct curl_slist **out, rlm_smtp_t const *inst,
326 rlm_smtp_env_t const *call_env)
327{
328 fr_sbuff_t sbuff;
329 fr_sbuff_uctx_talloc_t sbuff_ctx;
330
331 if (call_env->sender_address) return value_box_list_to_header(uctx, &uctx->header, call_env->sender_address, "From: ");
332
333 /* Initialize the buffer for the recipients. Used for FROM */
334 fr_sbuff_init_talloc(uctx, &sbuff, &sbuff_ctx, 256, SIZE_MAX);
335
336 /* Add the preposition for the header element */
337 (void) fr_sbuff_in_strcpy(&sbuff, "From: ");
338
339 /* Copy the envelope address as the From: source */
340 if (unlikely(fr_sbuff_in_bstrncpy(&sbuff, inst->envelope_address,
341 strlen(inst->envelope_address)) < 0)) {
342 talloc_free(fr_sbuff_buff(&sbuff));
343 return -1;
344 }
345
346 *out = curl_slist_append(*out, sbuff.buff);
347
348 /* Free the buffer used to generate the FROM header */
349 talloc_free(fr_sbuff_buff(&sbuff));
350
351 return 0;
352}
353
354/*
355 * Generates a curl_slist of recipients
356 */
357static int recipients_source(fr_mail_ctx_t *uctx, rlm_smtp_env_t const *call_env)
358{
359 request_t *request = uctx->request;
360 int recipients_set = 0;
361
362 /*
363 * Try to load the recipients into the envelope recipients if they are set
364 */
365 if(call_env->recipient_addrs) {
366 recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->recipient_addrs);
367 }
368
369 /*
370 * If any recipients were found, ignore to cc and bcc, return the amount added.
371 **/
372 if (recipients_set) {
373 RDEBUG2("Recipients were generated from \"SMTP-Recipients\" and/or recipients in the config");
374 return recipients_set;
375 }
376 RDEBUG2("No addresses were found in \"SMTP-Recipients\"");
377
378 /*
379 * Try to load the to: addresses into the envelope recipients if they are set
380 */
381 if (call_env->to_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->to_addrs);
382
383 /*
384 * Try to load the cc: addresses into the envelope recipients if they are set
385 */
386 if (call_env->cc_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->cc_addrs);
387
388 /*
389 * Try to load the bcc: addresses into the envelope recipients if they are set
390 */
391 if (call_env->bcc_addrs) recipients_set += value_box_list_to_slist(&uctx->recipients, call_env->bcc_addrs);
392
393 RDEBUG2("%d recipients set", recipients_set);
394 return recipients_set;
395}
396
397/*
398 * Generates a curl_slist of header elements header elements
399 */
400static int header_source(fr_mail_ctx_t *uctx, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
401{
402 fr_sbuff_t time_out;
403 request_t *request = uctx->request;
404 fr_value_box_t *vb = NULL;
405
406 /*
407 * Load in all of the header elements supplied in the config
408 */
409 if (fr_value_box_list_initialised(&call_env->headers)) {
410 while ((vb = fr_value_box_list_next(&call_env->headers, vb))) {
411 RDEBUG2("Adding header \"%pV\"", vb);
412 uctx->header = curl_slist_append(uctx->header, vb->vb_strvalue);
413 }
414 }
415
416 /* Add the From: line */
417 if (unlikely(generate_from_header(uctx, &uctx->header, inst, call_env) < 0)) {
418 RDEBUG2("From: header could not be added");
419 return -1;
420 }
421
422 /* Add the TO: line if there is one provided in the request by SMTP-TO */
423 value_box_list_to_header(uctx, &uctx->header, call_env->to_addrs, "To: ");
424
425 /* Add the CC: line if there is one provided in the request by SMTP-CC */
426 value_box_list_to_header(uctx, &uctx->header, call_env->cc_addrs, "Cc: ");
427
428 /* Add all the generic header elements in the request */
429 da_to_slist(uctx, &uctx->header, attr_smtp_header);
430
431 /* If no header elements could be found, there is an error */
432 if (!uctx->header) {
433 RDEBUG2("Header elements could not be added");
434 return -1;
435 }
436
437 /* Set the DATE: to the time that the request was received */
438 if (inst->set_date) {
439 time_out = FR_SBUFF_OUT(uctx->time_str, sizeof(uctx->time_str));
440 fr_time_strftime_local(&time_out, fr_time(), "DATE: %a, %d %b %Y %T %z, (%Z) \r\n");
441 uctx->header = curl_slist_append(uctx->header, uctx->time_str);
442 }
443
444 RDEBUG2("Finished generating the curl_slist for the header elements");
445 return 0;
446}
447
448/*
449 * Add the Body elements to the email
450 */
451static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
452{
453 fr_mail_ctx_t *uctx = mail_ctx;
455 request_t *request = uctx->request;
456 fr_pair_t *vp;
457
458 fr_dbuff_init(&out, (uint8_t *)ptr, (size * nmemb)); /* Wrap the output buffer so we can track our position easily */
459
461 if (!vp) {
462 RWARN("vp could not be found for the body element");
463 return 0;
464 }
465
466 /*
467 * Copy the vp into the email. If it cannot all be
468 * loaded, return the amount of memory that was loaded
469 * and get called again.
470 */
471 if (fr_dbuff_in_memcpy_partial(&out, &uctx->vp_in, SIZE_MAX) < fr_dbuff_remaining(&uctx->vp_in)) {
472 RDEBUG3("%zu bytes used (partial copy)", fr_dbuff_used(&out));
473 return fr_dbuff_used(&out);
474 }
475
476 /*
477 * Once this value pair is fully copied, prepare for the next element
478 */
480 if (vp) {
481 fr_dbuff_init(&uctx->vp_in, (uint8_t const *)vp->vp_strvalue, vp->vp_length);
482
483 }
484
485 RDEBUG3("%zu bytes used (full copy)", fr_dbuff_used(&out));
486 return fr_dbuff_used(&out);
487}
488
489/*
490 * Initialize all the body elements to be uploaded later
491 */
492static int body_init(fr_mail_ctx_t *uctx, curl_mime *mime)
493{
494 fr_pair_t *vp;
495 request_t *request = uctx->request;
496
497 curl_mimepart *part;
498 curl_mime *mime_body;
499
500 int body_elements = 0;
501
502 /* Initialize a second mime to apply special conditions to the body elements */
503 MEM(mime_body = curl_mime_init(uctx->randle->candle));
504
505 /* initialize the cursor used by the body_source function*/
506 vp = fr_pair_dcursor_by_da_init(&uctx->body_cursor, &uctx->request->request_pairs, attr_smtp_body);
507 fr_dbuff_init(&uctx->vp_in, (uint8_t const *)vp->vp_strvalue, vp->vp_length);
508
509 /* Add a mime part to mime_body for every body element */
510 while (vp) {
511 body_elements++;
512 MEM(part = curl_mime_addpart(mime_body));
513
514 curl_mime_encoder(part, "8bit");
515 curl_mime_data_cb(part, vp->vp_length, body_source, NULL, NULL, uctx);
516
518 }
519 RDEBUG2("initialized %d body element part(s)", body_elements);
520
521 /* Re-initialize the cursor for use when uploading the data to curl */
522 fr_pair_dcursor_by_da_init(&uctx->body_cursor, &uctx->request->request_pairs, attr_smtp_body);
523
524 /*
525 * Add body_mime as a subpart of the mime request with a local content-disposition
526 */
527 MEM(part = curl_mime_addpart(mime));
528 curl_mime_subparts(part, mime_body);
529 MEM(curl_mime_type(part, "multipart/mixed") == CURLE_OK);
530 uctx->body_header = curl_slist_append(NULL, "Content-Disposition: inline"); /* Initialize the body_header curl_slist */
531 curl_mime_headers(part, uctx->body_header, 1);
532
533 return body_elements;
534}
535
536/*
537 * Adds every SMTP_Attachments file to the email as a MIME part
538 */
539static int attachments_source(fr_mail_ctx_t *uctx, curl_mime *mime, rlm_smtp_t const *inst, rlm_smtp_env_t const *call_env)
540{
541 int attachments_set = 0;
542 fr_sbuff_uctx_talloc_t sbuff_ctx;
543 fr_sbuff_t path_buffer;
545 fr_value_box_t *vb = NULL;
546 fr_value_box_list_t const *list = call_env->attachments;
547 size_t i, list_count = talloc_array_length(call_env->attachments);
548
549
550 /* Make sure that a template directory is provided */
551 if (!inst->template_dir) return 0;
552
553 /* Initialize the buffer to write the file path */
554 fr_sbuff_init_talloc(uctx, &path_buffer, &sbuff_ctx, talloc_array_length(inst->template_dir) + 128, SIZE_MAX);
555
556 /* Write the initial path to the buffer */
557 fr_sbuff_in_bstrcpy_buffer(&path_buffer, inst->template_dir);
558
559 /* Make sure the template_directory path ends in a "/" */
560 if (inst->template_dir[talloc_array_length(inst->template_dir) - 2] != '/'){
561 (void) fr_sbuff_in_char(&path_buffer, '/');
562 }
563
564 /* Mark the buffer so we only re-write after the template_dir component */
565 fr_sbuff_marker(&m, &path_buffer);
566
567 /* Add the attachments to the email */
568 for (i = 0; i < list_count; i++) {
569 while ((vb = fr_value_box_list_next(list, vb))) {
570 if (str_to_attachments(uctx, mime, vb, &path_buffer, &m) < 0) return -1;
571
572 attachments_set++;
573 }
574 list++;
575 }
576
577 /* Check for any file attachments */
578 talloc_free(path_buffer.buff);
579 return attachments_set;
580}
581
582static void smtp_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
583{
584 fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
585 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
586 CURLMcode ret;
587
588 RDEBUG2("Forcefully cancelling pending SMTP request");
589
590 ret = curl_multi_remove_handle(t->mhandle->mandle, randle->candle); /* Gracefully terminate the request */
591 if (ret != CURLM_OK) {
592 RERROR("Failed removing curl handle from multi-handle: %s (%i)", curl_multi_strerror(ret), ret);
593 /* Not much we can do */
594 }
595 t->mhandle->transfers--;
596 smtp_slab_release(randle);
597}
598
599/** Callback to process response of SMTP server
600 *
601 * It checks if the response was CURLE_OK
602 * If it was, it tries to extract the certificate attributes
603 * If the response was not OK, we REJECT the request
604 * When responding to requests initiated by mod_authenticate this is simply
605 * a check on the username and password.
606 * When responding to requests initiated by mod_mail this indicates
607 * the mail has been queued.
608 */
610{
612 fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
613 fr_curl_tls_t const *tls = &inst->tls;
614 long curl_out;
615 long curl_out_valid;
616
617 curl_out_valid = curl_easy_getinfo(randle->candle, CURLINFO_SSL_VERIFYRESULT, &curl_out);
618
619 if (curl_out_valid == CURLE_OK){
620 RDEBUG2("server certificate %s verified", curl_out ? "was" : "not");
621 } else {
622 RDEBUG2("server certificate result not found");
623 }
624
625 if (randle->result != CURLE_OK) {
626 CURLcode result = randle->result;
627 smtp_slab_release(randle);
628
629 switch (result) {
630 case CURLE_PEER_FAILED_VERIFICATION:
631 case CURLE_LOGIN_DENIED:
633
634 default:
636 }
637 }
638
639 if (tls->extract_cert_attrs) fr_curl_response_certinfo(request, randle);
640 smtp_slab_release(randle);
641
643}
644
645/*
646 * Checks that there is a User-Name and User-Password field in the request
647 * As well as all of the required SMTP elements
648 * Sets the: username, password
649 * SMTP server URI
650 * timeout information
651 * TLS information
652 * Sender and recipient information
653 * Email header and body
654 * File attachments
655 *
656 * Then it queues the request and yields until a response is given
657 * When it responds, smtp_io_module_resume is called.
658 */
659static unlang_action_t CC_HINT(nonnull) mod_mail(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
660{
662 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
663 rlm_smtp_env_t *call_env = talloc_get_type_abort(mctx->env_data, rlm_smtp_env_t);
664 fr_curl_io_request_t *randle = NULL;
665 fr_mail_ctx_t *mail_ctx;
667 fr_pair_t const *smtp_body;
668
669 /* Elements provided by the request */
670 smtp_body = fr_pair_find_by_da(&request->request_pairs, NULL, attr_smtp_body);
671
672 /* Make sure all of the essential email components are present and possible*/
673 if (!smtp_body) {
674 RERROR("Attribute \"smtp-body\" is required for smtp");
676 }
677
678 if (!call_env->sender_address && !inst->envelope_address) {
679 RERROR("At least one of \"sender_address\" or \"envelope_address\" in the config, or \"SMTP-Sender-Address\" in the request is needed");
680 error:
681 if (randle) smtp_slab_release(randle);
682 RETURN_UNLANG_RCODE(rcode);
683 }
684
685 /*
686 * If the username is defined and is not static data
687 * a onetime connection is used, otherwise a persistent one
688 * can be used.
689 */
690 randle = (call_env->username_tmpl &&
691 !tmpl_is_data(call_env->username_tmpl)) ? smtp_slab_reserve(t->slab_onetime) :
692 smtp_slab_reserve(t->slab_persist);
693 if (!randle) {
694 RDEBUG2("A handle could not be allocated for the request");
696 }
697
698 /* Initialize the uctx to perform the email */
699 mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
700 *mail_ctx = (fr_mail_ctx_t) {
701 .request = request,
702 .randle = randle,
703 .mime = curl_mime_init(randle->candle),
704 .time = fr_time(), /* time the request was received. Used to set DATE: */
705 .recipients = NULL,
706 .header = NULL
707 };
708
709 FR_CURL_REQUEST_SET_OPTION(CURLOPT_UPLOAD, 1L);
710
711 /* Set the username and password if they have been provided */
712 if (call_env->username.vb_strvalue) {
713 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, call_env->username.vb_strvalue);
714
715 if (!call_env->password.vb_strvalue) goto skip_auth;
716
717 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, call_env->password.vb_strvalue);
718 RDEBUG3("Username and password set");
719 }
720
721skip_auth:
722 /* Set the envelope mail from address */
723 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_FROM,
724 (inst->envelope_address ? inst->envelope_address :
725 fr_value_box_list_head(call_env->sender_address)->vb_strvalue));
726
727 /* Set the recipients */
728 if (recipients_source(mail_ctx, call_env) <= 0) {
729 REDEBUG("At least one recipient is required to send an email");
730 goto error;
731 }
732 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_RCPT, mail_ctx->recipients);
733
734 /* Set the header elements */
735 if (header_source(mail_ctx, inst, call_env) != 0) {
736 REDEBUG("The header slist could not be generated");
737 rcode = RLM_MODULE_FAIL;
738 goto error;
739 }
740
741 /*
742 * CURLOPT_HTTPHEADER is the option that they use for the header in the curl example
743 *
744 * https://curl.haxx.se/libcurl/c/smtp-mime.html
745 */
746 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTPHEADER, mail_ctx->header);
747
748 /* Initialize the body elements to be uploaded */
749 if (body_init(mail_ctx, mail_ctx->mime) == 0) {
750 REDEBUG("The body could not be generated");
751 rcode = RLM_MODULE_FAIL;
752 goto error;
753 }
754
755 /* Initialize the attachments if there are any*/
756 if (attachments_source(mail_ctx, mail_ctx->mime, inst, call_env) < 0) {
757 rcode = RLM_MODULE_FAIL;
758 goto error;
759 }
760
761 /* Add the mime encoded elements to the curl request */
762 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MIMEPOST, mail_ctx->mime);
763
764 if (fr_curl_io_request_enqueue(t->mhandle, request, randle)) {
765 rcode = RLM_MODULE_FAIL;
766 goto error;
767 }
768
770}
771
772/*
773 * Sets the: username, password
774 * SMTP server URI
775 * timeout information
776 * and TLS information
777 *
778 * Then it queues the request and yields until a response is given
779 * When it responds, smtp_io_module_resume is called.
780 */
781static unlang_action_t CC_HINT(nonnull(1,2)) mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
782{
783 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
784 rlm_smtp_auth_env_t *env_data = talloc_get_type_abort(mctx->env_data, rlm_smtp_auth_env_t);
785 fr_curl_io_request_t *randle;
786
787 if (!env_data->username_tmpl) {
788 RDEBUG("No 'username' was set for authentication - failing the request");
790 }
791
792 if (!env_data->password_tmpl) {
793 RDEBUG("No 'username' was set for authentication - failing the request");
795 }
796
797 if (env_data->username.type != FR_TYPE_STRING || (env_data->username.vb_length == 0)) {
798 RWARN("\"%s\" is required for authentication", env_data->username_tmpl->name);
800 }
801
802 if (env_data->password.type != FR_TYPE_STRING || (env_data->password.vb_length == 0)) {
803 RWARN("\"%s\" is required for authentication", env_data->password_tmpl->name);
805 }
806
807 randle = smtp_slab_reserve(t->slab_onetime);
808 if (!randle) {
810 }
811
812 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, env_data->username.vb_strvalue);
813 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, env_data->password.vb_strvalue);
814
815 if (fr_curl_io_request_enqueue(t->mhandle, request, randle) < 0) {
816 error:
817 smtp_slab_release(randle);
819 }
820
822}
823
825 { .required = true, .concat = true, .type = FR_TYPE_STRING }, /* To: */
826 { .required = true, .concat = true, .type = FR_TYPE_STRING }, /* Subject: */
827 { .concat = true, .type = FR_TYPE_STRING }, /* Body */
829};
830
832 xlat_ctx_t const *xctx,
833 request_t *request, UNUSED fr_value_box_list_t *in)
834{
835 fr_curl_io_request_t *randle = talloc_get_type_abort(xctx->rctx, fr_curl_io_request_t);
836 fr_value_box_t *vb;
837
838 if (randle->result != CURLE_OK) {
839 RPERROR("Sending mail failed: %s %i", curl_easy_strerror(randle->result), randle->result);
840 smtp_slab_release(randle);
841 return XLAT_ACTION_FAIL;
842 }
843
844 smtp_slab_release(randle);
845
846 MEM(vb = fr_value_box_alloc(ctx, FR_TYPE_BOOL, NULL));
847 vb->vb_bool = true;
849
850 return XLAT_ACTION_DONE;
851}
852
853static void smtp_xlat_signal(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action)
854{
855 fr_curl_io_request_t *randle = talloc_get_type_abort(xctx->rctx, fr_curl_io_request_t);
856 rlm_smtp_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_smtp_thread_t);
857
858 smtp_io_module_signal(MODULE_CTX(xctx->mctx->mi, t, NULL, randle), request, action);
859}
860
861/** Xlat to send simple emails
862 *
863 * Example:
864 @verbatim
865 %smtp.send('bob@example.com', 'Something happened', 'More details about what happened')
866 @endverbatim
867 *
868 * @ingroup xlat_functions
869 */
871 xlat_ctx_t const *xctx, request_t *request,
872 fr_value_box_list_t *in)
873{
875 rlm_smtp_thread_t *t = talloc_get_type_abort(xctx->mctx->thread, rlm_smtp_thread_t);
876 fr_value_box_t *to, *subject, *body;
877 rlm_smtp_xlat_env_t *call_env = talloc_get_type_abort(xctx->env_data, rlm_smtp_xlat_env_t);
878 fr_curl_io_request_t *randle = NULL;
879 fr_mail_ctx_t *mail_ctx;
880 fr_value_box_t *vb = NULL;
881 char const *sender;
882 char *header_string;
883 curl_mimepart *part;
884
885 XLAT_ARGS(in, &to, &subject, &body);
886
887 randle = (call_env->username_tmpl &&
888 !tmpl_is_data(call_env->username_tmpl)) ? smtp_slab_reserve(t->slab_onetime) :
889 smtp_slab_reserve(t->slab_persist);
890 if (!randle) {
891 RERROR("A handle could not be allocated for the request");
892 return XLAT_ACTION_FAIL;
893 }
894
895 mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
896 *mail_ctx = (fr_mail_ctx_t) {
897 .request = request,
898 .randle = randle,
899 .mime = curl_mime_init(randle->candle),
900 .time = fr_time(),
901 };
902
903 FR_CURL_REQUEST_SET_OPTION(CURLOPT_UPLOAD, 1L);
904
905 /* Set the username and password if they have been provided */
906 if (call_env->username.vb_strvalue) {
907 FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, call_env->username.vb_strvalue);
908
909 if (!call_env->password.vb_strvalue) goto skip_auth;
910
911 FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, call_env->password.vb_strvalue);
912 RDEBUG2("Username and password set");
913 }
914
915skip_auth:
916 sender = inst->envelope_address ? inst->envelope_address : fr_value_box_list_head(call_env->sender_address)->vb_strvalue;
917 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_FROM, sender);
918 header_string = talloc_asprintf(call_env, "From: %s", sender);
919 mail_ctx->header = curl_slist_append(mail_ctx->header, header_string);
920
921 mail_ctx->recipients = curl_slist_append(mail_ctx->recipients, to->vb_strvalue);
922 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MAIL_RCPT, mail_ctx->recipients);
923 header_string = talloc_asprintf(call_env, "To: %s", to->vb_strvalue);
924 mail_ctx->header = curl_slist_append(mail_ctx->header, header_string);
925
926 header_string = talloc_asprintf(call_env, "Subject: %s", subject->vb_strvalue);
927 mail_ctx->header = curl_slist_append(mail_ctx->header, header_string);
928
929 if (fr_value_box_list_initialised(&call_env->headers)) {
930 while ((vb = fr_value_box_list_next(&call_env->headers, vb))) {
931 RDEBUG2("Adding header \"%pV\"", vb);
932 mail_ctx->header = curl_slist_append(mail_ctx->header, vb->vb_strvalue);
933 }
934 }
935 FR_CURL_REQUEST_SET_OPTION(CURLOPT_HTTPHEADER, mail_ctx->header);
936
937 MEM(part = curl_mime_addpart(mail_ctx->mime));
938 if (curl_mime_encoder(part, "8bit") != CURLE_OK) {
939 RERROR("Failed setting mime encoder");
940 error:
941 if (randle) smtp_slab_release(randle);
942 return XLAT_ACTION_FAIL;
943 }
944
945 if (body && (body->type == FR_TYPE_STRING)) {
946 if (curl_mime_data(part, body->vb_strvalue, body->vb_length) != CURLE_OK) {
947 RERROR("Failed adding email body");
948 goto error;
949 }
950 } else {
951 curl_mime_data(part, "", 0);
952 }
953 FR_CURL_REQUEST_SET_OPTION(CURLOPT_MIMEPOST, mail_ctx->mime);
954
955 if (fr_curl_io_request_enqueue(t->mhandle, request, randle)) goto error;
956
958}
959
960static int mod_bootstrap(module_inst_ctx_t const *mctx)
961{
962 xlat_t *xlat;
963
964 xlat = module_rlm_xlat_register(mctx->mi->boot, mctx, "send", smtp_send_xlat, FR_TYPE_BOOL);
967
968 return 0;
969}
970
971static int mod_instantiate(module_inst_ctx_t const *mctx)
972{
973 rlm_smtp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_smtp_t);
974
975 inst->conn_config.reuse.num_children = 1;
976 inst->conn_config.reuse.child_pool_size = sizeof(fr_mail_ctx_t);
977
978 return 0;
979}
980
981#define SMTP_COMMON_CLEANUP \
982 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t); \
983 if (mail_ctx->mime) curl_mime_free(mail_ctx->mime); \
984 if (mail_ctx->header) curl_slist_free_all(mail_ctx->header); \
985 if (mail_ctx->recipients) curl_slist_free_all(mail_ctx->recipients)
986
988{
990
991 if (randle->candle) curl_easy_cleanup(randle->candle);
992
993 return 0;
994}
995
997{
999
1000 if (randle->candle) curl_easy_reset(randle->candle);
1001
1002 return 0;
1003}
1004
1006{
1007 fr_mail_ctx_t *mail_ctx = NULL;
1008
1009 MEM(mail_ctx = talloc_zero(randle, fr_mail_ctx_t));
1010 randle->uctx = mail_ctx;
1011
1012 smtp_slab_element_set_destructor(randle, smtp_onetime_request_cleanup, NULL);
1013
1014 return 0;
1015}
1016
1018{
1019 if (mail_ctx->randle && mail_ctx->randle->candle) curl_easy_cleanup(mail_ctx->randle->candle);
1020
1021 return 0;
1022}
1023
1025{
1026 fr_mail_ctx_t *mail_ctx = NULL;
1027
1028 MEM(mail_ctx = talloc_zero(randle, fr_mail_ctx_t));
1029 mail_ctx->randle = randle;
1030 randle->uctx = mail_ctx;
1031 randle->candle = curl_easy_init();
1032 if (unlikely(!randle->candle)) {
1033 fr_strerror_printf("Unable to initialise CURL handle");
1034 return -1;
1035 }
1036 talloc_set_destructor(mail_ctx, smtp_mail_ctx_free);
1037
1038 smtp_slab_element_set_destructor(randle, smtp_persist_request_cleanup, NULL);
1039
1040 return 0;
1041}
1042
1044{
1045#if CURL_AT_LEAST_VERSION(7,45,0)
1046 FR_CURL_SET_OPTION(CURLOPT_DEFAULT_PROTOCOL, "smtp");
1047#endif
1048 FR_CURL_SET_OPTION(CURLOPT_URL, inst->uri);
1049#if CURL_AT_LEAST_VERSION(7,85,0)
1050 FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS_STR, "smtp,smtps");
1051#else
1052 FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS, CURLPROTO_SMTP | CURLPROTO_SMTPS);
1053#endif
1054 FR_CURL_SET_OPTION(CURLOPT_CONNECTTIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
1055 FR_CURL_SET_OPTION(CURLOPT_TIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
1056
1057 if (DEBUG_ENABLED3) FR_CURL_SET_OPTION(CURLOPT_VERBOSE, 1L);
1058
1059 if (fr_curl_easy_tls_init(randle, &inst->tls) != 0) goto error;
1060
1061 return 0;
1062error:
1063 return -1;
1064}
1065
1066static int smtp_onetime_conn_init(fr_curl_io_request_t *randle, void *uctx)
1067{
1068 rlm_smtp_t const *inst = talloc_get_type_abort(uctx, rlm_smtp_t);
1069 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
1070
1071 randle->candle = curl_easy_init();
1072 if (unlikely(!randle->candle)) {
1073 fr_strerror_printf("Unable to initialise CURL handle");
1074 return -1;
1075 }
1076
1077 memset(mail_ctx, 0, sizeof(fr_mail_ctx_t));
1078
1079 return smtp_conn_common_init(randle, inst);
1080}
1081
1082
1083static int smtp_persist_conn_init(fr_curl_io_request_t *randle, void *uctx)
1084{
1085 rlm_smtp_t const *inst = talloc_get_type_abort(uctx, rlm_smtp_t);
1086 fr_mail_ctx_t *mail_ctx = talloc_get_type_abort(randle->uctx, fr_mail_ctx_t);
1087
1088 memset(mail_ctx, 0, sizeof(fr_mail_ctx_t));
1089
1090 return smtp_conn_common_init(randle, inst);
1091}
1092
1093/*
1094 * Initialize a new thread with a curl instance
1095 */
1097{
1098 rlm_smtp_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_smtp_t);
1099 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
1100 fr_curl_handle_t *mhandle;
1101
1102 if (!(t->slab_onetime = smtp_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1104 inst, false, false))) {
1105 ERROR("Connection handle pool instantiation failed");
1106 return -1;
1107 }
1108 if (!(t->slab_persist = smtp_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
1110 inst, false, true))) {
1111 ERROR("Connection handle pool instantiation failed");
1112 return -1;
1113 }
1114
1115 mhandle = fr_curl_io_init(t, mctx->el, false);
1116 if (!mhandle) return -1;
1117
1118 t->mhandle = mhandle;
1119 return 0;
1120}
1121
1122/*
1123 * Close the thread and free the memory
1124 */
1126{
1127 rlm_smtp_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_smtp_thread_t);
1128
1129 talloc_free(t->mhandle);
1132 return 0;
1133}
1134
1135/** Parse the header section into tmpls for producing email headers
1136 *
1137 */
1138static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
1139 CONF_ITEM *ci,
1140 UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
1141{
1142 CONF_SECTION const *cs = cf_item_to_section(ci);
1143 CONF_ITEM const *item = NULL;
1144 CONF_PAIR const *cp;
1145 call_env_parsed_t *parsed_env;
1146 tmpl_t *parsed_tmpl;
1147 ssize_t slen;
1148
1149 while ((item = cf_item_next(cs, item))) {
1150 char *to_parse;
1151
1152 if (!cf_item_is_pair(item)) {
1153 cf_log_err(item, "Entry is not in \"header = value\" format");
1154 return -1;
1155 }
1156 cp = cf_item_to_pair(item);
1157
1158 MEM(parsed_env = call_env_parsed_add(ctx, out,
1160
1161 /*
1162 * Turn the conf pair `attr = value` to email header format
1163 */
1164 to_parse = talloc_asprintf(NULL, "%s: %s", cf_pair_attr(cp), cf_pair_value(cp));
1165
1166 slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
1167 &FR_SBUFF_IN(to_parse, talloc_strlen(to_parse)),
1169 t_rules);
1170 talloc_free(to_parse);
1171
1172 if (slen <= 0) {
1173 cf_canonicalize_error(cp, slen, "Failed parsing header", cf_pair_value(cp));
1174 error:
1175 call_env_parsed_free(out, parsed_env);
1176 return -1;
1177 }
1178
1179 if (tmpl_needs_resolving(parsed_tmpl) &&
1180 (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = t_rules->attr.dict_def }) < 0)) {
1181 cf_log_perr(cp, "Failed resolving header");
1182 goto error;
1183 }
1184
1185 call_env_parsed_set_tmpl(parsed_env, parsed_tmpl);
1186 }
1187
1188 return 0;
1189}
1190
1193 .env = (call_env_parser_t[]) {
1195 .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
1199 .pair.dflt = "SMTP-Recipients[*]", .pair.dflt_quote = T_BARE_WORD },
1201 .pair.dflt = "SMTP-TO[*]", .pair.dflt_quote = T_BARE_WORD },
1203 .pair.dflt = "SMTP-CC[*]", .pair.dflt_quote = T_BARE_WORD },
1205 .pair.dflt = "SMTP-BCC[*]", .pair.dflt_quote = T_BARE_WORD },
1207 .pair.dflt = "SMTP-Attachments[*]", .pair.dflt_quote = T_BARE_WORD },
1209
1211 }
1212};
1213
1216 .env = (call_env_parser_t[]) {
1218 ((call_env_parser_t[]) {
1221 rlm_smtp_auth_env_t, username, username_tmpl), .pair.dflt = "User-Name", .pair.dflt_quote = T_BARE_WORD },
1224 rlm_smtp_auth_env_t, password, password_tmpl), .pair.dflt = "User-Password", .pair.dflt_quote = T_BARE_WORD },
1226 }))},
1228 }
1229};
1230
1231/*
1232 * The module name should be the only globally exported symbol.
1233 * That is, everything else should be 'static'.
1234 *
1235 * If the module needs to temporarily modify it's instantiation
1236 * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1237 * The server will then take care of ensuring that the module
1238 * is single-threaded.
1239 */
1240extern module_rlm_t rlm_smtp;
1242 .common = {
1243 .magic = MODULE_MAGIC_INIT,
1244 .name = "smtp",
1245 .inst_size = sizeof(rlm_smtp_t),
1246 .thread_inst_size = sizeof(rlm_smtp_thread_t),
1247 .config = module_config,
1248 .bootstrap = mod_bootstrap,
1249 .instantiate = mod_instantiate,
1250 .thread_instantiate = mod_thread_instantiate,
1251 .thread_detach = mod_thread_detach,
1252 },
1253 .method_group = {
1254 .bindings = (module_method_binding_t[]){
1255 { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate, .method_env = &auth_env },
1256 { .section = SECTION_NAME("mail", CF_IDENT_ANY), .method = mod_mail, .method_env = &method_env },
1257
1259 }
1260 }
1261};
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:506
#define unlikely(_x)
Definition build.h:402
#define UNUSED
Definition build.h:336
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:777
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:690
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:719
#define CALL_ENV_TERMINATOR
Definition call_env.h:236
#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:365
#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:240
call_env_parser_t const * env
Parsing rules for call method env.
Definition call_env.h:247
#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs)
Specify a call_env_parser_t which defines a nested subsection.
Definition call_env.h:402
@ 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_SECRET
The value is a secret, and should not be logged.
Definition call_env.h:91
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition call_env.h:75
@ CALL_ENV_FLAG_PARSE_MISSING
If this subsection is missing, still parse it.
Definition call_env.h:88
@ CALL_ENV_FLAG_BARE_WORD_ATTRIBUTE
bare words are treated as an attribute, but strings may be xlats.
Definition call_env.h:92
@ 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:412
#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:340
Per method call config.
Definition call_env.h:180
#define CONF_PARSER_TERMINATOR
Definition cf_parse.h:657
#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:280
#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:309
Defines a CONF_PAIR to C data type mapping.
Definition cf_parse.h:594
Common header for all CONF_* types.
Definition cf_priv.h:49
Configuration AVP similar to a fr_pair_t.
Definition cf_priv.h:72
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:631
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition cf_util.c:683
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition cf_util.c:1618
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition cf_util.c:663
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition cf_util.c:1574
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition cf_util.c:1558
#define cf_log_err(_cf, _fmt,...)
Definition cf_util.h:285
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition cf_util.h:363
#define cf_item_next(_parent, _curr)
Definition cf_util.h:89
#define cf_log_perr(_cf, _fmt,...)
Definition cf_util.h:292
#define CF_IDENT_ANY
Definition cf_util.h:75
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:478
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:775
#define fr_dbuff_init(_out, _start, _len_or_end)
Initialise an dbuff for encoding or decoding.
Definition dbuff.h:362
#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:751
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
Definition dbuff.h:1447
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition dcursor.h:288
static int fr_dcursor_append(fr_dcursor_t *cursor, void *v)
Insert a single item at the end of the list.
Definition dcursor.h:406
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:46
#define ERROR(fmt,...)
Definition dhcpclient.c:40
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:292
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:305
#define DICT_AUTOLOAD_TERMINATOR
Definition dict.h:311
static fr_slen_t in
Definition dict.h:882
Specifies an attribute which must be present for the module to function.
Definition dict.h:291
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:304
#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
static xlat_action_t smtp_send_xlat(UNUSED TALLOC_CTX *ctx, UNUSED fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, fr_value_box_list_t *in)
Xlat to send simple emails.
Definition rlm_smtp.c:870
talloc_free(hp)
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:396
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:355
#define RWARN(fmt,...)
Definition log.h:309
#define RERROR(fmt,...)
Definition log.h:310
#define RPERROR(fmt,...)
Definition log.h:314
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition log.h:259
#define RPEDEBUG2(fmt,...)
Definition log.h:389
#define REDEBUG2(fmt,...)
Definition log.h:384
int rad_filename_box_make_safe(fr_value_box_t *vb, UNUSED void *uxtc)
Definition util.c:131
#define fr_time()
Definition event.c:60
static void * item(fr_lst_t const *lst, fr_lst_index_t idx)
Definition lst.c:121
@ FR_TYPE_STRING
String of printable characters.
@ FR_TYPE_BOOL
A truth value.
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
#define MODULE_CTX(_mi, _thread, _env_data, _rctx)
Wrapper to create a module_ctx_t as a compound literal.
Definition module_ctx.h:128
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
xlat_t * module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx, char const *name, xlat_func_t func, fr_type_t return_type)
Definition module_rlm.c:247
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:707
#define REDEBUG(fmt,...)
#define RDEBUG2(fmt,...)
#define RDEBUG(fmt,...)
#define RETURN_UNLANG_INVALID
Definition rcode.h:66
#define RETURN_UNLANG_RCODE(_rcode)
Definition rcode.h:61
#define RETURN_UNLANG_FAIL
Definition rcode.h:63
#define RETURN_UNLANG_REJECT
Definition rcode.h:62
#define RETURN_UNLANG_OK
Definition rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:44
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:51
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:48
static const call_env_method_t auth_env
Definition rlm_smtp.c:1214
struct curl_slist * recipients
Definition rlm_smtp.c:167
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:400
fr_value_box_list_t headers
Entries to add to email header.
Definition rlm_smtp.c:91
fr_time_t time
Definition rlm_smtp.c:171
fr_value_box_list_t * bcc_addrs
The address(es) used for the Bcc: header.
Definition rlm_smtp.c:70
global_lib_autoinst_t const *const rlm_smtp_lib[]
Definition rlm_smtp.c:55
static unlang_action_t smtp_io_module_resume(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Callback to process response of SMTP server.
Definition rlm_smtp.c:609
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:325
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:202
static int smtp_persist_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:1024
static const call_env_method_t smtp_call_env_xlat
Definition rlm_smtp.c:97
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:231
fr_value_box_t username
Value to use for user name.
Definition rlm_smtp.c:78
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:539
static void smtp_xlat_signal(xlat_ctx_t const *xctx, request_t *request, fr_signal_t action)
Definition rlm_smtp.c:853
static int smtp_persist_conn_init(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_smtp.c:1083
fr_dcursor_t cursor
Definition rlm_smtp.c:163
smtp_slab_list_t * slab_persist
Slab list for persistent connections.
Definition rlm_smtp.c:153
static int smtp_conn_common_init(fr_curl_io_request_t *randle, rlm_smtp_t const *inst)
Definition rlm_smtp.c:1043
fr_curl_io_request_t * randle
Definition rlm_smtp.c:162
static fr_dict_attr_t const * attr_smtp_body
Definition rlm_smtp.c:45
static const call_env_method_t method_env
Definition rlm_smtp.c:1191
static int smtp_onetime_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:987
static fr_dict_t const * dict_freeradius
Definition rlm_smtp.c:36
fr_value_box_t password
Password for authenticated mails.
Definition rlm_smtp.c:89
fr_dict_attr_autoload_t rlm_smtp_dict_attr[]
Definition rlm_smtp.c:48
fr_time_delta_t timeout
Timeout for connection and server response.
Definition rlm_smtp.c:115
static int recipients_source(fr_mail_ctx_t *uctx, rlm_smtp_env_t const *call_env)
Definition rlm_smtp.c:357
fr_dict_autoload_t rlm_smtp_dict[]
Definition rlm_smtp.c:39
fr_curl_conn_config_t conn_config
Reusable CURL handle config.
Definition rlm_smtp.c:120
tmpl_t * username_tmpl
tmpl expanded to populate username
Definition rlm_smtp.c:79
static void smtp_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition rlm_smtp.c:582
char const * uri
URI of smtp server.
Definition rlm_smtp.c:111
fr_value_box_t username
User to authenticate as when sending emails.
Definition rlm_smtp.c:63
fr_dbuff_t vp_in
Definition rlm_smtp.c:165
fr_value_box_t password
Value to use for password.
Definition rlm_smtp.c:80
static int mod_bootstrap(module_inst_ctx_t const *mctx)
Definition rlm_smtp.c:960
struct curl_slist * header
Definition rlm_smtp.c:168
tmpl_t * password_tmpl
tmpl expanded to populate password
Definition rlm_smtp.c:81
static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule)
static fr_dict_attr_t const * attr_smtp_header
Definition rlm_smtp.c:44
fr_dcursor_t body_cursor
Definition rlm_smtp.c:164
static int smtp_persist_request_cleanup(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:996
curl_mime * mime
Definition rlm_smtp.c:173
fr_value_box_list_t * cc_addrs
The address(es) used for the Cc: header.
Definition rlm_smtp.c:69
fr_value_box_list_t * to_addrs
The address(es) used for the To: header.
Definition rlm_smtp.c:68
#define SMTP_COMMON_CLEANUP
Definition rlm_smtp.c:981
request_t * request
Definition rlm_smtp.c:161
static int body_init(fr_mail_ctx_t *uctx, curl_mime *mime)
Definition rlm_smtp.c:492
fr_value_box_list_t headers
Entries to add to email header.
Definition rlm_smtp.c:72
module_rlm_t rlm_smtp
Definition rlm_smtp.c:1241
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Definition rlm_smtp.c:1096
struct curl_slist * body_header
Definition rlm_smtp.c:169
static unlang_action_t mod_authenticate(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_smtp.c:781
bool set_date
Definition rlm_smtp.c:118
static int str_to_attachments(fr_mail_ctx_t *uctx, curl_mime *mime, fr_value_box_t *vb, fr_sbuff_t *path_buffer, fr_sbuff_marker_t *m)
Definition rlm_smtp.c:282
static unlang_action_t mod_mail(unlang_result_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition rlm_smtp.c:659
static int smtp_onetime_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
Definition rlm_smtp.c:1005
fr_value_box_list_t * sender_address
The address(es) used to generate the From: header.
Definition rlm_smtp.c:90
fr_value_box_t password
Password for authenticated mails.
Definition rlm_smtp.c:65
char const * envelope_address
The address used to send the message.
Definition rlm_smtp.c:113
fr_value_box_list_t * recipient_addrs
The address(es) used as recipients. Overrides elements in to, cc and bcc.
Definition rlm_smtp.c:67
tmpl_t * username_tmpl
The tmpl used to produce the above.
Definition rlm_smtp.c:88
static int smtp_onetime_conn_init(fr_curl_io_request_t *randle, void *uctx)
Definition rlm_smtp.c:1066
char time_str[60]
Definition rlm_smtp.c:172
tmpl_t * username_tmpl
The tmpl used to produce the above.
Definition rlm_smtp.c:64
static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
Definition rlm_smtp.c:451
static xlat_arg_parser_t const smtp_xlat_args[]
Definition rlm_smtp.c:824
char const * template_dir
The directory that contains all email attachments.
Definition rlm_smtp.c:112
fr_value_box_t username
User to authenticate as when sending emails.
Definition rlm_smtp.c:87
fr_curl_tls_t tls
Used for handled all tls specific curl components.
Definition rlm_smtp.c:116
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:179
static const conf_parser_t module_config[]
Definition rlm_smtp.c:126
static int smtp_mail_ctx_free(fr_mail_ctx_t *mail_ctx)
Definition rlm_smtp.c:1017
fr_value_box_list_t * attachments
List of files to attach.
Definition rlm_smtp.c:71
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Definition rlm_smtp.c:1125
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition rlm_smtp.c:971
fr_curl_handle_t * mhandle
Thread specific multi handle.
Definition rlm_smtp.c:151
smtp_slab_list_t * slab_onetime
Slab list for onetime use connections.
Definition rlm_smtp.c:154
static xlat_action_t smtp_send_xlat_resume(TALLOC_CTX *ctx, fr_dcursor_t *out, xlat_ctx_t const *xctx, request_t *request, UNUSED fr_value_box_list_t *in)
Definition rlm_smtp.c:831
fr_value_box_list_t * sender_address
The address(es) used to generate the From: header.
Definition rlm_smtp.c:66
Call environment for SMTP authentication.
Definition rlm_smtp.c:77
Call environment for sending emails.
Definition rlm_smtp.c:62
Call environment for sending simple emails using an xlat.
Definition rlm_smtp.c:86
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:1471
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:1495
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:1515
#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:137
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition section.h:39
size_t inst_size
Size of the module's instance data.
Definition module.h:212
void * data
Module's instance data.
Definition module.h:293
void * boot
Data allocated during the boostrap phase.
Definition module.h:296
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition module.h:152
Named methods exported by a module.
Definition module.h:174
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:206
tmpl_attr_rules_t attr
Rules/data for parsing attribute references.
Definition tmpl.h:339
#define tmpl_needs_resolving(vpt)
Definition tmpl.h:223
Similar to tmpl_rules_t, but used to specify parameters that may change during subsequent resolution ...
Definition tmpl.h:368
Optional arguments passed to vp_tmpl functions.
Definition tmpl.h:336
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:124
#define FR_SLAB_TYPES(_name, _type)
Define type specific wrapper structs for slabs and slab elements.
Definition slab.h:75
return count
Definition module.c:155
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:431
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
fr_dict_t const * dict_def
Default dictionary to use with unqualified attribute references.
Definition tmpl.h:273
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
#define talloc_get_type_abort_const
Definition talloc.h:110
#define talloc_asprintf
Definition talloc.h:144
static size_t talloc_strlen(char const *s)
Returns the length of a talloc array containing a string.
Definition talloc.h:136
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:503
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:118
@ T_DOUBLE_QUOTED_STRING
Definition token.h:119
xlat_action_t unlang_xlat_yield(request_t *request, xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition xlat.c:543
#define XLAT_ARGS(_list,...)
Populate local variables with value boxes from the input list.
Definition xlat.h:383
unsigned int required
Argument must be present, and non-empty.
Definition xlat.h:146
#define XLAT_ARG_PARSER_TERMINATOR
Definition xlat.h:170
xlat_action_t
Definition xlat.h:37
@ XLAT_ACTION_FAIL
An xlat function failed.
Definition xlat.h:44
@ XLAT_ACTION_DONE
We're done evaluating this level of nesting.
Definition xlat.h:43
Definition for a single argument consumed by an xlat function.
Definition xlat.h:145
#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:639
#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:611
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:4181
#define fr_value_box_alloc(_ctx, _type, _enumv)
Allocate a value box of a specific type.
Definition value.h:644
#define fr_value_box_is_safe_for(_box, _safe_for)
Definition value.h:1100
uintptr_t fr_value_box_safe_for_t
Escaping that's been applied to a value box.
Definition value.h:162
int nonnull(2, 5))
static size_t char ** out
Definition value.h:1030
void * rctx
Resume context.
Definition xlat_ctx.h:54
void * env_data
Expanded call env data.
Definition xlat_ctx.h:53
module_ctx_t const * mctx
Synthesised module calling ctx.
Definition xlat_ctx.h:52
An xlat calling ctx.
Definition xlat_ctx.h:49
int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
Register the arguments of an xlat.
Definition xlat_func.c:363
void xlat_func_call_env_set(xlat_t *x, call_env_method_t const *env_method)
Register call environment of an xlat.
Definition xlat_func.c:389