The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 8d333217075dd17dd383f6927421eb0411af0bb7 $
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  */
25 RCSID("$Id: 8d333217075dd17dd383f6927421eb0411af0bb7 $")
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 
38 static fr_dict_t const *dict_freeradius;
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 
56 extern global_lib_autoinst_t const * const rlm_smtp_lib[];
60 };
61 
62 /** Call environment for sending emails.
63 */
64 typedef 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  */
79 typedef 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 
86 typedef 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 
94  bool set_date;
95 
96  fr_curl_conn_config_t conn_config; //!< Reusable CURL handle config
97 } rlm_smtp_t;
98 
99 /*
100  * A mapping of configuration file names to internal variables.
101  */
102 static 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 
126 typedef 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  */
136 typedef 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;
150 } fr_mail_ctx_t;
151 
152 /*
153  * Adds every element associated with a dict_attr to a curl_slist
154  */
155 static 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  */
178 static 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  */
207 static 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  */
258 static 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  */
301 static 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  */
333 static 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  */
376 static 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  */
427 static size_t body_source(char *ptr, size_t size, size_t nmemb, void *mail_ctx)
428 {
429  fr_mail_ctx_t *uctx = mail_ctx;
430  fr_dbuff_t out;
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  */
455  vp = fr_dcursor_next(&uctx->body_cursor);
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  */
468 static 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 
493  vp = fr_dcursor_next(&uctx->body_cursor);
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  */
515 static 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 
558 static 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  */
632 static 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 
694 skip_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 
734  if (fr_curl_io_request_enqueue(t->mhandle, request, randle)) RETURN_MODULE_INVALID;
735 
736  return unlang_module_yield(request, smtp_io_module_resume, smtp_io_module_signal, ~FR_SIGNAL_CANCEL, randle);
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  */
748 static 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 
772  if (fr_curl_io_request_enqueue(t->mhandle, request, randle)) RETURN_MODULE_INVALID;
773 
774  return unlang_module_yield(request, smtp_io_module_resume, smtp_io_module_signal, ~FR_SIGNAL_CANCEL, randle);
775 }
776 
777 static int mod_instantiate(module_inst_ctx_t const *mctx)
778 {
779  rlm_smtp_t *inst = talloc_get_type_abort(mctx->inst->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 
811 static int smtp_onetime_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
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 
823 static int smtp_mail_ctx_free(fr_mail_ctx_t *mail_ctx)
824 {
825  if (mail_ctx->randle && mail_ctx->randle->candle) curl_easy_cleanup(mail_ctx->randle->candle);
826 
827  return 0;
828 }
829 
830 static int smtp_persist_conn_alloc(fr_curl_io_request_t *randle, UNUSED void *uctx)
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 
849 static inline int smtp_conn_common_init(fr_curl_io_request_t *randle, rlm_smtp_t const *inst)
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;
868 error:
869  return -1;
870 }
871 
872 static 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 
889 static 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->inst->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 
935  talloc_free(t->mhandle);
938  return 0;
939 }
940 
941 /** Parse the header section into tmpls for producing email headers
942  *
943  */
944 static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
945  CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2,
946  UNUSED void const *data, 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),
974  cf_pair_value_quote(cp), NULL, t_rules);
975  talloc_free(to_parse);
976 
977  if (slen <= 0) {
978  cf_canonicalize_error(cp, slen, "Failed parsing header", cf_pair_value(cp));
979  error:
980  call_env_parsed_free(out, parsed_env);
981  return -1;
982  }
983 
984  if (tmpl_needs_resolving(parsed_tmpl) &&
985  (tmpl_resolve(parsed_tmpl, &(tmpl_res_rules_t){ .dict_def = t_rules->attr.dict_def }) < 0)) {
986  cf_log_perr(cp, "Failed resolving header");
987  goto error;
988  }
989 
990  call_env_parsed_set_tmpl(parsed_env, parsed_tmpl);
991  }
992 
993  return 0;
994 }
995 
998  .env = (call_env_parser_t[]) {
1000  .pair.dflt_quote = T_DOUBLE_QUOTED_STRING },
1002  { FR_CALL_ENV_OFFSET("sender_address", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, rlm_smtp_env_t, sender_address) },
1003  { FR_CALL_ENV_OFFSET("recipients", FR_TYPE_STRING, CALL_ENV_FLAG_NULLABLE, rlm_smtp_env_t, recipient_addrs),
1004  .pair.dflt = "&SMTP-Recipients[*]", .pair.dflt_quote = T_BARE_WORD },
1006  .pair.dflt = "&SMTP-TO[*]", .pair.dflt_quote = T_BARE_WORD },
1008  .pair.dflt = "&SMTP-CC[*]", .pair.dflt_quote = T_BARE_WORD },
1010  .pair.dflt = "&SMTP-BCC[*]", .pair.dflt_quote = T_BARE_WORD },
1012  .pair.dflt = "&SMTP-Attachments[*]", .pair.dflt_quote = T_BARE_WORD },
1014 
1016  }
1017 };
1018 
1019 static const call_env_method_t auth_env = {
1021  .env = (call_env_parser_t[]) {
1022  { FR_CALL_ENV_PARSE_OFFSET("username_attribute", FR_TYPE_STRING,
1024  rlm_smtp_auth_env_t, username, username_tmpl), .pair.dflt = "&User-Name", .pair.dflt_quote = T_BARE_WORD },
1025  { FR_CALL_ENV_PARSE_OFFSET("password_attribute", FR_TYPE_STRING,
1027  rlm_smtp_auth_env_t, password, password_tmpl), .pair.dflt = "&User-Password", .pair.dflt_quote = T_BARE_WORD },
1029  }
1030 };
1031 
1032 /*
1033  * The module name should be the only globally exported symbol.
1034  * That is, everything else should be 'static'.
1035  *
1036  * If the module needs to temporarily modify it's instantiation
1037  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
1038  * The server will then take care of ensuring that the module
1039  * is single-threaded.
1040  */
1041 extern module_rlm_t rlm_smtp;
1043  .common = {
1044  .magic = MODULE_MAGIC_INIT,
1045  .name = "smtp",
1046  .flags = MODULE_TYPE_THREAD_SAFE,
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_names = (module_method_name_t[]){
1055  { .name1 = "mail", .name2 = CF_IDENT_ANY, .method = mod_mail,
1056  .method_env = &method_env },
1057  { .name1 = "authenticate", .name2 = CF_IDENT_ANY, .method = mod_authenticate,
1058  .method_env = &auth_env },
1060  }
1061 };
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:444
#define unlikely(_x)
Definition: build.h:378
#define UNUSED
Definition: build.h:313
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:700
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:613
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:642
#define CALL_ENV_TERMINATOR
Definition: call_env.h:212
#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:341
#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:216
#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _ident2, _flags, _func)
Specify a call_env_parser_t which parses a subsection using a callback function.
Definition: call_env.h:388
@ CALL_ENV_FLAG_CONCAT
If the tmpl produced multiple boxes they should be concatenated.
Definition: call_env.h:74
@ CALL_ENV_FLAG_SUBSECTION
This is a subsection.
Definition: call_env.h:85
@ CALL_ENV_FLAG_ATTRIBUTE
Tmpl must contain an attribute reference.
Definition: call_env.h:84
@ CALL_ENV_FLAG_SECRET
The value is a secret, and should not be logged.
Definition: call_env.h:89
@ CALL_ENV_FLAG_NONE
Definition: call_env.h:72
@ CALL_ENV_FLAG_REQUIRED
Associated conf pair or section is required.
Definition: call_env.h:73
@ CALL_ENV_FLAG_NULLABLE
Tmpl expansions are allowed to produce no output.
Definition: call_env.h:78
#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:316
Per method call config.
Definition: call_env.h:171
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
#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:563
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:89
bool cf_item_is_pair(CONF_ITEM const *ci)
Determine if CONF_ITEM is a CONF_PAIR.
Definition: cf_util.c:597
CONF_PAIR * cf_item_to_pair(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_PAIR.
Definition: cf_util.c:629
char const * cf_pair_attr(CONF_PAIR const *pair)
Return the attr of a CONF_PAIR.
Definition: cf_util.c:1495
char const * cf_pair_value(CONF_PAIR const *pair)
Return the value of a CONF_PAIR.
Definition: cf_util.c:1511
CONF_SECTION * cf_item_to_section(CONF_ITEM const *ci)
Cast a CONF_ITEM to a CONF_SECTION.
Definition: cf_util.c:649
fr_token_t cf_pair_value_quote(CONF_PAIR const *pair)
Return the value (rhs) quoting of a pair.
Definition: cf_util.c:1555
#define cf_log_err(_cf, _fmt,...)
Definition: cf_util.h:265
#define cf_item_next(_ci, _prev)
Definition: cf_util.h:92
#define cf_canonicalize_error(_ci, _slen, _msg, _str)
Definition: cf_util.h:340
#define cf_log_perr(_cf, _fmt,...)
Definition: cf_util.h:272
#define CF_IDENT_ANY
Definition: cf_util.h:78
#define FR_CURL_REQUEST_SET_OPTION(_x, _y)
Definition: base.h:67
fr_curl_handle_t * fr_curl_io_init(TALLOC_CTX *ctx, fr_event_list_t *el, bool multiplex)
CURLcode result
Result of executing the request.
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:471
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:762
#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:738
#define fr_dbuff_in_memcpy_partial(_out, _in, _inlen)
Copy at most _inlen bytes into the dbuff.
Definition: dbuff.h:1433
static void * fr_dcursor_next(fr_dcursor_t *cursor)
Advanced the cursor to the next item.
Definition: dcursor.h:287
static void * fr_dcursor_current(fr_dcursor_t *cursor)
Return the item the cursor current points to.
Definition: dcursor.h:336
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
#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:114
int fr_curl_easy_tls_init(fr_curl_io_request_t *randle, fr_curl_tls_t const *conf)
Definition: base.c:90
global_lib_autoinst_t fr_curl_autoinst
Definition: base.c:342
conf_parser_t fr_curl_conn_config[]
Definition: base.c:84
conf_parser_t fr_curl_tls_config[]
Definition: base.c:56
#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.
Definition: merged_model.c:83
long int ssize_t
Definition: merged_model.c:24
unsigned char uint8_t
Definition: merged_model.c:30
void * env_data
Per call environment data.
Definition: module_ctx.h:44
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:63
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
void * thread
Thread instance data.
Definition: module_ctx.h:62
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:59
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:51
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:58
Specifies a module method identifier.
Definition: module_method.c:36
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
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:688
#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
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
username
Definition: rlm_securid.c:420
static const call_env_method_t auth_env
Definition: rlm_smtp.c:1019
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:996
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 char const *section_name1, UNUSED char const *section_name2, UNUSED void const *data, UNUSED call_env_parser_t const *rule)
Parse the header section into tmpls for producing email headers.
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:1042
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:1419
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:1445
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:1467
#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:114
@ MODULE_TYPE_THREAD_SAFE
Module is threadsafe.
Definition: module.h:49
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
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
Definition: signal.h:48
#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:175
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:575
RETURN_MODULE_FAIL
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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:270
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:536
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition: time.h:635
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:627
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
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:3521
static fr_slen_t data
Definition: value.h:1259
int nonnull(2, 5))
static size_t char ** out
Definition: value.h:984