2 * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
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.
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
14 * Original copyright & license follows:
16 * Copyright (C) Vince Brimhall 2004-2005
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.
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.
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.
34 #include "auth/digest/eDirectory/digest_common.h"
36 #if _SQUID_WINDOWS_ && !_SQUID_CYGWIN_
38 #define snprintf _snprintf
43 #define LDAPAPI __cdecl
46 #ifndef LDAP_OPT_X_TLS
47 #define LDAP_OPT_X_TLS 0x6000
49 #define ber_alloc() ber_alloc_t(0)
50 #endif /* LDAP_VERSION3 */
60 #include "edir_ldapext.h"
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"
69 #define NMAS_LDAP_EXT_VERSION 1
71 #define SMB_MALLOC_ARRAY(type, nelem) calloc(sizeof(type), nelem)
72 #define DEBUG(level, args)
74 /**********************************************************************
75 Take the request BER value and input data items and BER encodes the
76 data into the BER value
77 **********************************************************************/
79 static int berEncodePasswordData(
80 struct berval
**requestBV
,
83 const char *password2
)
86 BerElement
*requestBer
= NULL
;
88 const char * utf8ObjPtr
= NULL
;
90 const char * utf8PwdPtr
= NULL
;
92 const char * utf8Pwd2Ptr
= NULL
;
95 /* Convert objectDN and tag strings from Unicode to UTF-8 */
96 utf8ObjSize
= strlen(objectDN
)+1;
97 utf8ObjPtr
= objectDN
;
99 if (password
!= NULL
) {
100 utf8PwdSize
= strlen(password
)+1;
101 utf8PwdPtr
= password
;
104 if (password2
!= NULL
) {
105 utf8Pwd2Size
= strlen(password2
)+1;
106 utf8Pwd2Ptr
= password2
;
109 /* Allocate a BerElement for the request parameters. */
110 if ((requestBer
= ber_alloc()) == NULL
) {
111 err
= LDAP_ENCODING_ERROR
;
112 ber_free(requestBer
, 1);
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
);
123 /* BER encode the NMAS Version and the objectDN */
124 rc
= ber_printf(requestBer
, "{io}", NMAS_LDAP_EXT_VERSION
, utf8ObjPtr
, utf8ObjSize
);
128 err
= LDAP_ENCODING_ERROR
;
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
;
138 ber_free(requestBer
, 1);
144 /**********************************************************************
145 Take the request BER value and input data items and BER encodes the
146 data into the BER value
147 **********************************************************************/
149 static int berEncodeLoginData(
150 struct berval
**requestBV
,
152 unsigned int methodIDLen
,
153 unsigned int *methodID
,
158 unsigned int elemCnt
= methodIDLen
/ sizeof(unsigned int);
160 char *utf8ObjPtr
=NULL
;
163 char *utf8TagPtr
= NULL
;
166 utf8ObjPtr
= objectDN
;
167 utf8ObjSize
= strlen(utf8ObjPtr
)+1;
170 utf8TagSize
= strlen(utf8TagPtr
)+1;
172 /* Allocate a BerElement for the request parameters. */
173 BerElement
*requestBer
= ber_alloc();
175 return LDAP_ENCODING_ERROR
;
177 /* BER encode the NMAS Version and the objectDN */
178 if (ber_printf(requestBer
, "{io", NMAS_LDAP_EXT_VERSION
, utf8ObjPtr
, utf8ObjSize
) < 0) {
179 ber_free(requestBer
, 1);
180 return LDAP_ENCODING_ERROR
;
183 /* BER encode the MethodID Length and value */
184 if (ber_printf(requestBer
, "{i{", methodIDLen
) < 0) {
185 ber_free(requestBer
, 1);
186 return LDAP_ENCODING_ERROR
;
189 for (unsigned int i
= 0; i
< elemCnt
; ++i
) {
190 if (ber_printf(requestBer
, "i", methodID
[i
]) < 0) {
191 ber_free(requestBer
, 1);
192 return LDAP_ENCODING_ERROR
;
196 if (ber_printf(requestBer
, "}}", 0) < 0) {
197 ber_free(requestBer
, 1);
198 return LDAP_ENCODING_ERROR
;
202 /* BER Encode the the tag and data */
203 if (ber_printf(requestBer
, "oio}", utf8TagPtr
, utf8TagSize
, putDataLen
, putData
, putDataLen
) < 0) {
204 ber_free(requestBer
, 1);
205 return LDAP_ENCODING_ERROR
;
208 /* BER Encode the the tag */
209 if (ber_printf(requestBer
, "o}", utf8TagPtr
, utf8TagSize
) < 0) {
210 ber_free(requestBer
, 1);
211 return LDAP_ENCODING_ERROR
;
215 /* Convert the BER we just built to a berval that we'll send with the extended request. */
216 if (static_cast<ber_tag_t
>(ber_flatten(requestBer
, requestBV
)) == LBER_ERROR
) {
217 ber_free(requestBer
, 1);
218 return LDAP_ENCODING_ERROR
;
221 ber_free(requestBer
, 1);
222 return 0; /* no error */
225 /**********************************************************************
226 Takes the reply BER Value and decodes the NMAS server version and
227 return code and if a non null retData buffer was supplied, tries to
228 decode the the return data and length
229 **********************************************************************/
231 static int berDecodeLoginData(
232 struct berval
*replyBV
,
238 BerElement
*replyBer
= NULL
;
239 char *retOctStr
= NULL
;
240 size_t retOctStrLen
= 0;
242 if ((replyBer
= ber_init(replyBV
)) == NULL
) {
243 err
= LDAP_OPERATIONS_ERROR
;
244 } else if (retData
) {
245 retOctStrLen
= *retDataLen
+ 1;
246 retOctStr
= (char*)SMB_MALLOC_ARRAY(char, retOctStrLen
);
248 err
= LDAP_OPERATIONS_ERROR
;
249 } else if (ber_scanf(replyBer
, "{iis}", serverVersion
, &err
, retOctStr
, &retOctStrLen
) != LBER_ERROR
) {
250 if (*retDataLen
>= retOctStrLen
) {
251 memcpy(retData
, retOctStr
, retOctStrLen
);
253 err
= LDAP_NO_MEMORY
;
256 *retDataLen
= retOctStrLen
;
258 err
= LDAP_DECODING_ERROR
;
261 if (ber_scanf(replyBer
, "{ii}", serverVersion
, &err
) == LBER_ERROR
) {
263 err
= LDAP_DECODING_ERROR
;
269 ber_free(replyBer
, 1);
272 if (retOctStr
!= NULL
) {
273 memset(retOctStr
, 0, retOctStrLen
);
280 /**********************************************************************
281 Retrieves data in the login configuration of the specified object
282 that is tagged with the specified methodID and tag.
283 **********************************************************************/
285 static int getLoginConfig(
288 unsigned int methodIDLen
,
289 unsigned int *methodID
,
295 struct berval
*requestBV
= NULL
;
296 char *replyOID
= NULL
;
297 struct berval
*replyBV
= NULL
;
298 int serverVersion
= 0;
300 /* Validate unicode parameters. */
301 if ((strlen(objectDN
) == 0) || ld
== NULL
) {
302 return LDAP_NO_SUCH_ATTRIBUTE
;
305 err
= berEncodeLoginData(&requestBV
, objectDN
, methodIDLen
, methodID
, tag
, 0, NULL
);
308 } else if (!err
&& (err
= ldap_extended_operation_s(ld
, NMASLDAP_GET_LOGIN_CONFIG_REQUEST
,
309 requestBV
, NULL
, NULL
, &replyOID
, &replyBV
))) {
310 /* Call the ldap_extended_operation (synchronously) */
312 } else if (!replyOID
) {
313 /* Make sure there is a return OID */
314 err
= LDAP_NOT_SUPPORTED
;
315 } else if (strcmp(replyOID
, NMASLDAP_GET_LOGIN_CONFIG_RESPONSE
)) {
316 /* Is this what we were expecting to get back. */
317 err
= LDAP_NOT_SUPPORTED
;
318 } else if (!replyBV
) {
319 /* Do we have a good returned berval? */
321 /* No; returned berval means we experienced a rather drastic error. */
322 /* Return operations error. */
323 err
= LDAP_OPERATIONS_ERROR
;
326 err
= berDecodeLoginData(replyBV
, &serverVersion
, dataLen
, data
);
328 if (serverVersion
!= NMAS_LDAP_EXT_VERSION
) {
329 err
= LDAP_OPERATIONS_ERROR
;
337 /* Free the return OID string if one was returned. */
339 ldap_memfree(replyOID
);
342 /* Free memory allocated while building the request ber and berval. */
344 ber_bvfree(requestBV
);
347 /* Return the appropriate error/success code. */
351 /**********************************************************************
352 Attempts to get the Simple Password
353 **********************************************************************/
355 static int nmasldap_get_simple_pwd(
362 unsigned int methodID
= 0;
363 unsigned int methodIDLen
= sizeof(methodID
);
364 char tag
[] = {'P','A','S','S','W','O','R','D',' ','H','A','S','H',0};
366 size_t pwdBufLen
, bufferLen
;
368 bufferLen
= pwdBufLen
= pwdLen
+2;
369 pwdBuf
= (char*)SMB_MALLOC_ARRAY(char, pwdBufLen
); /* digest and null */
370 if (pwdBuf
== NULL
) {
371 return LDAP_NO_MEMORY
;
374 err
= getLoginConfig(ld
, objectDN
, methodIDLen
, &methodID
, tag
, &pwdBufLen
, pwdBuf
);
377 pwdBuf
[pwdBufLen
] = 0; /* null terminate */
380 case 1: /* cleartext password */
382 case 2: /* SHA1 HASH */
384 case 4: /* UNIXCrypt_ID */
385 case 8: /* SSHA_ID */
386 default: /* Unknown digest */
387 err
= LDAP_INAPPROPRIATE_AUTH
; /* only return clear text */
392 if (pwdLen
>= pwdBufLen
-1) {
393 memcpy(pwd
, &pwdBuf
[1], pwdBufLen
-1); /* skip digest tag and include null */
395 err
= LDAP_NO_MEMORY
;
401 if (pwdBuf
!= NULL
) {
402 memset(pwdBuf
, 0, bufferLen
);
409 /**********************************************************************
410 Attempts to get the Universal Password
411 **********************************************************************/
413 static int nmasldap_get_password(
416 size_t *pwdSize
, /* in bytes */
421 struct berval
*requestBV
= NULL
;
422 char *replyOID
= NULL
;
423 struct berval
*replyBV
= NULL
;
426 size_t pwdBufLen
, bufferLen
;
428 /* Validate char parameters. */
429 if (objectDN
== NULL
|| (strlen(objectDN
) == 0) || pwdSize
== NULL
|| ld
== NULL
) {
430 return LDAP_NO_SUCH_ATTRIBUTE
;
433 bufferLen
= pwdBufLen
= *pwdSize
;
434 pwdBuf
= (char*)SMB_MALLOC_ARRAY(char, pwdBufLen
+2);
435 if (pwdBuf
== NULL
) {
436 return LDAP_NO_MEMORY
;
439 err
= berEncodePasswordData(&requestBV
, objectDN
, NULL
, NULL
);
442 } else if ((err
= ldap_extended_operation_s(ld
, NMASLDAP_GET_PASSWORD_REQUEST
, requestBV
, NULL
, NULL
, &replyOID
, &replyBV
))) {
443 ; /* Call the ldap_extended_operation (synchronously) */
444 } else if (!replyOID
) {
445 /* Make sure there is a return OID */
446 err
= LDAP_NOT_SUPPORTED
;
447 } else if (strcmp(replyOID
, NMASLDAP_GET_PASSWORD_RESPONSE
)) {
448 /* Is this what we were expecting to get back. */
449 err
= LDAP_NOT_SUPPORTED
;
450 } else if (!replyBV
) {
451 /* Do we have a good returned berval? */
452 /* No; returned berval means we experienced a rather drastic error. */
453 /* Return operations error. */
454 err
= LDAP_OPERATIONS_ERROR
;
456 err
= berDecodeLoginData(replyBV
, &serverVersion
, &pwdBufLen
, pwdBuf
);
458 if (serverVersion
!= NMAS_LDAP_EXT_VERSION
) {
459 err
= LDAP_OPERATIONS_ERROR
;
461 } else if (!err
&& pwdBufLen
!= 0) {
462 if (*pwdSize
>= pwdBufLen
+1 && pwd
!= NULL
) {
463 memcpy(pwd
, pwdBuf
, pwdBufLen
);
464 pwd
[pwdBufLen
] = 0; /* add null termination */
466 *pwdSize
= pwdBufLen
; /* does not include null termination */
474 /* Free the return OID string if one was returned. */
476 ldap_memfree(replyOID
);
479 /* Free memory allocated while building the request ber and berval. */
481 ber_bvfree(requestBV
);
484 if (pwdBuf
!= NULL
) {
485 memset(pwdBuf
, 0, bufferLen
);
489 /* Return the appropriate error/success code. */
493 /**********************************************************************
494 Get the user's password from NDS.
495 *********************************************************************/
497 int nds_get_password(
505 rc
= nmasldap_get_password(ld
, object_dn
, pwd_len
, (unsigned char *)pwd
);
506 if (rc
== LDAP_SUCCESS
) {
507 #ifdef DEBUG_PASSWORD
508 DEBUG(100,("nmasldap_get_password returned %s for %s\n", pwd
, object_dn
));
510 DEBUG(5, ("NDS Universal Password retrieved for %s\n", object_dn
));
512 DEBUG(3, ("NDS Universal Password NOT retrieved for %s\n", object_dn
));
515 if (rc
!= LDAP_SUCCESS
) {
516 rc
= nmasldap_get_simple_pwd(ld
, object_dn
, *pwd_len
, pwd
);
517 if (rc
== LDAP_SUCCESS
) {
518 #ifdef DEBUG_PASSWORD
519 DEBUG(100,("nmasldap_get_simple_pwd returned %s for %s\n", pwd
, object_dn
));
521 DEBUG(5, ("NDS Simple Password retrieved for %s\n", object_dn
));
523 /* We couldn't get the password */
524 DEBUG(3, ("NDS Simple Password NOT retrieved for %s\n", object_dn
));
525 return LDAP_INVALID_CREDENTIALS
;
529 /* We got the password */