2 * Copyright (C) 1996-2014 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 "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
,
159 BerElement
*requestBer
= NULL
;
162 unsigned int elemCnt
= methodIDLen
/ sizeof(unsigned int);
164 char *utf8ObjPtr
=NULL
;
167 char *utf8TagPtr
= NULL
;
170 utf8ObjPtr
= objectDN
;
171 utf8ObjSize
= strlen(utf8ObjPtr
)+1;
174 utf8TagSize
= strlen(utf8TagPtr
)+1;
176 /* Allocate a BerElement for the request parameters. */
177 if ((requestBer
= ber_alloc()) == NULL
) {
178 err
= LDAP_ENCODING_ERROR
;
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;
185 /* BER encode the MethodID Length and value */
187 err
= (ber_printf(requestBer
, "{i{", methodIDLen
) < 0) ? LDAP_ENCODING_ERROR
: 0;
190 for (i
= 0; !err
&& i
< elemCnt
; ++i
) {
191 err
= (ber_printf(requestBer
, "i", methodID
[i
]) < 0) ? LDAP_ENCODING_ERROR
: 0;
195 err
= (ber_printf(requestBer
, "}}", 0) < 0) ? LDAP_ENCODING_ERROR
: 0;
199 /* BER Encode the the tag and data */
200 err
= (ber_printf(requestBer
, "oio}", utf8TagPtr
, utf8TagSize
, putDataLen
, putData
, putDataLen
) < 0) ? LDAP_ENCODING_ERROR
: 0;
202 /* BER Encode the the tag */
203 err
= (ber_printf(requestBer
, "o}", utf8TagPtr
, utf8TagSize
) < 0) ? LDAP_ENCODING_ERROR
: 0;
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
;
212 ber_free(requestBer
, 1);
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 **********************************************************************/
224 static int berDecodeLoginData(
225 struct berval
*replyBV
,
231 BerElement
*replyBer
= NULL
;
232 char *retOctStr
= NULL
;
233 size_t retOctStrLen
= 0;
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
);
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
);
246 err
= LDAP_NO_MEMORY
;
249 *retDataLen
= retOctStrLen
;
251 err
= LDAP_DECODING_ERROR
;
254 if (ber_scanf(replyBer
, "{ii}", serverVersion
, &err
) == LBER_ERROR
) {
256 err
= LDAP_DECODING_ERROR
;
262 ber_free(replyBer
, 1);
265 if (retOctStr
!= NULL
) {
266 memset(retOctStr
, 0, retOctStrLen
);
273 /**********************************************************************
274 Retrieves data in the login configuration of the specified object
275 that is tagged with the specified methodID and tag.
276 **********************************************************************/
278 static int getLoginConfig(
281 unsigned int methodIDLen
,
282 unsigned int *methodID
,
288 struct berval
*requestBV
= NULL
;
289 char *replyOID
= NULL
;
290 struct berval
*replyBV
= NULL
;
291 int serverVersion
= 0;
293 /* Validate unicode parameters. */
294 if ((strlen(objectDN
) == 0) || ld
== NULL
) {
295 return LDAP_NO_SUCH_ATTRIBUTE
;
298 err
= berEncodeLoginData(&requestBV
, objectDN
, methodIDLen
, methodID
, tag
, 0, NULL
);
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) */
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? */
314 /* No; returned berval means we experienced a rather drastic error. */
315 /* Return operations error. */
316 err
= LDAP_OPERATIONS_ERROR
;
319 err
= berDecodeLoginData(replyBV
, &serverVersion
, dataLen
, data
);
321 if (serverVersion
!= NMAS_LDAP_EXT_VERSION
) {
322 err
= LDAP_OPERATIONS_ERROR
;
330 /* Free the return OID string if one was returned. */
332 ldap_memfree(replyOID
);
335 /* Free memory allocated while building the request ber and berval. */
337 ber_bvfree(requestBV
);
340 /* Return the appropriate error/success code. */
344 /**********************************************************************
345 Attempts to get the Simple Password
346 **********************************************************************/
348 static int nmasldap_get_simple_pwd(
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};
359 size_t pwdBufLen
, bufferLen
;
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
;
367 err
= getLoginConfig(ld
, objectDN
, methodIDLen
, &methodID
, tag
, &pwdBufLen
, pwdBuf
);
370 pwdBuf
[pwdBufLen
] = 0; /* null terminate */
373 case 1: /* cleartext password */
375 case 2: /* SHA1 HASH */
377 case 4: /* UNIXCrypt_ID */
378 case 8: /* SSHA_ID */
379 default: /* Unknown digest */
380 err
= LDAP_INAPPROPRIATE_AUTH
; /* only return clear text */
385 if (pwdLen
>= pwdBufLen
-1) {
386 memcpy(pwd
, &pwdBuf
[1], pwdBufLen
-1); /* skip digest tag and include null */
388 err
= LDAP_NO_MEMORY
;
394 if (pwdBuf
!= NULL
) {
395 memset(pwdBuf
, 0, bufferLen
);
402 /**********************************************************************
403 Attempts to get the Universal Password
404 **********************************************************************/
406 static int nmasldap_get_password(
409 size_t *pwdSize
, /* in bytes */
414 struct berval
*requestBV
= NULL
;
415 char *replyOID
= NULL
;
416 struct berval
*replyBV
= NULL
;
419 size_t pwdBufLen
, bufferLen
;
421 /* Validate char parameters. */
422 if (objectDN
== NULL
|| (strlen(objectDN
) == 0) || pwdSize
== NULL
|| ld
== NULL
) {
423 return LDAP_NO_SUCH_ATTRIBUTE
;
426 bufferLen
= pwdBufLen
= *pwdSize
;
427 pwdBuf
= (char*)SMB_MALLOC_ARRAY(char, pwdBufLen
+2);
428 if (pwdBuf
== NULL
) {
429 return LDAP_NO_MEMORY
;
432 err
= berEncodePasswordData(&requestBV
, objectDN
, NULL
, NULL
);
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
;
449 err
= berDecodeLoginData(replyBV
, &serverVersion
, &pwdBufLen
, pwdBuf
);
451 if (serverVersion
!= NMAS_LDAP_EXT_VERSION
) {
452 err
= LDAP_OPERATIONS_ERROR
;
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 */
459 *pwdSize
= pwdBufLen
; /* does not include null termination */
467 /* Free the return OID string if one was returned. */
469 ldap_memfree(replyOID
);
472 /* Free memory allocated while building the request ber and berval. */
474 ber_bvfree(requestBV
);
477 if (pwdBuf
!= NULL
) {
478 memset(pwdBuf
, 0, bufferLen
);
482 /* Return the appropriate error/success code. */
486 /**********************************************************************
487 Get the user's password from NDS.
488 *********************************************************************/
490 int nds_get_password(
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
));
503 DEBUG(5, ("NDS Universal Password retrieved for %s\n", object_dn
));
505 DEBUG(3, ("NDS Universal Password NOT retrieved for %s\n", object_dn
));
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
));
514 DEBUG(5, ("NDS Simple Password retrieved for %s\n", object_dn
));
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
;
522 /* We got the password */