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

 

Introduction

Documentation

Support

Miscellaneous