All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
sasl.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 #include "ldap.h"
18 
19 /**
20  * $Id: 04af92c82870f791347122dd74376eb6b6493ec8 $
21  * @file sasl.c
22  * @brief Functions to perform SASL binds against an LDAP directory.
23  *
24  * @author Arran Cudbard-Bell <a.cudbardb@freeradius.org>
25  * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
26  * @copyright 2015 The FreeRADIUS Server Project.
27  */
28 #include <freeradius-devel/radiusd.h>
29 #include <freeradius-devel/rad_assert.h>
30 
31 #include <sasl/sasl.h>
32 
33 /** Data passed to the _sasl interact callback.
34  *
35  */
36 typedef struct rlm_ldap_sasl_ctx {
37  rlm_ldap_t const *inst; //!< LDAP instance
38  REQUEST *request; //!< The current request.
39 
40  char const *identity; //!< User's DN or identity.
41  char const *password; //!< Bind password.
42 
43  ldap_sasl *extra; //!< Extra fields (realm and proxy id).
45 
46 /** Callback for ldap_sasl_interactive_bind
47  *
48  * @param handle used for the SASL bind.
49  * @param flags data as provided to ldap_sasl_interactive_bind.
50  * @param ctx Our context data, containing the identity, password, realm and various other things.
51  * @param sasl_callbacks Array of challenges to provide responses for.
52  * @return SASL_OK.
53  */
54 static int _sasl_interact(UNUSED LDAP *handle, UNUSED unsigned flags, void *ctx, void *sasl_callbacks)
55 {
56  rlm_ldap_sasl_ctx_t *this = ctx;
57  REQUEST *request = this->request;
58  rlm_ldap_t const *inst = this->inst;
59  sasl_interact_t *cb = sasl_callbacks;
60  sasl_interact_t *cb_p;
61 
62  for (cb_p = cb; cb_p->id != SASL_CB_LIST_END; cb_p++) {
63  MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL challenge : %s", cb_p->challenge);
64  MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL prompt : %s", cb_p->prompt);
65 
66  switch (cb_p->id) {
67  case SASL_CB_AUTHNAME:
68  cb_p->result = this->identity;
69  break;
70 
71  case SASL_CB_PASS:
72  cb_p->result = this->password;
73  break;
74 
75  case SASL_CB_USER:
76  cb_p->result = this->extra->proxy ? this->extra->proxy : this->identity;
77  break;
78 
79  case SASL_CB_GETREALM:
80  if (this->extra->realm) cb_p->result = this->extra->realm;
81  break;
82 
83  default:
84  break;
85  }
86  MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL result : %s", cb_p->result ? (char const *)cb_p->result : "");
87  }
88  return SASL_OK;
89 }
90 
91 /** Initiate an LDAP interactive bind
92  *
93  * @param[in] inst rlm_ldap configuration.
94  * @param[in] request Current request, this may be NULL, in which case all debug logging is done with radlog.
95  * @param[in] conn to use. May change as this function calls functions which auto re-connect.
96  * @param[in] identity of the user.
97  * @param[in] password of the user.
98  * @param[in] sasl mechanism to use for bind, and additional parameters.
99  * @param[in] serverctrls Search controls to pass to the server. May be NULL.
100  * @param[in] clientctrls Search controls for ldap_sasl_interactive. May be NULL.
101  * @param[out] error message resulting from bind.
102  * @param[out] extra information about the error.
103  * @return One of the LDAP_PROC_* (#ldap_rcode_t) values.
104  */
106  ldap_handle_t *conn, char const *identity,
107  char const *password, ldap_sasl *sasl,
108  LDAPControl **serverctrls, LDAPControl **clientctrls,
109  char const **error, char **extra)
110 {
111  ldap_rcode_t status;
112  int ret = 0;
113  int msgid;
114  char const *mech;
115  LDAPMessage *result = NULL;
116  rlm_ldap_sasl_ctx_t sasl_ctx; /* SASL defaults */
117 
118  LDAPControl *our_serverctrls[LDAP_MAX_CONTROLS];
119  LDAPControl *our_clientctrls[LDAP_MAX_CONTROLS];
120 
121  rlm_ldap_control_merge(our_serverctrls, our_clientctrls,
122  sizeof(our_serverctrls) / sizeof(*our_serverctrls),
123  sizeof(our_clientctrls) / sizeof(*our_clientctrls),
124  conn, serverctrls, clientctrls);
125 
126  /* rlm_ldap_result may not be called */
127  if (error) *error = NULL;
128  if (extra) *extra = NULL;
129 
130  sasl_ctx.inst = inst;
131  sasl_ctx.request = request;
132  sasl_ctx.identity = identity;
133  sasl_ctx.password = password;
134  sasl_ctx.extra = sasl;
135 
136  MOD_ROPTIONAL(RDEBUG2, DEBUG2, "Starting SASL mech(s): %s", sasl->mech);
137  for (;;) {
138  ret = ldap_sasl_interactive_bind(conn->handle, NULL, sasl->mech,
139  our_serverctrls, our_clientctrls,
140  LDAP_SASL_AUTOMATIC,
141  _sasl_interact, &sasl_ctx, result,
142  &mech, &msgid);
143 
144  /*
145  * If ldap_sasl_interactive_bind indicates it didn't want
146  * to continue, then we're done.
147  *
148  * Calling ldap_result here, results in a timeout in some
149  * cases, so we need to figure out whether the bind was
150  * successful without the help of ldap_result.
151  */
152  if (ret != LDAP_SASL_BIND_IN_PROGRESS) {
153  status = rlm_ldap_result(inst, conn, -1, identity, NULL, error, extra);
154  break; /* Old result gets freed on after exit */
155  }
156 
157  ldap_msgfree(result); /* We always need to free the old message */
158 
159  /*
160  * If LDAP parse result indicates there was an error
161  * then we're done.
162  */
163  status = rlm_ldap_result(inst, conn, msgid, identity, &result, error, extra);
164  switch (status) {
165  case LDAP_PROC_SUCCESS: /* ldap_sasl_interactive_bind should have indicated success */
166  case LDAP_PROC_CONTINUE:
167  break;
168 
169  default:
170  goto done;
171  }
172 
173  /*
174  * ...otherwise, the bind is still in progress.
175  */
176  MOD_ROPTIONAL(RDEBUG3, DEBUG3, "Continuing SASL mech %s...", mech);
177 
178  /*
179  * Write the servers response to the debug log
180  */
181  if (((request && RDEBUG_ENABLED3) || DEBUG_ENABLED3) && result) {
182  struct berval *srv_cred;
183 
184  if (ldap_parse_sasl_bind_result(conn->handle, result, &srv_cred, 0) == 0) {
185  char *escaped;
186 
187  escaped = fr_asprint(request, srv_cred->bv_val, srv_cred->bv_len, '\0');
188  MOD_ROPTIONAL(RDEBUG3, DEBUG3, "SASL response : %s", escaped);
189 
190  talloc_free(escaped);
191  ldap_memfree(srv_cred);
192  }
193  }
194  }
195 done:
196  ldap_msgfree(result);
197 
198  return status;
199 }
Tracks the state of a libldap connection handle.
Definition: ldap.h:163
static int _sasl_interact(UNUSED LDAP *handle, UNUSED unsigned flags, void *ctx, void *sasl_callbacks)
Callback for ldap_sasl_interactive_bind.
Definition: sasl.c:54
Operation was successfull.
Definition: ldap.h:397
#define DEBUG3(fmt,...)
Definition: log.h:177
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:239
#define DEBUG_ENABLED3
True if global debug level 1-3 messages are enabled.
Definition: log.h:171
void rlm_ldap_control_merge(LDAPControl *serverctrls_out[], LDAPControl *clientctrls_out[], size_t serverctrls_len, size_t clientctrls_len, ldap_handle_t *conn, LDAPControl *serverctrls_in[], LDAPControl *clientctrls_in[])
Merge connection and call specific client and server controls.
Definition: control.c:41
#define UNUSED
Definition: libradius.h:134
ldap_rcode_t rlm_ldap_sasl_interactive(rlm_ldap_t const *inst, REQUEST *request, ldap_handle_t *conn, char const *identity, char const *password, ldap_sasl *sasl, LDAPControl **serverctrls, LDAPControl **clientctrls, char const **error, char **extra)
Initiate an LDAP interactive bind.
Definition: sasl.c:105
REQUEST * request
The current request.
Definition: sasl.c:38
rlm_ldap_t const * inst
LDAP instance.
Definition: sasl.c:37
#define inst
ldap_rcode_t
Codes returned by rlm_ldap internal functions.
Definition: ldap.h:395
#define MOD_ROPTIONAL(_l_request, _l_global, fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:338
#define DEBUG2(fmt,...)
Definition: log.h:176
static bool done
Definition: radclient.c:53
Operation is in progress.
Definition: ldap.h:396
ldap_rcode_t rlm_ldap_result(rlm_ldap_t const *inst, ldap_handle_t const *conn, int msgid, char const *dn, LDAPMessage **result, char const **error, char **extra)
Parse response from LDAP server dealing with any errors.
Definition: ldap.c:517
char identity[]
Definition: eap_pwd.h:630
#define RDEBUG2(fmt,...)
Definition: log.h:244
struct rlm_ldap_sasl_ctx rlm_ldap_sasl_ctx_t
Data passed to the _sasl interact callback.
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
Definition: print.c:390
ldap_sasl * extra
Extra fields (realm and proxy id).
Definition: sasl.c:43
char const * mech
SASL mech(s) to try.
Definition: ldap.h:143
Data passed to the _sasl interact callback.
Definition: sasl.c:36
char const * password
Bind password.
Definition: sasl.c:41
#define LDAP_MAX_CONTROLS
Maximum number of client/server controls.
Definition: ldap.h:105
char const * identity
User's DN or identity.
Definition: sasl.c:40
LDAP authorization and authentication module headers.
LDAP * handle
libldap handle.
Definition: ldap.h:164
#define RDEBUG3(fmt,...)
Definition: log.h:245