The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
edir.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: 74b4b1084da786be5a4e001705e594eb9bfe6f5f $
19  * @file lib/ldap/edir.c
20  * @brief LDAP extension for reading eDirectory universal password.
21  *
22  * To contact Novell about this file by physical or electronic mail, you may
23  * find current contact information at www.novell.com.
24  *
25  * @copyright 2012 Olivier Beytrison (olivier@heliosnet.org)
26  * @copyright 2012 Alan DeKok (aland@freeradius.org)
27  * @copyright 2002-2004 Novell, Inc.
28  */
29 RCSID("$Id: 74b4b1084da786be5a4e001705e594eb9bfe6f5f $")
30 
32 
33 #include <freeradius-devel/util/debug.h>
34 #include <freeradius-devel/ldap/base.h>
35 
36 /* NMAS error codes */
37 #define NMAS_E_BASE (-1600)
38 
39 #define NMAS_E_FRAG_FAILURE (NMAS_E_BASE-31) /* -1631 0xFFFFF9A1 */
40 #define NMAS_E_SYSTEM_RESOURCES (NMAS_E_BASE-34) /* -1634 0xFFFFF99E */
41 #define NMAS_E_INSUFFICIENT_MEMORY (NMAS_E_BASE-35) /* -1635 0xFFFFF99D */
42 #define NMAS_E_NOT_SUPPORTED (NMAS_E_BASE-36) /* -1636 0xFFFFF99C */
43 #define NMAS_E_INVALID_PARAMETER (NMAS_E_BASE-43) /* -1643 0xFFFFF995 */
44 #define NMAS_E_INVALID_VERSION (NMAS_E_BASE-52) /* -1652 0xFFFFF98C */
45 #define NMAS_E_ACCESS_NOT_ALLOWED (NMAS_E_BASE-59) /* -1659 0xFFFFF985 */
46 #define NMAS_E_INVALID_SPM_REQUEST (NMAS_E_BASE-97) /* -1697 0xFFFFF95F */
47 
48 /* OID of LDAP extension calls to read Universal Password */
49 #define NMASLDAP_GET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.13"
50 #define NMASLDAP_GET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.14"
51 
52 #define NMAS_LDAP_EXT_VERSION 1
53 
54 typedef struct {
57  char const *reqoid;
58  struct berval *dn;
61 
62 /** Takes the object DN and BER encodes the data into the BER value which is used as part of the request
63  *
64  @verbatim
65  RequestBer contents:
66  clientVersion INTEGER
67  targetObjectDN OCTET STRING
68  @endverbatim
69  *
70  * @param[out] request_bv where to write the request BER value (must be freed with ber_bvfree).
71  * @param[in] dn to query for.
72  * @return
73  * - 0 on success.
74  * - < 0 on error.
75  */
76 static int ber_encode_request_data(char const *dn, struct berval **request_bv)
77 {
78  int err = 0;
79  int rc = 0;
80  BerElement *request_ber = NULL;
81 
82  if (!dn || !*dn) {
84  goto finish;
85  }
86 
87  /* Allocate a BerElement for the request parameters.*/
88  if ((request_ber = ber_alloc()) == NULL) {
90  goto finish;
91  }
92 
93  rc = ber_printf(request_ber, "{io}", NMAS_LDAP_EXT_VERSION, dn, strlen(dn) + 1);
94  if (rc < 0) {
96  goto finish;
97  }
98 
99  /*
100  * Convert the BER we just built to a berval that we'll
101  * send with the extended request.
102  */
103  if (ber_flatten(request_ber, request_bv) < 0) {
105  goto finish;
106  }
107 
108 finish:
109  if (request_ber) ber_free(request_ber, 1);
110 
111  return err;
112 }
113 
114 /** Converts the reply into server version and a return code
115  *
116  * This function takes the reply BER Value and decodes the NMAS server version and return code and if a non
117  * null retData buffer was supplied, tries to decode the the return data and length.
118  *
119  @verbatim
120  ResponseBer contents:
121  server_version INTEGER
122  error INTEGER
123  data OCTET STRING
124  @endverbatim
125  *
126  * @param[in] reply_bv reply data from extended request.
127  * @param[out] server_version that responded.
128  * @param[out] out data.
129  * @param[out] outlen Length of data written to out.
130  * @return
131  * - 0 on success.
132  * - < 0 on error.
133  */
134 static int ber_decode_login_data(struct berval *reply_bv, int *server_version, void *out, size_t *outlen)
135 {
136  int rc = 0;
137  int err = 0;
138  BerElement *reply_ber = NULL;
139 
140  fr_assert(out != NULL);
141  fr_assert(outlen != NULL);
142 
143  if ((reply_ber = ber_init(reply_bv)) == NULL) {
145  goto finish;
146  }
147 
148  rc = ber_scanf(reply_ber, "{iis}", server_version, &err, out, outlen);
149  if (rc == -1) {
151  goto finish;
152  }
153 
154 finish:
155  if (reply_ber) ber_free(reply_ber, 1);
156 
157  return err;
158 }
159 
160 /** Submit LDAP extended operation to retrieve Universal Password
161  *
162  * @param p_result Result of current operation.
163  * @param priority Unused.
164  * @param request Current request.
165  * @param uctx eDir lookup context.
166  * @return One of the RLM_MODULE_* values.
167  */
169  void *uctx)
170 {
171  ldap_edir_ctx_t *edir_ctx = talloc_get_type_abort(uctx, ldap_edir_ctx_t);
172  return fr_ldap_trunk_extended(edir_ctx, &edir_ctx->query, request, edir_ctx->ttrunk,
173  edir_ctx->reqoid, edir_ctx->dn, NULL, NULL);
174 }
175 
176 /** Handle results of retrieving Universal Password
177  *
178  * @param p_result Result of current operation.
179  * @param priority Unused.
180  * @param request Current request.
181  * @param uctx eDir lookup context.
182  * @return One of the RLM_MODULE_* values.
183  */
185  void *uctx)
186 {
187  ldap_edir_ctx_t *edir_ctx = talloc_get_type_abort(uctx, ldap_edir_ctx_t);
188  fr_ldap_query_t *query = edir_ctx->query;
189  rlm_rcode_t rcode = RLM_MODULE_OK;
190  fr_pair_t *vp;
191  char *reply_oid = NULL;
192  struct berval *reply_bv = NULL;
193  size_t bufsize;
194  char buffer[256];
195  int err = 0;
196  int server_version;
197 
198  switch (query->ret){
199  case LDAP_SUCCESS:
200  break;
201 
202  default:
203  REDEBUG("Failed retrieving Universal Password");
204  rcode = RLM_MODULE_FAIL;
205  goto finish;
206  }
207 
208  err = ldap_parse_extended_result(query->ldap_conn->handle, query->result, &reply_oid, &reply_bv, false);
209 
210  switch (err) {
211  case LDAP_SUCCESS:
212  break;
213  }
214 
215  /* Make sure there is a return OID */
216  if (!reply_oid) {
218  goto finish;
219  }
220 
221  /* Is this what we were expecting to get back. */
222  if (strcmp(reply_oid, NMASLDAP_GET_PASSWORD_RESPONSE) != 0) {
224  goto finish;
225  }
226 
227  /* Do we have a good returned berval? */
228  if (!reply_bv) {
229  /*
230  * No; returned berval means we experienced a rather
231  * drastic error. Return operations error.
232  */
234  goto finish;
235  }
236 
237  bufsize = sizeof(buffer);
238  err = ber_decode_login_data(reply_bv, &server_version, buffer, &bufsize);
239  if (err) goto finish;
240 
241  if (server_version != NMAS_LDAP_EXT_VERSION) {
243  goto finish;
244  }
245 
246  /*
247  * Add Password.Cleartext attribute to the request
248  */
249  MEM(pair_update_control(&vp, edir_ctx->password_da) >= 0);
250  fr_pair_value_bstrndup(vp, buffer, bufsize, true);
251 
252  if (RDEBUG_ENABLED3) {
253  RDEBUG3("Added eDirectory password. control.%pP", vp);
254  } else {
255  RDEBUG2("Added eDirectory password");
256  }
257 
258 finish:
259  /*
260  * Free any libldap allocated resources.
261  */
262  if (reply_bv) ber_bvfree(reply_bv);
263  if (reply_oid) ldap_memfree(reply_oid);
264 
265  if (err) {
266  REDEBUG("Failed to retrieve eDirectory password: (%i) %s", err, fr_ldap_edir_errstr(err));
267  rcode = RLM_MODULE_FAIL;
268  }
269 
270  RETURN_MODULE_RCODE(rcode);
271 }
272 
273 /** Cancel an in progress Universal Password lookup
274  *
275  */
276 static void ldap_edir_get_password_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
277 {
278  ldap_edir_ctx_t *edir_ctx = talloc_get_type_abort(uctx, ldap_edir_ctx_t);
279 
280  if (!edir_ctx->query || !edir_ctx->query->treq) return;
281 
283 }
284 
285 /** Initiate retrieval of the universal password from Novell eDirectory
286  *
287  * @param[in] request Current request.
288  * @param[in] dn of the user whose password is to be retrieved.
289  * @param[in] ttrunk on which to send the LDAP request.
290  * @param[in] password_da DA to use when creating password attribute.
291  * @return
292  * - UNLANG_ACTION_PUSHED_CHILD on success.
293  * - UNLANG_ACTION_FAIL on failure.
294  */
296  fr_dict_attr_t const *password_da)
297 {
298  ldap_edir_ctx_t *edir_ctx;
299  int err = 0;
300 
301  if (!dn || !*dn) {
302  REDEBUG("Missing DN");
303  return UNLANG_ACTION_FAIL;
304  }
305 
306  MEM(edir_ctx = talloc(unlang_interpret_frame_talloc_ctx(request), ldap_edir_ctx_t));
307 
308  *edir_ctx = (ldap_edir_ctx_t) {
310  .ttrunk = ttrunk,
311  .password_da = password_da
312  };
313 
314  err = ber_encode_request_data(dn, &edir_ctx->dn);
315  if (err) {
316  REDEBUG("Failed to encode user DN: %s", fr_ldap_edir_errstr(err));
317  talloc_free(edir_ctx);
318  return UNLANG_ACTION_FAIL;
319  }
320 
322  ldap_edir_get_password_cancel, ~FR_SIGNAL_CANCEL,
323  UNLANG_SUB_FRAME, edir_ctx);
324 }
325 
326 char const *fr_ldap_edir_errstr(int code)
327 {
328  switch (code) {
329  case NMAS_E_FRAG_FAILURE:
330  return "BER manipulation failed";
331 
334  return "Insufficient memory or system resources";
335 
337  return "Server response indicated Universal Password is not supported (missing password response OID)";
338 
340  return "Bad arguments passed to eDir functions";
341 
343  return "LDAP EXT version does not match expected version" STRINGIFY(NMAS_LDAP_EXT_VERSION);
344 
346  return "Bound user does not have sufficient rights to read the Universal Password of users";
347 
349  return "Universal password is not enabled for the container of this user object";
350 
351  default:
352  return ldap_err2string(code);
353  }
354 }
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition: action.h:36
static int const char char buffer[256]
Definition: acutest.h:574
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define STRINGIFY(x)
Definition: build.h:195
#define UNUSED
Definition: build.h:313
static fr_slen_t err
Definition: dict.h:645
#define NMAS_E_INVALID_VERSION
Definition: edir.c:44
static int ber_encode_request_data(char const *dn, struct berval **request_bv)
Takes the object DN and BER encodes the data into the BER value which is used as part of the request.
Definition: edir.c:76
static unlang_action_t ldap_edir_get_password_start(UNUSED rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Submit LDAP extended operation to retrieve Universal Password.
Definition: edir.c:168
#define NMAS_E_SYSTEM_RESOURCES
Definition: edir.c:40
#define NMAS_E_FRAG_FAILURE
Definition: edir.c:39
#define NMAS_E_ACCESS_NOT_ALLOWED
Definition: edir.c:45
static int ber_decode_login_data(struct berval *reply_bv, int *server_version, void *out, size_t *outlen)
Converts the reply into server version and a return code.
Definition: edir.c:134
fr_dict_attr_t const * password_da
Definition: edir.c:59
static void ldap_edir_get_password_cancel(UNUSED request_t *request, UNUSED fr_signal_t action, void *uctx)
Cancel an in progress Universal Password lookup.
Definition: edir.c:276
static unlang_action_t ldap_edir_get_password_resume(rlm_rcode_t *p_result, UNUSED int *priority, request_t *request, void *uctx)
Handle results of retrieving Universal Password.
Definition: edir.c:184
#define NMASLDAP_GET_PASSWORD_REQUEST
Definition: edir.c:49
fr_ldap_thread_trunk_t * ttrunk
Definition: edir.c:56
#define NMASLDAP_GET_PASSWORD_RESPONSE
Definition: edir.c:50
char const * fr_ldap_edir_errstr(int code)
Definition: edir.c:326
#define NMAS_E_INVALID_PARAMETER
Definition: edir.c:43
char const * reqoid
Definition: edir.c:57
#define NMAS_LDAP_EXT_VERSION
Definition: edir.c:52
#define NMAS_E_INSUFFICIENT_MEMORY
Definition: edir.c:41
struct berval * dn
Definition: edir.c:58
unlang_action_t fr_ldap_edir_get_password(request_t *request, char const *dn, fr_ldap_thread_trunk_t *ttrunk, fr_dict_attr_t const *password_da)
Initiate retrieval of the universal password from Novell eDirectory.
Definition: edir.c:295
#define NMAS_E_NOT_SUPPORTED
Definition: edir.c:42
#define NMAS_E_INVALID_SPM_REQUEST
Definition: edir.c:46
fr_ldap_query_t * query
Definition: edir.c:55
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition: interpret.c:1384
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
fr_trunk_request_t * treq
Trunk request this query is associated with.
Definition: base.h:451
LDAP * handle
libldap handle.
Definition: base.h:331
fr_ldap_result_code_t ret
Result code.
Definition: base.h:465
fr_ldap_connection_t * ldap_conn
LDAP connection this query is running on.
Definition: base.h:452
LDAPMessage * result
Head of LDAP results list.
Definition: base.h:463
LDAP query structure.
Definition: base.h:420
Thread LDAP trunk structure.
Definition: base.h:397
unlang_action_t fr_ldap_trunk_extended(TALLOC_CTX *ctx, fr_ldap_query_t **out, request_t *request, fr_ldap_thread_trunk_t *ttrunk, char const *reqoid, struct berval *reqdata, LDAPControl **serverctrls, LDAPControl **clientctrls)
Run an async LDAP "extended operation" query on a trunk connection.
Definition: base.c:827
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RDEBUG3(fmt,...)
Definition: log.h:343
talloc_free(reap)
int fr_pair_value_bstrndup(fr_pair_t *vp, char const *src, size_t len, bool tainted)
Copy data into a "string" type value pair.
Definition: pair.c:2781
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define RETURN_MODULE_RCODE(_rcode)
Definition: rcode.h:64
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_OK
The module is OK, continue.
Definition: rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
#define pair_update_control(_attr, _da)
Return or allocate a fr_pair_t in the control list.
Definition: pair.h:140
fr_signal_t
Definition: signal.h:48
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
void fr_trunk_request_signal_cancel(fr_trunk_request_t *treq)
Cancel a trunk request.
Definition: trunk.c:2047
static size_t char ** out
Definition: value.h:984