]> git.ipfire.org Git - thirdparty/squid.git/blob - helpers/digest_auth/eDirectory/edir_ldapext.cc
Boilerplate: update copyright blurbs on Squid helpers
[thirdparty/squid.git] / helpers / digest_auth / eDirectory / edir_ldapext.cc
1 /*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /*
10 * NDS LDAP helper functions
11 * Copied From Samba-3.0.24 pdb_nds.c and trimmed down to the
12 * limited functionality needed to access the plain text password only
13 *
14 * Original copyright & license follows:
15 *
16 * Copyright (C) Vince Brimhall 2004-2005
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
31 */
32
33 #include "squid.h"
34 #include "digest_common.h"
35
36 #if _SQUID_WINDOWS_ && !_SQUID_CYGWIN_
37
38 #define snprintf _snprintf
39 #include <windows.h>
40 #include <winldap.h>
41 #include <winber.h>
42 #ifndef LDAPAPI
43 #define LDAPAPI __cdecl
44 #endif
45 #ifdef LDAP_VERSION3
46 #ifndef LDAP_OPT_X_TLS
47 #define LDAP_OPT_X_TLS 0x6000
48 #endif
49 #define ber_alloc() ber_alloc_t(0)
50 #endif /* LDAP_VERSION3 */
51
52 #else
53
54 #include <lber.h>
55 #include <ldap.h>
56
57 #endif
58 #include <wchar.h>
59
60 #include "edir_ldapext.h"
61
62 #define NMASLDAP_GET_LOGIN_CONFIG_REQUEST "2.16.840.1.113719.1.39.42.100.3"
63 #define NMASLDAP_GET_LOGIN_CONFIG_RESPONSE "2.16.840.1.113719.1.39.42.100.4"
64 #define NMASLDAP_SET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.11"
65 #define NMASLDAP_SET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.12"
66 #define NMASLDAP_GET_PASSWORD_REQUEST "2.16.840.1.113719.1.39.42.100.13"
67 #define NMASLDAP_GET_PASSWORD_RESPONSE "2.16.840.1.113719.1.39.42.100.14"
68
69 #define NMAS_LDAP_EXT_VERSION 1
70
71 #define SMB_MALLOC_ARRAY(type, nelem) calloc(sizeof(type), nelem)
72 #define DEBUG(level, args)
73
74 /**********************************************************************
75 Take the request BER value and input data items and BER encodes the
76 data into the BER value
77 **********************************************************************/
78
79 static int berEncodePasswordData(
80 struct berval **requestBV,
81 const char *objectDN,
82 const char *password,
83 const char *password2)
84 {
85 int err = 0, rc=0;
86 BerElement *requestBer = NULL;
87
88 const char * utf8ObjPtr = NULL;
89 int utf8ObjSize = 0;
90 const char * utf8PwdPtr = NULL;
91 int utf8PwdSize = 0;
92 const char * utf8Pwd2Ptr = NULL;
93 int utf8Pwd2Size = 0;
94
95 /* Convert objectDN and tag strings from Unicode to UTF-8 */
96 utf8ObjSize = strlen(objectDN)+1;
97 utf8ObjPtr = objectDN;
98
99 if (password != NULL) {
100 utf8PwdSize = strlen(password)+1;
101 utf8PwdPtr = password;
102 }
103
104 if (password2 != NULL) {
105 utf8Pwd2Size = strlen(password2)+1;
106 utf8Pwd2Ptr = password2;
107 }
108
109 /* Allocate a BerElement for the request parameters. */
110 if ((requestBer = ber_alloc()) == NULL) {
111 err = LDAP_ENCODING_ERROR;
112 ber_free(requestBer, 1);
113 return err;
114 }
115
116 if (password != NULL && password2 != NULL) {
117 /* BER encode the NMAS Version, the objectDN, and the password */
118 rc = ber_printf(requestBer, "{iooo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize, utf8Pwd2Ptr, utf8Pwd2Size);
119 } else if (password != NULL) {
120 /* BER encode the NMAS Version, the objectDN, and the password */
121 rc = ber_printf(requestBer, "{ioo}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize, utf8PwdPtr, utf8PwdSize);
122 } else {
123 /* BER encode the NMAS Version and the objectDN */
124 rc = ber_printf(requestBer, "{io}", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize);
125 }
126
127 if (rc < 0) {
128 err = LDAP_ENCODING_ERROR;
129 } else {
130 err = 0;
131 /* Convert the BER we just built to a berval that we'll send with the extended request. */
132 if ((ber_tag_t)ber_flatten(requestBer, requestBV) == LBER_ERROR) {
133 err = LDAP_ENCODING_ERROR;
134 }
135 }
136
137 if (requestBer) {
138 ber_free(requestBer, 1);
139 }
140
141 return err;
142 }
143
144 /**********************************************************************
145 Take the request BER value and input data items and BER encodes the
146 data into the BER value
147 **********************************************************************/
148
149 static int berEncodeLoginData(
150 struct berval **requestBV,
151 char *objectDN,
152 unsigned int methodIDLen,
153 unsigned int *methodID,
154 char *tag,
155 size_t putDataLen,
156 void *putData)
157 {
158 int err = 0;
159 BerElement *requestBer = NULL;
160
161 unsigned int i;
162 unsigned int elemCnt = methodIDLen / sizeof(unsigned int);
163
164 char *utf8ObjPtr=NULL;
165 int utf8ObjSize = 0;
166
167 char *utf8TagPtr = NULL;
168 int utf8TagSize = 0;
169
170 utf8ObjPtr = objectDN;
171 utf8ObjSize = strlen(utf8ObjPtr)+1;
172
173 utf8TagPtr = tag;
174 utf8TagSize = strlen(utf8TagPtr)+1;
175
176 /* Allocate a BerElement for the request parameters. */
177 if ((requestBer = ber_alloc()) == NULL) {
178 err = LDAP_ENCODING_ERROR;
179 return err;
180 }
181
182 /* BER encode the NMAS Version and the objectDN */
183 err = (ber_printf(requestBer, "{io", NMAS_LDAP_EXT_VERSION, utf8ObjPtr, utf8ObjSize) < 0) ? LDAP_ENCODING_ERROR : 0;
184
185 /* BER encode the MethodID Length and value */
186 if (!err) {
187 err = (ber_printf(requestBer, "{i{", methodIDLen) < 0) ? LDAP_ENCODING_ERROR : 0;
188 }
189
190 for (i = 0; !err && i < elemCnt; ++i) {
191 err = (ber_printf(requestBer, "i", methodID[i]) < 0) ? LDAP_ENCODING_ERROR : 0;
192 }
193
194 if (!err) {
195 err = (ber_printf(requestBer, "}}", 0) < 0) ? LDAP_ENCODING_ERROR : 0;
196 }
197
198 if (putData) {
199 /* BER Encode the the tag and data */
200 err = (ber_printf(requestBer, "oio}", utf8TagPtr, utf8TagSize, putDataLen, putData, putDataLen) < 0) ? LDAP_ENCODING_ERROR : 0;
201 } else {
202 /* BER Encode the the tag */
203 err = (ber_printf(requestBer, "o}", utf8TagPtr, utf8TagSize) < 0) ? LDAP_ENCODING_ERROR : 0;
204 }
205
206 /* Convert the BER we just built to a berval that we'll send with the extended request. */
207 if (!err && (ber_tag_t)ber_flatten(requestBer, requestBV) == LBER_ERROR) {
208 err = LDAP_ENCODING_ERROR;
209 }
210
211 if (requestBer) {
212 ber_free(requestBer, 1);
213 }
214
215 return err;
216 }
217
218 /**********************************************************************
219 Takes the reply BER Value and decodes the NMAS server version and
220 return code and if a non null retData buffer was supplied, tries to
221 decode the the return data and length
222 **********************************************************************/
223
224 static int berDecodeLoginData(
225 struct berval *replyBV,
226 int *serverVersion,
227 size_t *retDataLen,
228 void *retData )
229 {
230 int err = 0;
231 BerElement *replyBer = NULL;
232 char *retOctStr = NULL;
233 size_t retOctStrLen = 0;
234
235 if ((replyBer = ber_init(replyBV)) == NULL) {
236 err = LDAP_OPERATIONS_ERROR;
237 } else if (retData) {
238 retOctStrLen = *retDataLen + 1;
239 retOctStr = (char*)SMB_MALLOC_ARRAY(char, retOctStrLen);
240 if (!retOctStr) {
241 err = LDAP_OPERATIONS_ERROR;
242 } else if (ber_scanf(replyBer, "{iis}", serverVersion, &err, retOctStr, &retOctStrLen) != LBER_ERROR) {
243 if (*retDataLen >= retOctStrLen) {
244 memcpy(retData, retOctStr, retOctStrLen);
245 } else if (!err) {
246 err = LDAP_NO_MEMORY;
247 }
248
249 *retDataLen = retOctStrLen;
250 } else if (!err) {
251 err = LDAP_DECODING_ERROR;
252 }
253 } else {
254 if (ber_scanf(replyBer, "{ii}", serverVersion, &err) == LBER_ERROR) {
255 if (!err) {
256 err = LDAP_DECODING_ERROR;
257 }
258 }
259 }
260
261 if (replyBer) {
262 ber_free(replyBer, 1);
263 }
264
265 if (retOctStr != NULL) {
266 memset(retOctStr, 0, retOctStrLen);
267 free(retOctStr);
268 }
269
270 return err;
271 }
272
273 /**********************************************************************
274 Retrieves data in the login configuration of the specified object
275 that is tagged with the specified methodID and tag.
276 **********************************************************************/
277
278 static int getLoginConfig(
279 LDAP *ld,
280 char *objectDN,
281 unsigned int methodIDLen,
282 unsigned int *methodID,
283 char *tag,
284 size_t *dataLen,
285 void *data )
286 {
287 int err = 0;
288 struct berval *requestBV = NULL;
289 char *replyOID = NULL;
290 struct berval *replyBV = NULL;
291 int serverVersion = 0;
292
293 /* Validate unicode parameters. */
294 if ((strlen(objectDN) == 0) || ld == NULL) {
295 return LDAP_NO_SUCH_ATTRIBUTE;
296 }
297
298 err = berEncodeLoginData(&requestBV, objectDN, methodIDLen, methodID, tag, 0, NULL);
299 if (err) {
300 ;
301 } else if (!err && (err = ldap_extended_operation_s(ld, NMASLDAP_GET_LOGIN_CONFIG_REQUEST,
302 requestBV, NULL, NULL, &replyOID, &replyBV))) {
303 /* Call the ldap_extended_operation (synchronously) */
304 ;
305 } else if (!replyOID) {
306 /* Make sure there is a return OID */
307 err = LDAP_NOT_SUPPORTED;
308 } else if (strcmp(replyOID, NMASLDAP_GET_LOGIN_CONFIG_RESPONSE)) {
309 /* Is this what we were expecting to get back. */
310 err = LDAP_NOT_SUPPORTED;
311 } else if (!replyBV) {
312 /* Do we have a good returned berval? */
313
314 /* No; returned berval means we experienced a rather drastic error. */
315 /* Return operations error. */
316 err = LDAP_OPERATIONS_ERROR;
317 } else {
318
319 err = berDecodeLoginData(replyBV, &serverVersion, dataLen, data);
320
321 if (serverVersion != NMAS_LDAP_EXT_VERSION) {
322 err = LDAP_OPERATIONS_ERROR;
323 }
324 }
325
326 if (replyBV) {
327 ber_bvfree(replyBV);
328 }
329
330 /* Free the return OID string if one was returned. */
331 if (replyOID) {
332 ldap_memfree(replyOID);
333 }
334
335 /* Free memory allocated while building the request ber and berval. */
336 if (requestBV) {
337 ber_bvfree(requestBV);
338 }
339
340 /* Return the appropriate error/success code. */
341 return err;
342 }
343
344 /**********************************************************************
345 Attempts to get the Simple Password
346 **********************************************************************/
347
348 static int nmasldap_get_simple_pwd(
349 LDAP *ld,
350 char *objectDN,
351 size_t pwdLen,
352 char *pwd )
353 {
354 int err = 0;
355 unsigned int methodID = 0;
356 unsigned int methodIDLen = sizeof(methodID);
357 char tag[] = {'P','A','S','S','W','O','R','D',' ','H','A','S','H',0};
358 char *pwdBuf=NULL;
359 size_t pwdBufLen, bufferLen;
360
361 bufferLen = pwdBufLen = pwdLen+2;
362 pwdBuf = (char*)SMB_MALLOC_ARRAY(char, pwdBufLen); /* digest and null */
363 if (pwdBuf == NULL) {
364 return LDAP_NO_MEMORY;
365 }
366
367 err = getLoginConfig(ld, objectDN, methodIDLen, &methodID, tag, &pwdBufLen, pwdBuf);
368 if (err == 0) {
369 if (pwdBufLen !=0) {
370 pwdBuf[pwdBufLen] = 0; /* null terminate */
371
372 switch (pwdBuf[0]) {
373 case 1: /* cleartext password */
374 break;
375 case 2: /* SHA1 HASH */
376 case 3: /* MD5_ID */
377 case 4: /* UNIXCrypt_ID */
378 case 8: /* SSHA_ID */
379 default: /* Unknown digest */
380 err = LDAP_INAPPROPRIATE_AUTH; /* only return clear text */
381 break;
382 }
383
384 if (!err) {
385 if (pwdLen >= pwdBufLen-1) {
386 memcpy(pwd, &pwdBuf[1], pwdBufLen-1); /* skip digest tag and include null */
387 } else {
388 err = LDAP_NO_MEMORY;
389 }
390 }
391 }
392 }
393
394 if (pwdBuf != NULL) {
395 memset(pwdBuf, 0, bufferLen);
396 free(pwdBuf);
397 }
398
399 return err;
400 }
401
402 /**********************************************************************
403 Attempts to get the Universal Password
404 **********************************************************************/
405
406 static int nmasldap_get_password(
407 LDAP *ld,
408 char *objectDN,
409 size_t *pwdSize, /* in bytes */
410 unsigned char *pwd )
411 {
412 int err = 0;
413
414 struct berval *requestBV = NULL;
415 char *replyOID = NULL;
416 struct berval *replyBV = NULL;
417 int serverVersion;
418 char *pwdBuf;
419 size_t pwdBufLen, bufferLen;
420
421 /* Validate char parameters. */
422 if (objectDN == NULL || (strlen(objectDN) == 0) || pwdSize == NULL || ld == NULL) {
423 return LDAP_NO_SUCH_ATTRIBUTE;
424 }
425
426 bufferLen = pwdBufLen = *pwdSize;
427 pwdBuf = (char*)SMB_MALLOC_ARRAY(char, pwdBufLen+2);
428 if (pwdBuf == NULL) {
429 return LDAP_NO_MEMORY;
430 }
431
432 err = berEncodePasswordData(&requestBV, objectDN, NULL, NULL);
433 if (err) {
434 ;
435 } else if ((err = ldap_extended_operation_s(ld, NMASLDAP_GET_PASSWORD_REQUEST, requestBV, NULL, NULL, &replyOID, &replyBV))) {
436 ; /* Call the ldap_extended_operation (synchronously) */
437 } else if (!replyOID) {
438 /* Make sure there is a return OID */
439 err = LDAP_NOT_SUPPORTED;
440 } else if (strcmp(replyOID, NMASLDAP_GET_PASSWORD_RESPONSE)) {
441 /* Is this what we were expecting to get back. */
442 err = LDAP_NOT_SUPPORTED;
443 } else if (!replyBV) {
444 /* Do we have a good returned berval? */
445 /* No; returned berval means we experienced a rather drastic error. */
446 /* Return operations error. */
447 err = LDAP_OPERATIONS_ERROR;
448 } else {
449 err = berDecodeLoginData(replyBV, &serverVersion, &pwdBufLen, pwdBuf);
450
451 if (serverVersion != NMAS_LDAP_EXT_VERSION) {
452 err = LDAP_OPERATIONS_ERROR;
453
454 } else if (!err && pwdBufLen != 0) {
455 if (*pwdSize >= pwdBufLen+1 && pwd != NULL) {
456 memcpy(pwd, pwdBuf, pwdBufLen);
457 pwd[pwdBufLen] = 0; /* add null termination */
458 }
459 *pwdSize = pwdBufLen; /* does not include null termination */
460 }
461 }
462
463 if (replyBV) {
464 ber_bvfree(replyBV);
465 }
466
467 /* Free the return OID string if one was returned. */
468 if (replyOID) {
469 ldap_memfree(replyOID);
470 }
471
472 /* Free memory allocated while building the request ber and berval. */
473 if (requestBV) {
474 ber_bvfree(requestBV);
475 }
476
477 if (pwdBuf != NULL) {
478 memset(pwdBuf, 0, bufferLen);
479 free(pwdBuf);
480 }
481
482 /* Return the appropriate error/success code. */
483 return err;
484 }
485
486 /**********************************************************************
487 Get the user's password from NDS.
488 *********************************************************************/
489
490 int nds_get_password(
491 LDAP *ld,
492 char *object_dn,
493 size_t *pwd_len,
494 char *pwd )
495 {
496 int rc = -1;
497
498 rc = nmasldap_get_password(ld, object_dn, pwd_len, (unsigned char *)pwd);
499 if (rc == LDAP_SUCCESS) {
500 #ifdef DEBUG_PASSWORD
501 DEBUG(100,("nmasldap_get_password returned %s for %s\n", pwd, object_dn));
502 #endif
503 DEBUG(5, ("NDS Universal Password retrieved for %s\n", object_dn));
504 } else {
505 DEBUG(3, ("NDS Universal Password NOT retrieved for %s\n", object_dn));
506 }
507
508 if (rc != LDAP_SUCCESS) {
509 rc = nmasldap_get_simple_pwd(ld, object_dn, *pwd_len, pwd);
510 if (rc == LDAP_SUCCESS) {
511 #ifdef DEBUG_PASSWORD
512 DEBUG(100,("nmasldap_get_simple_pwd returned %s for %s\n", pwd, object_dn));
513 #endif
514 DEBUG(5, ("NDS Simple Password retrieved for %s\n", object_dn));
515 } else {
516 /* We couldn't get the password */
517 DEBUG(3, ("NDS Simple Password NOT retrieved for %s\n", object_dn));
518 return LDAP_INVALID_CREDENTIALS;
519 }
520 }
521
522 /* We got the password */
523 return LDAP_SUCCESS;
524 }
525