The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_imap.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: 816e6231a0dd9558741e933e5a48c618899d517b $
19  * @file rlm_imap.c
20  * @brief imap server authentication.
21  *
22  * @copyright 2020 The FreeRADIUS server project
23  * @copyright 2020 Network RADIUS SAS (legal@networkradius.com)
24  */
25 RCSID("$Id: 816e6231a0dd9558741e933e5a48c618899d517b $")
26 
27 #include <freeradius-devel/curl/base.h>
28 #include <freeradius-devel/server/base.h>
29 #include <freeradius-devel/server/global_lib.h>
30 #include <freeradius-devel/server/module_rlm.h>
31 #include <freeradius-devel/util/slab.h>
32 
33 static fr_dict_t const *dict_radius; /*dictionary for radius protocol*/
34 
37 
40  { .out = &dict_radius, .proto = "radius" },
41  { NULL }
42 };
43 
46  { .out = &attr_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_radius },
47  { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius },
48  { NULL },
49 };
50 
51 extern global_lib_autoinst_t const * const rlm_imap_lib[];
55 };
56 
57 typedef struct {
58  char const *uri; //!< URI of imap server
59  fr_time_delta_t timeout; //!< Timeout for connection and server response
61  fr_curl_conn_config_t conn_config; //!< Reusable CURL handle config
62 } rlm_imap_t;
63 
66 
67 typedef struct {
68  imap_slab_list_t *slab; //!< Slab list for connection handles.
69  fr_curl_handle_t *mhandle; //!< Thread specific multi handle. Serves as the dispatch and coralling structure for imap requests.
71 
72 /*
73  * A mapping of configuration file names to internal variables.
74  */
75 static const conf_parser_t module_config[] = {
76  { FR_CONF_OFFSET("uri", rlm_imap_t, uri) },
77  { FR_CONF_OFFSET("timeout", rlm_imap_t, timeout), .dflt = "5.0" },
78  { FR_CONF_OFFSET_SUBSECTION("tls", 0, rlm_imap_t, tls, fr_curl_tls_config ) },//!<loading the tls values
79  { FR_CONF_OFFSET_SUBSECTION("connection", 0, rlm_imap_t, conn_config, fr_curl_conn_config ) },
81 };
82 
83 static void imap_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
84 {
85  fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
86  rlm_imap_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_imap_thread_t);
87  CURLMcode ret;
88 
89  RDEBUG2("Forcefully cancelling pending IMAP request");
90 
91  ret = curl_multi_remove_handle(t->mhandle->mandle, randle->candle); /* Gracefully terminate the request */
92  if (ret != CURLM_OK) {
93  RERROR("Failed removing curl handle from multi-handle: %s (%i)", curl_multi_strerror(ret), ret);
94  /* Not much we can do */
95  }
96  t->mhandle->transfers--;
97  imap_slab_release(randle);
98 }
99 
100 /*
101  * Called when the IMAP server responds
102  * It checks if the response was CURLE_OK
103  * If it wasn't we returns REJECT, if it was we returns OK
104 */
106  request_t *request)
107 {
109  fr_curl_io_request_t *randle = talloc_get_type_abort(mctx->rctx, fr_curl_io_request_t);
110  fr_curl_tls_t const *tls;
111  long curl_out;
112  long curl_out_valid;
113 
114  tls = &inst->tls;
115 
116  curl_out_valid = curl_easy_getinfo(randle->candle, CURLINFO_SSL_VERIFYRESULT, &curl_out);
117  if (curl_out_valid == CURLE_OK){
118  RDEBUG2("server certificate %s verified", curl_out ? "was" : "not");
119  } else {
120  RDEBUG2("server certificate result not found");
121  }
122 
123  if (randle->result != CURLE_OK) {
124  CURLcode result = randle->result;
125  imap_slab_release(randle);
126  switch(result) {
127  case CURLE_PEER_FAILED_VERIFICATION:
128  case CURLE_LOGIN_DENIED:
130  default:
132  }
133  }
134 
135  if (tls->extract_cert_attrs) fr_curl_response_certinfo(request, randle);
136 
137  imap_slab_release(randle);
139 }
140 
141 /*
142  * Checks that there is a User-Name and User-Password field in the request
143  * Checks that User-Password is not Blank
144  * Sets the: username, password
145  * website URI
146  * timeout information
147  * and TLS information
148  *
149  * Then it queues the request and yields until a response is given
150  * When it responds, mod_authenticate_resume is called.
151  */
152 static unlang_action_t CC_HINT(nonnull(1,2)) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
153 {
154  rlm_imap_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_imap_thread_t);
155 
156  fr_pair_t const *username;
157  fr_pair_t const *password;
158  fr_curl_io_request_t *randle;
159 
160  username = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
161  password = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_password);
162 
163  if (!username) {
164  REDEBUG("Attribute \"User-Name\" is required for authentication");
166  }
167 
168  if (!password) {
169  RDEBUG2("Attribute \"User-Password\" is required for authentication");
171  }
172 
173  if (password->vp_length == 0) {
174  RDEBUG2("\"User-Password\" must not be empty");
176  }
177 
178  randle = imap_slab_reserve(t->slab);
179  if (!randle){
181  }
182 
183  FR_CURL_REQUEST_SET_OPTION(CURLOPT_USERNAME, username->vp_strvalue);
184  FR_CURL_REQUEST_SET_OPTION(CURLOPT_PASSWORD, password->vp_strvalue);
185 
186  if (fr_curl_io_request_enqueue(t->mhandle, request, randle)) {
187  error:
188  imap_slab_release(randle);
190  }
191 
192  return unlang_module_yield(request, mod_authenticate_resume, imap_io_module_signal, ~FR_SIGNAL_CANCEL, randle);
193 }
194 
195 /** Clean up CURL handle on freeing
196  *
197  */
199 {
200  curl_easy_cleanup(randle->candle);
201 
202  return 0;
203 }
204 
205 /** Callback to configure a CURL handle when it is allocated
206  *
207  */
208 static int imap_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
209 {
210  rlm_imap_t const *inst = talloc_get_type_abort(uctx, rlm_imap_t);
211 
212  randle->candle = curl_easy_init();
213  if (unlikely(!randle->candle)) {
214  error:
215  fr_strerror_printf("Unable to initialise CURL handle");
216  return -1;
217  }
218 
219  talloc_set_destructor(randle, _mod_conn_free);
220 
221 #if CURL_AT_LEAST_VERSION(7,45,0)
222  FR_CURL_SET_OPTION(CURLOPT_DEFAULT_PROTOCOL, "imap");
223 #endif
224  FR_CURL_SET_OPTION(CURLOPT_URL, inst->uri);
225 #if CURL_AT_LEAST_VERSION(7,85,0)
226  FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS_STR, "imap,imaps");
227 #else
228  FR_CURL_SET_OPTION(CURLOPT_PROTOCOLS, CURLPROTO_IMAP | CURLPROTO_IMAPS);
229 #endif
230  FR_CURL_SET_OPTION(CURLOPT_CONNECTTIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
231  FR_CURL_SET_OPTION(CURLOPT_TIMEOUT_MS, fr_time_delta_to_msec(inst->timeout));
232 
233  if (DEBUG_ENABLED3) FR_CURL_SET_OPTION(CURLOPT_VERBOSE, 1L);
234 
235  if (fr_curl_easy_tls_init(randle, &inst->tls) != 0) goto error;
236 
237  return 0;
238 }
239 
240 /*
241  * Initialize a new thread with a curl instance
242  */
244 {
245  rlm_imap_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_imap_t);
246  rlm_imap_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_imap_thread_t);
247  fr_curl_handle_t *mhandle;
248 
249  if (!(t->slab = imap_slab_list_alloc(t, mctx->el, &inst->conn_config.reuse,
250  imap_conn_alloc, NULL, inst,
251  false, false))) {
252  ERROR("Connection handle pool instantiation failed");
253  return -1;
254  }
255 
256  mhandle = fr_curl_io_init(t, mctx->el, false);
257  if (!mhandle) return -1;
258 
259  t->mhandle = mhandle;
260  return 0;
261 }
262 
263 /*
264  * Close the thread and free the memory
265  */
267 {
268  rlm_imap_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_imap_thread_t);
269 
270  talloc_free(t->mhandle);
271  talloc_free(t->slab);
272  return 0;
273 }
274 
275 /*
276  * The module name should be the only globally exported symbol.
277  * That is, everything else should be 'static'.
278  *
279  * If the module needs to temporarily modify it's instantiation
280  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
281  * The server will then take care of ensuring that the module
282  * is single-threaded.
283  */
284 extern module_rlm_t rlm_imap;
286  .common = {
287  .magic = MODULE_MAGIC_INIT,
288  .name = "imap",
289  .inst_size = sizeof(rlm_imap_t),
290  .thread_inst_size = sizeof(rlm_imap_thread_t),
291  .config = module_config,
292  .thread_instantiate = mod_thread_instantiate,
293  .thread_detach = mod_thread_detach,
294  },
295  .method_group = {
296  .bindings = (module_method_binding_t[]){
297  { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate },
299  }
300  }
301 };
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:481
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#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:564
#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
int fr_curl_io_request_enqueue(fr_curl_handle_t *mhandle, request_t *request, fr_curl_io_request_t *creq)
Sends a request using libcurl.
Definition: io.c:482
CURL * candle
Request specific handle.
Definition: base.h:102
Uctx data for timer and I/O functions.
Definition: base.h:91
Structure representing an individual request being passed to curl for processing.
Definition: base.h:101
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#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:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#define GLOBAL_LIB_TERMINATOR
Definition: global_lib.h:51
Structure to define how to initialise libraries with global configuration.
Definition: global_lib.h:38
int fr_curl_response_certinfo(request_t *request, fr_curl_io_request_t *randle)
Definition: base.c:172
int fr_curl_easy_tls_init(fr_curl_io_request_t *randle, fr_curl_tls_t const *conf)
Definition: base.c:141
global_lib_autoinst_t fr_curl_autoinst
Definition: base.c:400
conf_parser_t fr_curl_conn_config[]
Definition: base.c:99
conf_parser_t fr_curl_tls_config[]
Definition: base.c:68
#define RERROR(fmt,...)
Definition: log.h:298
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition: log.h:259
talloc_free(reap)
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
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
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:63
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:693
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define RETURN_MODULE_REJECT
Definition: rcode.h:55
#define RETURN_MODULE_INVALID
Definition: rcode.h:59
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
static fr_dict_attr_t const * attr_user_password
Definition: rlm_imap.c:35
imap_slab_list_t * slab
Slab list for connection handles.
Definition: rlm_imap.c:68
fr_time_delta_t timeout
Timeout for connection and server response.
Definition: rlm_imap.c:59
fr_curl_tls_t tls
Definition: rlm_imap.c:60
static fr_dict_t const * dict_radius
Definition: rlm_imap.c:33
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_imap.c:152
static int imap_conn_alloc(fr_curl_io_request_t *randle, void *uctx)
Callback to configure a CURL handle when it is allocated.
Definition: rlm_imap.c:208
static void imap_io_module_signal(module_ctx_t const *mctx, request_t *request, UNUSED fr_signal_t action)
Definition: rlm_imap.c:83
char const * uri
URI of imap server.
Definition: rlm_imap.c:58
fr_curl_conn_config_t conn_config
Reusable CURL handle config.
Definition: rlm_imap.c:61
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Definition: rlm_imap.c:243
global_lib_autoinst_t const *const rlm_imap_lib[]
Definition: rlm_imap.c:52
static unlang_action_t mod_authenticate_resume(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_imap.c:105
static fr_dict_attr_t const * attr_user_name
Definition: rlm_imap.c:36
fr_dict_autoload_t rlm_imap_dict[]
Definition: rlm_imap.c:39
fr_dict_attr_autoload_t rlm_imap_dict_attr[]
Definition: rlm_imap.c:45
module_rlm_t rlm_imap
Definition: rlm_imap.c:285
static const conf_parser_t module_config[]
Definition: rlm_imap.c:75
static int _mod_conn_free(fr_curl_io_request_t *randle)
Clean up CURL handle on freeing.
Definition: rlm_imap.c:198
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Definition: rlm_imap.c:266
fr_curl_handle_t * mhandle
Thread specific multi handle. Serves as the dispatch and coralling structure for imap requests.
Definition: rlm_imap.c:69
username
Definition: rlm_securid.c:420
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition: section.h:40
void * data
Module's instance data.
Definition: module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition: module.h:151
Named methods exported by a module.
Definition: module.h:173
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
unlang_action_t unlang_module_yield(request_t *request, module_method_t resume, unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
Yield a request back to the interpreter from within a module.
Definition: module.c:419
RETURN_MODULE_FAIL
eap_aka_sim_process_conf_t * inst
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
#define talloc_get_type_abort_const
Definition: talloc.h:282
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
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
int nonnull(2, 5))