summaryrefslogtreecommitdiffstats
path: root/src/modules/rlm_ldap/edir.c
blob: ddac7e226a1bac6dc449d84b4dab9f24425255dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
/*
 *   This program is is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or (at
 *   your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

/**
 * $Id$
 * @file edir.c
 * @brief LDAP extension for reading eDirectory universal password.
 *
 * To contact Novell about this file by physical or electronic mail, you may
 * find current contact  information at www.novell.com.
 *
 * @copyright 2012 Olivier Beytrison <olivier@heliosnet.org>
 * @copyright 2012 Alan DeKok <aland@freeradius.org>
 * @copyright 2002-2004 Novell, Inc.
 */

RCSID("$Id$")

#include	<freeradius-devel/radiusd.h>
#include	<freeradius-devel/rad_assert.h>

#include "ldap.h"

/* NMAS error codes */
#define NMAS_E_BASE	(-1600)

#define NMAS_E_FRAG_FAILURE		(NMAS_E_BASE-31)	/* -1631 0xFFFFF9A1 */
#define NMAS_E_BUFFER_OVERFLOW		(NMAS_E_BASE-33)	/* -1633 0xFFFFF99F */
#define NMAS_E_SYSTEM_RESOURCES		(NMAS_E_BASE-34)	/* -1634 0xFFFFF99E */
#define NMAS_E_INSUFFICIENT_MEMORY	(NMAS_E_BASE-35)	/* -1635 0xFFFFF99D */
#define NMAS_E_NOT_SUPPORTED		(NMAS_E_BASE-36)	/* -1636 0xFFFFF99C */
#define NMAS_E_INVALID_PARAMETER	(NMAS_E_BASE-43)	/* -1643 0xFFFFF995 */
#define NMAS_E_INVALID_VERSION		(NMAS_E_BASE-52)	/* -1652 0xFFFFF98C */
#define NMAS_E_ACCESS_NOT_ALLOWED	(NMAS_E_BASE-59)	/* -1659 0xFFFFF985 */
#define NMAS_E_INVALID_SPM_REQUEST	(NMAS_E_BASE-97)	/* -1697 0xFFFFF95F */

/* OID of LDAP extenstion calls to read Universal Password */
#define NMASLDAP_GET_PASSWORD_REQUEST     "2.16.840.1.113719.1.39.42.100.13"
#define NMASLDAP_GET_PASSWORD_RESPONSE    "2.16.840.1.113719.1.39.42.100.14"

#define NMAS_LDAP_EXT_VERSION 1

/** Takes the object DN and BER encodes the data into the BER value which is used as part of the request
 *
 @verbatim
	RequestBer contents:
		clientVersion		INTEGER
		targetObjectDN		OCTET STRING
 @endverbatim
 *
 * @param[out] request_bv where to write the request BER value (must be freed with ber_bvfree).
 * @param[in] dn to query for.
 * @return
 *	- 0 on success.
 *	- < 0 on error.
 */
static int ber_encode_request_data(char const *dn, struct berval **request_bv)
{
	int err = 0;
	int rc = 0;
	BerElement *request_ber = NULL;

	if (!dn || !*dn) {
		err = NMAS_E_INVALID_PARAMETER;
		goto finish;
	}

	/* Allocate a BerElement for the request parameters.*/
	if ((request_ber = ber_alloc()) == NULL) {
		err = NMAS_E_FRAG_FAILURE;
		goto finish;
	}

	rc = ber_printf(request_ber, "{io}", NMAS_LDAP_EXT_VERSION, dn, strlen(dn) + 1);
	if (rc < 0) {
		err = NMAS_E_FRAG_FAILURE;
		goto finish;
	}

	/*
	 *	Convert the BER we just built to a berval that we'll
	 *	send with the extended request.
	 */
	if (ber_flatten(request_ber, request_bv) < 0) {
		err = NMAS_E_FRAG_FAILURE;
		goto finish;
	}

finish:
	if (request_ber) ber_free(request_ber, 1);

	return err;
}

/** Converts the reply into server version and a return code
 *
 * This function takes the reply BER Value and decodes the NMAS server version and return code and if a non
 * null retData buffer was supplied, tries to decode the the return data and length.
 *
 @verbatim
	ResponseBer contents:
		server_version		INTEGER
		error       		INTEGER
		data			OCTET STRING
 @endverbatim
 *
 * @param[in] reply_bv reply data from extended request.
 * @param[out] server_version that responded.
 * @param[out] out data.
 * @param[out] outlen Length of data written to out.
 * @return
 *	- 0 on success.
 *	- < 0 on error.
 */
static int ber_decode_login_data(struct berval *reply_bv, int *server_version, void *out, size_t *outlen)
{
	int rc = 0;
	int err = 0;
	BerElement *reply_ber = NULL;

	rad_assert(out != NULL);
	rad_assert(outlen != NULL);

	if ((reply_ber = ber_init(reply_bv)) == NULL) {
		err = NMAS_E_SYSTEM_RESOURCES;
		goto finish;
	}

	rc = ber_scanf(reply_ber, "{iis}", server_version, &err, out, outlen);
	if (rc == -1) {
		err = NMAS_E_FRAG_FAILURE;
		goto finish;
	}

finish:

	if (reply_ber) ber_free(reply_ber, 1);

	return err;
}

/** Attempt to retrieve the universal password from Novell eDirectory
 *
 * @param[in] ld LDAP handle.
 * @param[in] dn of user we want to retrieve the password for.
 * @param[out] password Where to write the retrieved password.
 * @param[out] passlen Length of data written to the password buffer.
 * @return
 *	- 0 on success.
 *	- < 0 on failure.
 */
int nmasldap_get_password(LDAP *ld, char const *dn, char *password, size_t *passlen)
{
	int err = 0;
	struct berval *request_bv = NULL;
	char *reply_oid = NULL;
	struct berval *reply_bv = NULL;
	int server_version;
	size_t bufsize;
	char buffer[256];

	/* Validate  parameters. */
	if (!dn || !*dn || !passlen || !ld) {
		return NMAS_E_INVALID_PARAMETER;
	}

	err = ber_encode_request_data(dn, &request_bv);
	if (err) goto finish;

	/* Call the ldap_extended_operation (synchronously) */
	err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, request_bv, NULL, NULL, &reply_oid, &reply_bv);
	if (err) goto finish;

	/* Make sure there is a return OID */
	if (!reply_oid) {
		err = NMAS_E_NOT_SUPPORTED;
		goto finish;
	}

	/* Is this what we were expecting to get back. */
	if (strcmp(reply_oid, NMASLDAP_GET_PASSWORD_RESPONSE) != 0) {
		err = NMAS_E_NOT_SUPPORTED;
		goto finish;
	}

	/* Do we have a good returned berval? */
	if (!reply_bv) {
		/*
		 *	No; returned berval means we experienced a rather
		 *	drastic error.  Return operations error.
		 */
		err = NMAS_E_SYSTEM_RESOURCES;
		goto finish;
	}

	bufsize = sizeof(buffer);
	err = ber_decode_login_data(reply_bv, &server_version, buffer, &bufsize);
	if (err) goto finish;

	if (server_version != NMAS_LDAP_EXT_VERSION) {
		err = NMAS_E_INVALID_VERSION;
		goto finish;
	}

	if (bufsize > *passlen) {
		err = NMAS_E_BUFFER_OVERFLOW;
		goto finish;
	}

	memcpy(password, buffer, bufsize);
	password[bufsize] = '\0';
	*passlen = bufsize;

finish:
	if (reply_bv) {
		ber_bvfree(reply_bv);
	}

	/* Free the return OID string if one was returned. */
	if (reply_oid) {
		ldap_memfree(reply_oid);
	}

	/* Free memory allocated while building the request ber and berval. */
	if (request_bv) {
		ber_bvfree(request_bv);
	}

	return err;
}

char const *edir_errstr(int code) {
	switch (code) {
	case NMAS_E_FRAG_FAILURE:
		return "BER manipulation failed";

	case NMAS_E_BUFFER_OVERFLOW:
		return "Insufficient buffer space to write retrieved password";

	case NMAS_E_SYSTEM_RESOURCES:
	case NMAS_E_INSUFFICIENT_MEMORY:
		return "Insufficient memory or system resources";

	case NMAS_E_NOT_SUPPORTED:
		return "Server response indicated Universal Password is not supported (missing password response OID)";

	case NMAS_E_INVALID_PARAMETER:
		return "Bad arguments passed to eDir functions";

	case NMAS_E_INVALID_VERSION:
		return "LDAP EXT version does not match expected version" STRINGIFY(NMAS_LDAP_EXT_VERSION);

	case NMAS_E_ACCESS_NOT_ALLOWED:
		return "Bound user does not have sufficient rights to read the Universal Password of users";

	case NMAS_E_INVALID_SPM_REQUEST:
		return "Universal password is not enabled for the container of this user object";

	default:
		return ldap_err2string(code);
	}
}