2 * Copyright (C) 1996-2022 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 * Copyright (C) 2009-2011 Chad E. Naugle
12 ********************************************************************************
14 * This file is part of ext_edirectory_userip_acl.
16 * ext_edirectory_userip_acl is free software: you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation, either version 2 of the License, or
19 * (at your option) any later version.
21 * ext_edirectory_userip_acl is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
26 * You should have received a copy of the GNU General Public License
27 * along with squid_edir_iplookup. If not, see <http://www.gnu.org/licenses/>.
29 ********************************************************************************
31 * ext_edirectory_userip_acl.cc -- Rev 2011-03-28
33 * - Misc code cleanups using "static", and 64-bit SLES fix for SearchFilterLDAP()
37 /* Squid-3.X includes */
39 #include "helper/protocol_defines.h"
43 #define EDUI_PROGRAM_NAME "ext_edirectory_userip_acl"
44 #define EDUI_PROGRAM_VERSION "2.1"
59 #ifdef HAVE_ARPA_INET_H
60 #include <arpa/inet.h>
62 #define LDAP_DEPRECATED 1 /* Set flag for enabling classic ldap functions */
72 #ifdef HAVE_SYS_SOCKET_H
73 #include <sys/socket.h>
75 #ifdef HAVE_NETINET_IN_H
76 #include <netinet/in.h>
79 #ifdef HELPER_INPUT_BUFFER
80 #define EDUI_MAXLEN HELPER_INPUT_BUFFER
82 #define EDUI_MAXLEN 4096 /* Modified to improve performance, unless HELPER_INPUT_BUFFER exists */
85 /* ldap compile options */
91 /* define LDAP_AUTH_TLS
92 * - ldap.h Hack for cleaner code, if it does not provide it.
95 # ifndef LDAP_AUTH_TLS
96 # define LDAP_AUTH_TLS ((ber_tag_t) 0xb3U)
100 /* conf_t - status flags */
101 #define EDUI_MODE_INIT 0x01
102 #define EDUI_MODE_DEBUG 0x02 /* Replace with Squid's debug system */
103 #define EDUI_MODE_TLS 0x04
104 #define EDUI_MODE_IPV4 0x08
105 #define EDUI_MODE_IPV6 0x10
106 #define EDUI_MODE_GROUP 0x20 /* Group is REQUIRED */
107 #define EDUI_MODE_PERSIST 0x40 /* Persistent LDAP connections */
108 #define EDUI_MODE_KILL 0x80
110 /* conf_t - Program configuration struct typedef */
112 char program
[EDUI_MAXLEN
];
113 char basedn
[EDUI_MAXLEN
];
114 char host
[EDUI_MAXLEN
];
115 char attrib
[EDUI_MAXLEN
];
116 char dn
[EDUI_MAXLEN
];
117 char passwd
[EDUI_MAXLEN
];
118 char search_filter
[EDUI_MAXLEN
]; /* Base search_filter that gets copied to edui_ldap_t */
122 time_t persist_timeout
;
126 /* edui_ldap_t - status flags */
127 #define LDAP_INIT_S 0x0001
128 #define LDAP_OPEN_S 0x0002
129 #define LDAP_BIND_S 0x0004
130 #define LDAP_SEARCH_S 0x0008 /* We got data */
131 #define LDAP_VAL_S 0x0010 /* Data has been copied to l->val */
132 #define LDAP_CLOSE_S 0x0020
133 #define LDAP_PERSIST_S 0x0040 /* Persistent connection */
134 #define LDAP_IDLE_S 0x0080 /* Connection is idle */
135 #define LDAP_SSL_S 0x0100
136 #define LDAP_TLS_S 0x0200
137 #define LDAP_IPV4_S 0x0400 /* Search IP is IPv4 */
138 #define LDAP_IPV6_S 0x0800 /* Search IP is IPv6 */
140 /* edui_ldap_t - Meaningful error codes */
141 #define LDAP_ERR_NULL -1 /* Null edui_ldap_t pointer */
142 #define LDAP_ERR_POINTER -2 /* Null l->lp pointer */
143 #define LDAP_ERR_PARAM -3 /* Null or Missing parameters */
144 #define LDAP_ERR_INIT -4 /* Not initialized */
145 #define LDAP_ERR_OPEN -5 /* Not open */
146 #define LDAP_ERR_CONNECT -6 /* Unable to connect */
147 #define LDAP_ERR_BIND -7 /* Not bound */
148 #define LDAP_ERR_SEARCHED -8 /* Already Searched */
149 #define LDAP_ERR_NOT_SEARCHED -9 /* Not searching */
150 #define LDAP_ERR_INVALID -10 /* Invalid parameter */
151 #define LDAP_ERR_OOB -11 /* Out of bounds value */
152 #define LDAP_ERR_PERSIST -12 /* Persistent mode is not active */
153 #define LDAP_ERR_DATA -13 /* Required data missing */
154 #define LDAP_ERR_NOTFOUND -14 /* Item not found */
155 #define LDAP_ERR_OTHER -15 /* Other Generic Error condition */
156 #define LDAP_ERR_FAILED -16 /* Operation failed */
157 #define LDAP_ERR_SUCCESS -17 /* Operation successful */
159 /* edui_ldap_t - struct typedef */
164 char basedn
[EDUI_MAXLEN
];
165 char host
[EDUI_MAXLEN
];
166 char dn
[EDUI_MAXLEN
];
167 char passwd
[EDUI_MAXLEN
];
168 char search_filter
[EDUI_MAXLEN
]; /* search_group gets appended here by GroupLDAP */
169 char search_ip
[EDUI_MAXLEN
]; /* Could be IPv4 or IPv6, set by ConvertIP */
170 char userid
[EDUI_MAXLEN
]; /* Resulting userid */
173 unsigned long type
; /* Type of bind */
176 int err
; /* LDAP error code */
178 int num_ent
; /* Number of entry's found via search */
179 int num_val
; /* Number of value's found via getval */
182 /* Global function prototypes */
183 static void local_printfx(const char *,...);
184 static int StringSplit(char *, char, char *, size_t);
185 static int BinarySplit(void *, size_t, char, void *, size_t);
186 static void DisplayVersion();
187 static void DisplayUsage();
188 static void InitConf();
189 static void DisplayConf();
190 static void InitLDAP(edui_ldap_t
*);
191 static int OpenLDAP(edui_ldap_t
*, char *, unsigned int);
192 static int CloseLDAP(edui_ldap_t
*);
193 static int SetVerLDAP(edui_ldap_t
*, int);
194 static int BindLDAP(edui_ldap_t
*, char *, char *, unsigned int);
195 static int ConvertIP(edui_ldap_t
*, char *);
196 static int ResetLDAP(edui_ldap_t
*);
197 static int SearchFilterLDAP(edui_ldap_t
*, char *);
198 static int SearchLDAP(edui_ldap_t
*, int, char *, char **);
199 static int SearchIPLDAP(edui_ldap_t
*);
200 const char *ErrLDAP(int);
201 extern "C" void SigTrap(int);
203 /* Global variables */
204 const char *search_attrib
[] = { "cn", "uid", "networkAddress", "groupMembership", NULL
};
205 static edui_conf_t edui_conf
;
206 static edui_ldap_t edui_ldap
;
212 * Print formatted message to stderr AND stdout, without preformatting.
214 * - Exists as a hack, because SEND_OK() does not appear to work here.
218 local_printfx(const char *msg
,...)
220 char prog
[EDUI_MAXLEN
], dbuf
[EDUI_MAXLEN
];
224 if (edui_conf
.program
[0] == '\0')
225 xstrncpy(prog
, EDUI_PROGRAM_NAME
, sizeof(prog
));
227 xstrncpy(prog
, edui_conf
.program
, sizeof(prog
));
231 debug("local_printfx() FAILURE, no data.\n");
236 x
= vsnprintf(dbuf
, sz
, msg
, ap
);
245 debug("local_printfx() FAILURE: %" PRIuSIZE
"\n", x
);
248 /* stdout needs to be flushed for it to work with Squid */
253 * StringSplit() - <string-to-split> <char> <split-object> <obj-size>
255 * Breaks down string, splitting out element <char> into <split-object>, and removing it from string.
256 * Will not exceed size tolerances.
260 StringSplit(char *In_Str
, char chr
, char *Out_Str
, size_t Out_Sz
)
262 if ((In_Str
== NULL
) || (Out_Str
== NULL
))
265 size_t In_Len
= strlen(In_Str
) + 1;
267 // find the char delimiter position...
269 while (*p
!= chr
&& *p
!= '\0' && (In_Str
+In_Len
) > p
) {
273 size_t i
= (p
-In_Str
);
275 // token to big for the output buffer
279 // wipe the unused Out_Obj area
280 memset(Out_Str
+i
, 0, Out_Sz
-i
);
281 // copy token from In_Str to Out_Str
282 memcpy(Out_Str
, In_Str
, i
);
284 // omit the delimiter
289 // chr not found (or \0 found first). Wipe whole input buffer.
290 memset(In_Str
, 0, In_Len
);
292 // Returning <0 breaks current ConvertIP() code for last object
296 // move the unused In_Str forward
297 memmove(In_Str
, p
, In_Len
-i
);
298 // wipe the end of In_Str
299 memset(In_Str
+In_Len
-i
, 0, i
);
304 * BinarySplit() - <binary-to-split> <bin-size> <char> <split-object> <obj-size>
306 * Breaks down Binary Block, splitting out element <char> into <split-object>, and removing it from Block, padding remainder with '\0'.
307 * Will not exceed size tolerances.
311 BinarySplit(void *In_Obj
, size_t In_Sz
, char chr
, void *Out_Obj
, size_t Out_Sz
)
314 if ((In_Obj
== NULL
) || (Out_Obj
== NULL
))
317 char *in
= static_cast<char*>(In_Obj
);
318 char *out
= static_cast<char*>(Out_Obj
);
320 // find the char delimiter position...
321 char *p
= static_cast<char*>(In_Obj
);
322 while (*p
!= chr
&& (in
+In_Sz
) > p
) {
328 // token to big for the output buffer
332 // wipe the unused Out_Obj area
333 memset(out
+i
, 0, Out_Sz
-i
);
334 // copy token from In_Obj to Out_Obj
335 memcpy(Out_Obj
, In_Obj
, i
);
337 // omit the delimiter
343 memset(In_Obj
, 0, In_Sz
);
345 // Returning <0 breaks current code for last object
349 // move the unused In_Obj forward
350 memmove(In_Obj
, p
, In_Sz
-i
);
351 // wipe the end of In_Obj
352 memset(in
+In_Sz
-i
, 0, i
);
356 /* Displays version information */
360 local_printfx("Squid eDirectory IP Lookup Helper %s. Copyright (C) 2009-2011 Chad E. Naugle\n", EDUI_PROGRAM_VERSION
);
363 /* Displays program usage information */
369 local_printfx("Usage: %s\n", edui_conf
.program
);
370 local_printfx(" -H <host> -p <port> [-Z] [-P] [-v 3] -b <basedn> -s <scope>\n");
371 local_printfx(" -D <binddn> -W <bindpass> -F <search-filter> [-G] \n\n");
372 local_printfx(" -d : Debug Mode.\n");
373 local_printfx(" -4 : Force Addresses to be in IPv4 (127.0.0.1 format).\n");
374 local_printfx(" -6 : Force Addresses to be in IPv6 (::1 format).\n");
375 local_printfx(" -H <host> : Specify hostname/ip of server.\n");
376 local_printfx(" -p <port> : Specify port number. (Range 1-65535)\n");
377 local_printfx(" -Z : Enable TLS security.\n");
378 local_printfx(" -P : Use persistent connections.\n");
379 local_printfx(" -t <sec> : Timeout factor for persistent connections. (Default is 60 sec, set to 0 for never timeout)\n");
380 local_printfx(" -v <1,2,3> : Set LDAP version to 1, 2, or 3.\n");
381 local_printfx(" -b <base> : Specify Base DN. (ie. \"o=ORG\")\n");
382 local_printfx(" -s <scope> : Specify LDAP Search Scope (base, one, sub; defaults to 'one').\n");
383 local_printfx(" -D <dn> : Specify Binding DN. (ie. cn=squid,o=ORG)\n");
384 local_printfx(" -W <pass> : Specify Binding password.\n");
385 local_printfx(" -u <attrib> : Set userid attribute (Defaults to \"cn\").\n");
386 local_printfx(" -F <filter> : Specify LDAP search filter. (ie. \"(objectClass=User)\")\n");
387 local_printfx(" -G : Specify if LDAP search group is required. (ie. \"groupMembership=\")\n");
388 local_printfx(" -V : Display version & exit.\n");
389 local_printfx(" -h : This screen & exit.\n");
393 /* Initializes program's configuration parameters */
397 *(edui_conf
.program
) = '\0';
398 *(edui_conf
.basedn
) = '\0';
399 *(edui_conf
.host
) = '\0';
400 *(edui_conf
.attrib
) = '\0';
401 *(edui_conf
.dn
) = '\0';
402 *(edui_conf
.passwd
) = '\0';
403 *(edui_conf
.search_filter
) = '\0';
404 edui_conf
.scope
= -1;
407 edui_conf
.persist_timeout
= -1;
409 edui_conf
.mode
|= EDUI_MODE_INIT
;
412 /* Displays running configuration */
416 if (!(edui_conf
.mode
& EDUI_MODE_DEBUG
))
420 local_printfx("Configuration:\n");
421 local_printfx(" EDUI_MAXLEN: %u\n", EDUI_MAXLEN
);
422 if (edui_conf
.mode
& EDUI_MODE_DEBUG
)
423 local_printfx(" Debug mode: ON\n");
425 local_printfx(" Debug mode: OFF\n");
426 if (edui_conf
.mode
& EDUI_MODE_IPV4
)
427 local_printfx(" Address format: IPv4 (127.0.0.1)\n");
428 else if (edui_conf
.mode
& EDUI_MODE_IPV6
)
429 local_printfx(" Address format: IPv6 (::1)\n");
431 local_printfx(" Address format: Not enforced.\n");
432 if (edui_conf
.host
[0] != '\0')
433 local_printfx(" Hostname: %s\n", edui_conf
.host
);
435 local_printfx(" Hostname: localhost\n");
436 if (edui_conf
.port
> 0)
437 local_printfx(" Port: %d\n", edui_conf
.port
);
439 local_printfx(" Port: %d\n", LDAP_PORT
);
440 if (edui_conf
.mode
& EDUI_MODE_TLS
)
441 local_printfx(" TLS mode: ON\n");
443 local_printfx(" TLS mode: OFF\n");
444 if (edui_conf
.mode
& EDUI_MODE_PERSIST
) {
445 local_printfx(" Persistent mode: ON\n");
446 if (edui_conf
.persist_timeout
> 0)
447 local_printfx(" Persistent mode idle timeout: %ld\n", static_cast<long int>(edui_conf
.persist_timeout
));
449 local_printfx(" Persistent mode idle timeout: OFF\n");
451 local_printfx(" Persistent mode: OFF\n");
452 local_printfx(" LDAP Version: %d\n", edui_conf
.ver
);
453 if (edui_conf
.basedn
[0] != '\0')
454 local_printfx(" Base DN: %s\n", edui_conf
.basedn
);
456 local_printfx(" Base DN: None\n");
457 if (edui_conf
.dn
[0] != '\0')
458 local_printfx(" Binding DN: %s\n", edui_conf
.dn
);
460 local_printfx(" Binding DN: Anonymous\n");
461 if (edui_conf
.passwd
[0] != '\0')
462 local_printfx(" Binding Password: %s\n", edui_conf
.passwd
);
464 local_printfx(" Binding Password: None\n");
465 switch (edui_conf
.scope
) {
467 local_printfx(" Search Scope: base\n");
470 local_printfx(" Search Scope: one level\n");
473 local_printfx(" Search Scope: subtree\n");
476 local_printfx(" Search Scope: base\n");
479 if (edui_conf
.attrib
[0] != '\0')
480 local_printfx(" Search Attribute: %s\n", edui_conf
.attrib
);
482 local_printfx(" Search Attribute: cn\n");
483 if (edui_conf
.search_filter
[0] != '\0')
484 local_printfx(" Search Filter: %s\n", edui_conf
.search_filter
);
486 local_printfx(" Search Filter: (&(objectClass=User)(networkAddress=*))\n");
487 if (edui_conf
.mode
& EDUI_MODE_GROUP
)
488 local_printfx(" Search Group Required: Yes\n");
490 local_printfx(" Search Group Required: No\n");
494 /* InitLDAP() - <edui_ldap_t>
496 * Initialize LDAP structure for use, zeroing out all variables.
500 InitLDAP(edui_ldap_t
*l
)
502 if (l
== NULL
) return;
508 ldap_value_free_len(l
->val
);
515 *(l
->search_filter
) = '\0';
517 memset(l
->search_ip
, '\0', sizeof(l
->search_ip
));
519 l
->status
|= LDAP_INIT_S
;
523 l
->err
= -1; /* Set error to LDAP_SUCCESS by default */
526 l
->num_ent
= 0; /* Number of entries in l->lm */
527 l
->num_val
= 0; /* Number of entries in l->val */
529 /* Set default settings from conf */
530 if (edui_conf
.basedn
[0] != '\0')
531 xstrncpy(l
->basedn
, edui_conf
.basedn
, sizeof(l
->basedn
));
532 if (edui_conf
.host
[0] != '\0')
533 xstrncpy(l
->host
, edui_conf
.host
, sizeof(l
->host
));
534 if (edui_conf
.port
!= 0)
535 l
->port
= edui_conf
.port
;
536 if (edui_conf
.dn
[0] != '\0')
537 xstrncpy(l
->dn
, edui_conf
.dn
, sizeof(l
->dn
));
538 if (edui_conf
.passwd
[0] != '\0')
539 xstrncpy(l
->passwd
, edui_conf
.passwd
, sizeof(l
->passwd
));
540 if (edui_conf
.search_filter
[0] != '\0')
541 xstrncpy(l
->search_filter
, edui_conf
.search_filter
, sizeof(l
->search_filter
));
542 if (!(edui_conf
.scope
< 0))
543 l
->scope
= edui_conf
.scope
;
546 /* OpenLDAP() - <edui_ldap_t> <host> <port>
548 * Build LDAP struct with hostname and port, and ready it for binding.
552 OpenLDAP(edui_ldap_t
*l
, char *h
, unsigned int p
)
554 if ((l
== NULL
) || (h
== NULL
)) return LDAP_ERR_NULL
;
555 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized, or might be in use */
556 if (l
->status
& LDAP_OPEN_S
) return LDAP_ERR_OPEN
; /* Already open */
557 if (l
->status
& LDAP_BIND_S
) return LDAP_ERR_BIND
; /* Already bound */
559 xstrncpy(l
->host
, h
, sizeof(l
->host
));
563 l
->port
= LDAP_PORT
; /* Default is port 389 */
566 if (l
->port
== LDAPS_PORT
)
567 l
->status
|= (LDAP_SSL_S
| LDAP_TLS_S
); /* SSL Port: 636 */
571 l
->lp
= ldap_init(l
->host
, l
->port
);
573 l
->lp
= ldap_open(l
->host
, l
->port
);
576 l
->err
= LDAP_CONNECT_ERROR
;
577 return LDAP_ERR_CONNECT
; /* Unable to connect */
580 // l->status &= ~(LDAP_INIT_S);
581 l
->status
|= LDAP_OPEN_S
;
582 l
->err
= LDAP_SUCCESS
;
583 return LDAP_ERR_SUCCESS
;
587 /* CloseLDAP() - <edui_ldap_t>
589 * Close LDAP connection, and clean up data structure.
593 CloseLDAP(edui_ldap_t
*l
)
596 if (l
== NULL
) return LDAP_ERR_NULL
;
597 if (l
->lp
== NULL
) return LDAP_ERR_NULL
;
598 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Connection not initialized */
599 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Connection not open */
605 if (l
->val
!= NULL
) {
606 ldap_value_free_len(l
->val
);
610 /* okay, so it's open, close it - No need to check other criteria */
611 s
= ldap_unbind(l
->lp
);
612 if (s
== LDAP_SUCCESS
) {
613 l
->status
= LDAP_INIT_S
;
615 l
->err
= s
; /* Set LDAP error code */
616 return LDAP_ERR_SUCCESS
;
618 l
->err
= s
; /* Set LDAP error code */
619 return LDAP_ERR_FAILED
;
623 /* SetVerLDAP() - <edui_ldap_t> <version>
625 * Set LDAP version number for connection to <version> of 1, 2, or 3
629 SetVerLDAP(edui_ldap_t
*l
, int v
)
632 if (l
== NULL
) return LDAP_ERR_NULL
;
633 if ((v
> 3) || (v
< 1)) return LDAP_ERR_PARAM
;
634 if (l
->lp
== NULL
) return LDAP_ERR_POINTER
;
635 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized */
636 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Not open */
637 if (l
->status
& LDAP_BIND_S
) return LDAP_ERR_BIND
; /* Already bound */
640 x
= ldap_set_option(l
->lp
, LDAP_OPT_PROTOCOL_VERSION
, &v
);
641 if (x
== LDAP_SUCCESS
) {
643 l
->err
= x
; /* Set LDAP error code */
644 return LDAP_ERR_SUCCESS
;
646 l
->err
= x
; /* Set LDAP error code */
647 return LDAP_ERR_FAILED
;
651 /* BindLDAP() - <edui_ldap_t> <use-dn> <use-password> <type>
653 * Bind LDAP connection (Open) using optional dn and password, of <type>
657 BindLDAP(edui_ldap_t
*l
, char *dn
, char *pw
, unsigned int t
)
660 if (l
== NULL
) return LDAP_ERR_NULL
;
661 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized */
662 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Not open */
663 if (l
->status
& LDAP_BIND_S
) return LDAP_ERR_BIND
; /* Already bound */
664 if (l
->lp
== NULL
) return LDAP_ERR_POINTER
; /* Error */
666 /* Copy details - dn and pw CAN be NULL for anonymous and/or TLS */
668 if (strlen(dn
) >= sizeof(l
->dn
))
669 return LDAP_ERR_OOB
; /* DN too large */
671 if ((l
->basedn
[0] != '\0') && (strstr(dn
, l
->basedn
) == NULL
)) {
672 /* We got a basedn, but it's not part of dn */
673 const int x
= snprintf(l
->dn
, sizeof(l
->dn
)-1, "%s,%s", dn
, l
->basedn
);
674 if (x
< 0 || static_cast<size_t>(x
) >= sizeof(l
->dn
))
675 return LDAP_ERR_OOB
; /* DN too large */
677 xstrncpy(l
->dn
, dn
, sizeof(l
->dn
));
680 xstrncpy(l
->passwd
, pw
, sizeof(l
->passwd
));
687 case LDAP_AUTH_SIMPLE
:
693 #ifdef LDAP_AUTH_KRBV4
694 case LDAP_AUTH_KRBV4
:
698 #ifdef LDAP_AUTH_KRBV41
699 case LDAP_AUTH_KRBV41
:
703 #ifdef LDAP_AUTH_KRBV42
704 case LDAP_AUTH_KRBV42
:
709 case LDAP_AUTH_TLS
: /* Added for chicken switch to TLS-enabled without using SSL */
714 l
->type
= LDAP_AUTH_NONE
;
715 break; /* Default to anonymous bind */
719 #if defined(LDAP_AUTH_TLS) && defined(NETSCAPE_SSL) && HAVE_LDAP_START_TLS_S
720 if (l
->type
== LDAP_AUTH_TLS
)
721 s
= ldap_start_tls_s(l
->lp
, NULL
, NULL
);
724 s
= ldap_bind_s(l
->lp
, l
->dn
, l
->passwd
, l
->type
);
725 if (s
== LDAP_SUCCESS
) {
726 l
->status
|= LDAP_BIND_S
; /* Success */
727 l
->err
= s
; /* Set LDAP error code */
728 return LDAP_ERR_SUCCESS
;
730 l
->err
= s
; /* Set LDAP error code */
731 return LDAP_ERR_FAILED
;
735 // XXX: duplicate (partial) of Ip::Address::lookupHostIp
737 * Convert the IP address string representation in src to
738 * its binary representation.
740 * \return binary representation of the src IP address.
741 * Must be free'd using freeaddrinfo().
743 static struct addrinfo
*
744 makeIpBinary(const char *src
)
746 struct addrinfo want
;
747 memset(&want
, 0, sizeof(want
));
748 want
.ai_flags
= AI_NUMERICHOST
; // prevent actual DNS lookups!
750 struct addrinfo
*dst
= nullptr;
751 if (getaddrinfo(src
, nullptr, &want
, &dst
) != 0) {
753 /* free any memory getaddrinfo() dynamically allocated. */
763 * Convert srcLen bytes from src into HEX and store into dst, which
764 * has a maximum content size of dstSize including c-string terminator.
765 * The dst value produced will be a 0-terminated c-string.
767 * \retval N length of dst written (excluding c-string terminator)
768 * \retval -11 (LDAP_ERR_OOB) buffer overflow detected
771 makeHexString(char *dst
, const int dstSize
, const char *src
, const int srcLen
)
773 // HEX encoding doubles the amount of bytes/octets copied
774 if ((srcLen
*2) >= dstSize
)
775 return LDAP_ERR_OOB
; // cannot copy that many
779 for (int k
= 0; k
< srcLen
; ++k
) {
780 int c
= static_cast<int>(src
[k
]);
784 const int hlen
= snprintf(hexc
, sizeof(hexc
), "%02X", c
);
785 if (hlen
< 0 || static_cast<size_t>(hlen
) > sizeof(hexc
)) // should be impossible
793 * ConvertIP() - <edui_ldap_t> <ip>
795 * Take an IPv4 address in dot-decimal or IPv6 notation, and convert to 2-digit HEX stored in l->search_ip
796 * This is the networkAddress that we search LDAP for.
799 ConvertIP(edui_ldap_t
*l
, char *ip
)
802 if (l
== NULL
) return LDAP_ERR_NULL
;
803 if (ip
== NULL
) return LDAP_ERR_PARAM
;
804 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized */
805 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Not open */
806 if (!(l
->status
& LDAP_BIND_S
)) return LDAP_ERR_BIND
; /* Not bound */
808 y
= memchr((void *)ip
, ':', EDUI_MAXLEN
);
809 z
= memchr((void *)ip
, '.', EDUI_MAXLEN
);
810 if ((y
!= NULL
) && (z
!= NULL
)) {
813 return LDAP_ERR_INVALID
;
815 if ((y
!= NULL
) && (edui_conf
.mode
& EDUI_MODE_IPV4
)) {
816 /* IPv4 Mode forced */
817 return LDAP_ERR_INVALID
;
818 } else if (y
!= NULL
) {
820 if (l
->status
& LDAP_IPV4_S
)
821 l
->status
&= ~(LDAP_IPV4_S
);
822 if (!(l
->status
& LDAP_IPV6_S
))
823 l
->status
|= (LDAP_IPV6_S
);
826 if ((z
!= NULL
) && (edui_conf
.mode
& EDUI_MODE_IPV6
)) {
827 /* IPv6 Mode forced */
828 return LDAP_ERR_INVALID
;
829 } else if (z
!= NULL
) {
831 if (l
->status
& LDAP_IPV6_S
)
832 l
->status
&= ~(LDAP_IPV6_S
);
833 if (!(l
->status
& LDAP_IPV4_S
))
834 l
->status
|= (LDAP_IPV4_S
);
838 size_t s
= LDAP_ERR_INVALID
;
839 if (struct addrinfo
*dst
= makeIpBinary(ip
)) {
840 if (dst
->ai_family
== AF_INET6
) {
841 struct sockaddr_in6
*sia
= reinterpret_cast<struct sockaddr_in6
*>(dst
->ai_addr
);
842 const char *ia
= reinterpret_cast<const char *>(sia
->sin6_addr
.s6_addr
);
843 s
= makeHexString(l
->search_ip
, sizeof(l
->search_ip
), ia
, 16); // IPv6 = 16-byte address
845 } else if (dst
->ai_family
== AF_INET
) {
846 struct sockaddr_in
*sia
= reinterpret_cast<struct sockaddr_in
*>(dst
->ai_addr
);
847 const char *ia
= reinterpret_cast<const char *>(&(sia
->sin_addr
));
848 s
= makeHexString(l
->search_ip
, sizeof(l
->search_ip
), ia
, 4); // IPv4 = 4-byte address
849 } // else leave s with LDAP_ERR_INVALID value
856 /* ResetLDAP() - <edui_ldap_t>
858 * Resets LDAP connection for next search query.
862 ResetLDAP(edui_ldap_t
*l
)
864 if (l
== NULL
) return LDAP_ERR_NULL
;
865 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized */
866 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Not open */
867 if (!(l
->status
& LDAP_BIND_S
)) return LDAP_ERR_BIND
; /* Not bound */
868 if (!(l
->status
& LDAP_PERSIST_S
)) return LDAP_ERR_PERSIST
; /* Not persistent */
870 /* Cleanup data struct */
871 if (l
->status
& LDAP_VAL_S
)
872 l
->status
&= ~(LDAP_VAL_S
);
873 if (l
->status
& LDAP_SEARCH_S
)
874 l
->status
&= ~(LDAP_SEARCH_S
);
875 if (l
->status
& LDAP_IPV4_S
)
876 l
->status
&= ~(LDAP_IPV4_S
);
877 if (l
->status
& LDAP_IPV6_S
)
878 l
->status
&= ~(LDAP_IPV6_S
);
883 if (l
->val
!= NULL
) {
884 ldap_value_free_len(l
->val
);
887 memset(l
->search_ip
, '\0', sizeof(l
->search_ip
));
888 *(l
->search_filter
) = '\0';
889 xstrncpy(l
->search_filter
, edui_conf
.search_filter
, sizeof(l
->search_filter
));
891 if (!(l
->status
& LDAP_IDLE_S
))
892 l
->status
|= LDAP_IDLE_S
; /* Set idle mode */
895 l
->err
= LDAP_SUCCESS
;
896 return LDAP_ERR_SUCCESS
;
900 * SearchFilterLDAP() - <edui_ldap_t> <IP> <group>
902 * Build LDAP Search Filter string and copy to l->search_filter
906 SearchFilterLDAP(edui_ldap_t
*l
, char *group
)
910 char bufa
[EDUI_MAXLEN
], bufb
[EDUI_MAXLEN
], bufc
[EDUI_MAXLEN
], bufd
[EDUI_MAXLEN
], bufg
[EDUI_MAXLEN
];
911 if (l
== NULL
) return LDAP_ERR_NULL
;
912 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized */
913 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Not open */
914 if (!(l
->status
& LDAP_BIND_S
)) return LDAP_ERR_BIND
; /* Not Bound */
915 if (l
->search_ip
[0] == '\0') return LDAP_ERR_DATA
; /* Search IP is required */
917 /* Zero out if not already */
918 memset(bufa
, '\0', sizeof(bufa
));
919 // using memset() for 'bufa' fixes the 64-bit issue
925 s
= strlen(l
->search_ip
);
929 for (i
= 0; i
< s
; ++i
) {
933 bufc
[j
] = l
->search_ip
[i
];
937 bufc
[j
] = l
->search_ip
[i
];
943 /* No groupMembership= to add, yay! */
945 if (l
->status
& LDAP_IPV4_S
) {
946 const int ln
= snprintf(bufd
, sizeof(bufd
), "(networkAddress=8\\23\\00\\00%s)(networkAddress=9\\23\\00\\00%s)", bufc
, bufc
);
947 if (ln
< 0 || static_cast<size_t>(ln
) >= sizeof(bufd
))
950 } else if (l
->status
& LDAP_IPV6_S
) {
951 const int ln
= snprintf(bufd
, sizeof(bufd
), "(networkAddress=10\\23\\00\\00%s)(networkAddress=11\\23\\00\\00%s)", bufc
, bufc
);
952 if (ln
< 0 || static_cast<size_t>(ln
) >= sizeof(bufd
))
955 const int x
= snprintf(bufa
, sizeof(bufa
), "(&%s(|(networkAddress=1\\23%s)%s))", edui_conf
.search_filter
, bufc
, bufd
);
956 if (x
< 0 || static_cast<size_t>(x
) >= sizeof(bufa
))
960 /* Needs groupMembership= to add... */
961 /* groupMembership -- NOTE: Squid *MUST* provide "cn=" from squid.conf */
962 if ((l
->basedn
[0] != '\0') && (strstr(group
, l
->basedn
) == NULL
)) {
963 const int ln
= snprintf(bufg
, sizeof(bufg
), ",%s", l
->basedn
);
964 if (ln
< 0 || static_cast<size_t>(ln
) >= sizeof(bufd
))
968 if (l
->status
& LDAP_IPV4_S
) {
969 const int ln
= snprintf(bufd
, sizeof(bufd
), "(networkAddress=8\\23\\00\\00%s)(networkAddress=9\\23\\00\\00%s)", bufc
, bufc
);
970 if (ln
< 0 || static_cast<size_t>(ln
) >= sizeof(bufd
))
972 } else if (l
->status
& LDAP_IPV6_S
) {
973 const int ln
= snprintf(bufd
, sizeof(bufd
), "(networkAddress=10\\23\\00\\00%s)(networkAddress=11\\23\\00\\00%s)", bufc
, bufc
);
974 if (ln
< 0 || static_cast<size_t>(ln
) >= sizeof(bufd
))
977 const int x
= snprintf(bufa
, sizeof(bufa
), "(&(&%s(groupMembership=%s%s)(|(networkAddress=1\\23%s)%s)))", edui_conf
.search_filter
, group
, bufg
, bufc
, bufd
);
978 if (x
< 0 || static_cast<size_t>(x
) >= sizeof(bufa
))
982 xstrncpy(l
->search_filter
, bufa
, sizeof(l
->search_filter
));
987 * SearchLDAP() - <edui_ldap_t> <scope> <filter> <attrib>
989 * Initate LDAP query, under <scope> levels, filtering matches with <filter> and optionally <attrib>
990 * <attrib> will generally be networkAddress ...
994 SearchLDAP(edui_ldap_t
*l
, int scope
, char *filter
, char **attrs
)
997 char ft
[EDUI_MAXLEN
];
998 if (l
== NULL
) return LDAP_ERR_NULL
;
999 if ((scope
< 0) || (filter
== NULL
)) return LDAP_ERR_PARAM
; /* If attrs is NULL, then all attrs will return */
1000 if (l
->lp
== NULL
) return LDAP_ERR_POINTER
;
1001 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized */
1002 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Not open */
1003 if (!(l
->status
& LDAP_BIND_S
)) return LDAP_ERR_BIND
; /* Not bound */
1004 if (l
->status
& LDAP_SEARCH_S
) return LDAP_ERR_SEARCHED
; /* Already searching */
1005 if (l
->basedn
[0] == '\0') return LDAP_ERR_DATA
; /* We require a basedn */
1007 ldap_msgfree(l
->lm
); /* Make sure l->lm is empty */
1009 xstrncpy(ft
, filter
, sizeof(ft
));
1011 /* We have a binded connection, with a free l->lm, so let's get this done */
1014 s
= ldap_search_s(l
->lp
, l
->basedn
, LDAP_SCOPE_BASE
, ft
, attrs
, 0, &(l
->lm
));
1017 s
= ldap_search_s(l
->lp
, l
->basedn
, LDAP_SCOPE_ONELEVEL
, ft
, attrs
, 0, &(l
->lm
));
1020 s
= ldap_search_s(l
->lp
, l
->basedn
, LDAP_SCOPE_SUBTREE
, ft
, attrs
, 0, &(l
->lm
));
1023 /* Only search ONE by default */
1024 s
= ldap_search_s(l
->lp
, l
->basedn
, LDAP_SCOPE_ONELEVEL
, ft
, attrs
, 0, &(l
->lm
));
1027 if (s
== LDAP_SUCCESS
) {
1028 l
->status
|= (LDAP_SEARCH_S
); /* Mark as searched */
1030 l
->idle_time
= 0; /* Connection in use, reset idle timer */
1031 l
->num_ent
= ldap_count_entries(l
->lp
, l
->lm
); /* Counted */
1032 return LDAP_ERR_SUCCESS
;
1036 return LDAP_ERR_FAILED
;
1041 * SearchIPLDAP() - <edui_ldap_t>
1043 * Scan LDAP and get all networkAddress Values, and see if they match l->search_ip
1044 * Actual IP matching routine for eDirectory
1048 SearchIPLDAP(edui_ldap_t
*l
)
1053 char bufa
[EDUI_MAXLEN
];
1054 char bufb
[EDUI_MAXLEN
];
1056 if (l
== NULL
) return LDAP_ERR_NULL
;
1057 if (l
->lp
== NULL
) return LDAP_ERR_POINTER
;
1058 if (!(l
->status
& LDAP_INIT_S
)) return LDAP_ERR_INIT
; /* Not initialized */
1059 if (!(l
->status
& LDAP_OPEN_S
)) return LDAP_ERR_OPEN
; /* Not open */
1060 if (!(l
->status
& LDAP_BIND_S
)) return LDAP_ERR_BIND
; /* Not bound */
1061 if (!(l
->status
& LDAP_SEARCH_S
)) return LDAP_ERR_NOT_SEARCHED
; /* Not searched */
1062 if (l
->num_ent
<= 0) {
1063 debug("l->num_ent: %d\n", l
->num_ent
);
1064 return LDAP_ERR_DATA
; /* No entries found */
1067 ldap_value_free_len(l
->val
); /* Clear data before populating */
1069 if (l
->status
& LDAP_VAL_S
)
1070 l
->status
&= ~(LDAP_VAL_S
); /* Clear VAL bit */
1071 if (edui_conf
.attrib
[0] == '\0')
1072 xstrncpy(edui_conf
.attrib
, "cn", sizeof(edui_conf
.attrib
)); /* Make sure edui_conf.attrib is set */
1074 /* Sift through entries */
1075 struct berval
**ber
= NULL
;
1076 for (ent
= ldap_first_entry(l
->lp
, l
->lm
); ent
!= NULL
; ent
= ldap_next_entry(l
->lp
, ent
)) {
1077 l
->val
= ldap_get_values_len(l
->lp
, ent
, "networkAddress");
1078 ber
= ldap_get_values_len(l
->lp
, ent
, edui_conf
.attrib
); /* edui_conf.attrib is the <userid> mapping */
1079 if (l
->val
!= NULL
) {
1080 x
= ldap_count_values_len(l
->val
); /* We got x values ... */
1083 /* Display all values */
1084 for (i
= 0; i
< x
; ++i
) {
1085 j
= l
->val
[i
]->bv_len
;
1086 memcpy(bufa
, l
->val
[i
]->bv_val
, j
);
1087 z
= BinarySplit(bufa
, j
, '#', bufb
, sizeof(bufb
));
1088 /* BINARY DEBUGGING *
1089 local_printfx("value[%" PRIuSIZE "]: BinarySplit(", (size_t) i);
1090 for (k = 0; k < z; ++k) {
1094 local_printfx("%02X", c);
1096 local_printfx(", ");
1097 for (k = 0; k < (j - z - 1); ++k) {
1101 local_printfx("%02X", c);
1103 local_printfx("): %" PRIuSIZE "\n", (size_t) z);
1104 * BINARY DEBUGGING */
1108 /* IPv4 address (eDirectory 8.7 and below) */
1109 /* bufa is the address, just compare it */
1110 if (!(l
->status
& LDAP_IPV4_S
) || (l
->status
& LDAP_IPV6_S
))
1111 break; /* Not looking for IPv4 */
1112 const int blen
= makeHexString(bufb
, sizeof(bufb
), bufa
, z
);
1115 /* Compare value with IP */
1116 if (memcmp(l
->search_ip
, bufb
, blen
) == 0) {
1117 /* We got a match! - Scan 'ber' for 'cn' values */
1118 z
= ldap_count_values_len(ber
);
1119 for (j
= 0; j
< z
; ++j
) {
1120 // broken? xstrncpy(l->userid, ber[j]->bv_val, min(sizeof(l->userid),static_cast<size_t>(ber[j]->bv_len)));
1121 xstrncpy(l
->userid
, ber
[j
]->bv_val
, sizeof(l
->userid
));
1122 /* Using bv_len of min() breaks the result by 2 chars */
1124 ldap_value_free_len(l
->val
);
1126 ldap_value_free_len(ber
);
1129 l
->err
= LDAP_SUCCESS
;
1130 l
->status
&= ~(LDAP_SEARCH_S
);
1131 return LDAP_ERR_SUCCESS
; /* We got our userid */
1133 /* Not matched, continue */
1134 } else if ((j
== 8) || (j
== 9)) {
1135 /* IPv4 (UDP/TCP) address (eDirectory 8.8 and higher) */
1136 /* bufa + 2 is the address (skip 2 digit port) */
1137 if (!(l
->status
& LDAP_IPV4_S
) || (l
->status
& LDAP_IPV6_S
))
1138 break; /* Not looking for IPv4 */
1139 const int blen
= makeHexString(bufb
, sizeof(bufb
), &bufa
[2], z
);
1142 /* Compare value with IP */
1143 if (memcmp(l
->search_ip
, bufb
, blen
) == 0) {
1144 /* We got a match! - Scan 'ber' for 'cn' values */
1145 z
= ldap_count_values_len(ber
);
1146 for (j
= 0; j
< z
; ++j
) {
1147 // broken? xstrncpy(l->userid, ber[j]->bv_val, min(sizeof(l->userid),static_cast<size_t>(ber[j]->bv_len)));
1148 xstrncpy(l
->userid
, ber
[j
]->bv_val
, sizeof(l
->userid
));
1149 /* Using bv_len of min() breaks the result by 2 chars */
1151 ldap_value_free_len(l
->val
);
1153 ldap_value_free_len(ber
);
1156 l
->err
= LDAP_SUCCESS
;
1157 l
->status
&= ~(LDAP_SEARCH_S
);
1158 return LDAP_ERR_SUCCESS
; /* We got our userid */
1160 /* Not matched, continue */
1161 } else if ((j
== 10) || (j
== 11)) {
1162 /* IPv6 (UDP/TCP) address (eDirectory 8.8 and higher) */
1163 /* bufa + 2 is the address (skip 2 digit port) */
1164 if (!(l
->status
& LDAP_IPV6_S
))
1165 break; /* Not looking for IPv6 */
1166 const int blen
= makeHexString(bufb
, sizeof(bufb
), &bufa
[2], z
);
1169 /* Compare value with IP */
1170 if (memcmp(l
->search_ip
, bufb
, blen
) == 0) {
1171 /* We got a match! - Scan 'ber' for 'cn' values */
1172 z
= ldap_count_values_len(ber
);
1173 for (j
= 0; j
< z
; ++j
) {
1174 // broken? xstrncpy(l->userid, ber[j]->bv_val, min(sizeof(l->userid),static_cast<size_t>(ber[j]->bv_len)));
1175 xstrncpy(l
->userid
, ber
[j
]->bv_val
, sizeof(l
->userid
));
1176 /* Using bv_len of min() breaks the result by 2 chars */
1178 ldap_value_free_len(l
->val
);
1180 ldap_value_free_len(ber
);
1183 l
->err
= LDAP_SUCCESS
;
1184 l
->status
&= ~(LDAP_SEARCH_S
);
1185 return LDAP_ERR_SUCCESS
; /* We got our userid */
1187 /* Not matched, continue */
1190 /* Others are unsupported */
1194 ldap_value_free_len(ber
);
1198 ldap_value_free_len(l
->val
);
1202 ldap_value_free_len(ber
);
1205 /* Attr not found, continue */
1207 /* No entries found using given attr */
1208 if (l
->val
!= NULL
) {
1209 ldap_value_free_len(l
->val
);
1212 if (l
->lm
!= NULL
) {
1213 ldap_msgfree(l
->lm
);
1218 l
->err
= LDAP_NO_SUCH_OBJECT
;
1219 l
->status
&= ~(LDAP_SEARCH_S
);
1220 return LDAP_ERR_NOTFOUND
; /* Not found ... Sorry :) */
1224 * ErrLDAP() - <errno>
1226 * Returns error description of error code
1234 return "Null pointer provided";
1235 case LDAP_ERR_POINTER
:
1236 return "Null LDAP pointer";
1237 case LDAP_ERR_PARAM
:
1238 return "Null or Missing parameter(s)";
1240 return "LDAP data not initialized";
1242 return "LDAP connection is not active";
1243 case LDAP_ERR_CONNECT
:
1244 return "Unable to connect to LDAP host";
1246 return "LDAP connection is not bound";
1247 case LDAP_ERR_SEARCHED
:
1248 return "LDAP connection has already been searched";
1249 case LDAP_ERR_NOT_SEARCHED
:
1250 return "LDAP connection has not been searched";
1251 case LDAP_ERR_INVALID
:
1252 return "Invalid parameters";
1254 return "Parameter is out of bounds";
1255 case LDAP_ERR_PERSIST
:
1256 return "Persistent mode is not active";
1258 return "Required data has not been found";
1259 case LDAP_ERR_NOTFOUND
:
1260 return "Item or object has not been found";
1261 case LDAP_ERR_OTHER
:
1262 return "An unknown error has occurred";
1263 case LDAP_ERR_FAILED
:
1264 return "Operation has failed";
1265 case LDAP_ERR_SUCCESS
:
1266 return "Operation is successful";
1268 return "An unknown error has occurred";
1273 * SigTrap() - <signal>
1275 * Traps signal codes by number, and gracefully shuts down.
1281 if (!(edui_conf
.mode
& EDUI_MODE_KILL
))
1282 edui_conf
.mode
|= EDUI_MODE_KILL
;
1285 if (edui_ldap
.status
& LDAP_OPEN_S
)
1286 CloseLDAP(&edui_ldap
);
1288 debug("Terminating, Signal: %d\n", s
);
1293 * MainSafe() - <argc> <argv>
1295 * "Safe" version of main()
1299 MainSafe(int argc
, char **argv
)
1301 char bufa
[EDUI_MAXLEN
], bufb
[EDUI_MAXLEN
], *p
= NULL
;
1302 char bufc
[EDUI_MAXLEN
];
1303 char sfmod
[EDUI_MAXLEN
];
1307 struct sigaction sv
;
1311 memset(bufa
, '\0', sizeof(bufa
));
1312 memset(bufb
, '\0', sizeof(bufb
));
1313 memset(bufc
, '\0', sizeof(bufc
));
1314 memset(sfmod
, '\0', sizeof(sfmod
));
1315 memset(&sv
, 0, sizeof(sv
));
1318 xstrncpy(edui_conf
.program
, argv
[0], sizeof(edui_conf
.program
));
1324 for (i
= 1; i
< k
; ++i
) {
1325 /* Classic / novelty usage schemes */
1326 if (!strcmp(argv
[i
], "--help")) {
1329 } else if (!strcmp(argv
[i
], "--usage")) {
1332 } else if (!strcmp(argv
[i
], "--version")) {
1335 } else if (argv
[i
][0] == '-') {
1336 s
= strlen(argv
[i
]);
1337 for (j
= 1; j
< s
; ++j
) {
1338 switch (argv
[i
][j
]) {
1346 if (!(edui_conf
.mode
& EDUI_MODE_DEBUG
))
1347 edui_conf
.mode
|= EDUI_MODE_DEBUG
; /* Don't set mode more than once */
1348 debug_enabled
= 1; /* Official Squid-3 Debug Mode */
1351 if (!(edui_conf
.mode
& EDUI_MODE_IPV4
) || !(edui_conf
.mode
& EDUI_MODE_IPV6
))
1352 edui_conf
.mode
|= EDUI_MODE_IPV4
; /* Don't set mode more than once */
1355 if (!(edui_conf
.mode
& EDUI_MODE_IPV4
) || !(edui_conf
.mode
& EDUI_MODE_IPV6
))
1356 edui_conf
.mode
|= EDUI_MODE_IPV6
; /* Don't set mode more than once */
1359 if (!(edui_conf
.mode
& EDUI_MODE_TLS
))
1360 edui_conf
.mode
|= EDUI_MODE_TLS
; /* Don't set mode more than once */
1363 if (!(edui_conf
.mode
& EDUI_MODE_PERSIST
))
1364 edui_conf
.mode
|= EDUI_MODE_PERSIST
; /* Don't set mode more than once */
1367 ++i
; /* Set LDAP version */
1368 if (argv
[i
] != NULL
) {
1369 edui_conf
.ver
= atoi(argv
[i
]);
1370 if (edui_conf
.ver
< 1)
1372 else if (edui_conf
.ver
> 3)
1375 local_printfx("No parameters given for 'v'.\n");
1381 ++i
; /* Set Persistent timeout */
1382 if (argv
[i
] != NULL
) {
1383 edui_conf
.persist_timeout
= atoi(argv
[i
]);
1384 if (edui_conf
.persist_timeout
< 0)
1385 edui_conf
.persist_timeout
= 0;
1387 local_printfx("No parameters given for 't'.\n");
1393 ++i
; /* Set Base DN */
1394 if (argv
[i
] != NULL
)
1395 xstrncpy(edui_conf
.basedn
, argv
[i
], sizeof(edui_conf
.basedn
));
1397 local_printfx("No parameters given for 'b'.\n");
1403 ++i
; /* Set Hostname */
1404 if (argv
[i
] != NULL
)
1405 xstrncpy(edui_conf
.host
, argv
[i
], sizeof(edui_conf
.host
));
1407 local_printfx("No parameters given for 'H'.\n");
1414 if (argv
[i
] != NULL
)
1415 edui_conf
.port
= atoi(argv
[i
]);
1417 local_printfx("No parameters given for 'p'.\n");
1423 ++i
; /* Set Bind DN */
1424 if (argv
[i
] != NULL
)
1425 xstrncpy(edui_conf
.dn
, argv
[i
], sizeof(edui_conf
.dn
));
1427 local_printfx("No parameters given for 'D'.\n");
1433 ++i
; /* Set Bind PWD */
1434 if (argv
[i
] != NULL
)
1435 xstrncpy(edui_conf
.passwd
, argv
[i
], sizeof(edui_conf
.passwd
));
1437 local_printfx("No parameters given for 'W'.\n");
1443 ++i
; /* Set Search Filter */
1444 if (argv
[i
] != NULL
)
1445 xstrncpy(edui_conf
.search_filter
, argv
[i
], sizeof(edui_conf
.search_filter
));
1447 local_printfx("No parameters given for 'F'.\n");
1453 if (!(edui_conf
.mode
& EDUI_MODE_GROUP
))
1454 edui_conf
.mode
|= EDUI_MODE_GROUP
; /* Don't set mode more than once */
1457 ++i
; /* Set Scope Level */
1458 if (argv
[i
] != NULL
) {
1459 if (!strncmp(argv
[i
], "base", 4))
1460 edui_conf
.scope
= 0;
1461 else if (!strncmp(argv
[i
], "one", 4))
1462 edui_conf
.scope
= 1;
1463 else if (!strncmp(argv
[i
], "sub", 4))
1464 edui_conf
.scope
= 2;
1466 edui_conf
.scope
= 1; /* Default is 'one' */
1468 local_printfx("No parameters given for 's'.\n");
1474 ++i
; /* Set Search Attribute */
1475 if (argv
[i
] != NULL
) {
1476 xstrncpy(edui_conf
.attrib
, argv
[i
], sizeof(edui_conf
.attrib
));
1478 local_printfx("No parameters given for 'u'.\n");
1483 case '-': /* We got a second '-' ... ignore */
1486 local_printfx("Invalid parameter - '%c'.\n", argv
[i
][j
]);
1491 /* Incorrect parameter, display usage */
1498 /* Set predefined required parameters if none are given, localhost:LDAP_PORT, etc */
1499 if (edui_conf
.host
[0] == '\0') /* Default to localhost */
1500 xstrncpy(edui_conf
.host
, "localhost", sizeof(edui_conf
.host
));
1501 if (edui_conf
.port
< 0)
1502 edui_conf
.port
= LDAP_PORT
; /* Default: LDAP_PORT */
1503 if ((edui_conf
.mode
& EDUI_MODE_IPV4
) && (edui_conf
.mode
& EDUI_MODE_IPV6
))
1504 edui_conf
.mode
&= ~(EDUI_MODE_IPV6
); /* Default to IPv4 */
1505 if (edui_conf
.ver
< 0)
1507 if (!(edui_conf
.mode
& EDUI_MODE_TLS
))
1508 edui_conf
.mode
|= EDUI_MODE_TLS
; /* eDirectory requires TLS mode */
1509 if ((edui_conf
.mode
& EDUI_MODE_TLS
) && (edui_conf
.ver
< 3))
1510 edui_conf
.ver
= 3; /* TLS requires version 3 */
1511 if (edui_conf
.persist_timeout
< 0)
1512 edui_conf
.persist_timeout
= 600; /* Default: 600 seconds (10 minutes) */
1513 if (edui_conf
.scope
< 0)
1514 edui_conf
.scope
= 1; /* Default: one */
1515 if (edui_conf
.search_filter
[0] == '\0')
1516 xstrncpy(edui_conf
.search_filter
, "(&(objectclass=User)(networkAddress=*))", sizeof(edui_conf
.search_filter
));
1517 if (edui_conf
.attrib
[0] == '\0')
1518 xstrncpy(edui_conf
.attrib
, "cn", sizeof(edui_conf
.attrib
));
1519 if (edui_conf
.basedn
[0] == '\0') {
1520 local_printfx("FATAL: No '-b' option provided (Base DN).\n");
1524 /* Trap the following signals */
1525 sigemptyset(&sv
.sa_mask
);
1526 sv
.sa_handler
= SigTrap
;
1527 sigaction(SIGTERM
, &sv
, NULL
);
1528 sv
.sa_handler
= SigTrap
;
1529 sigaction(SIGHUP
, &sv
, NULL
);
1530 sv
.sa_handler
= SigTrap
;
1531 sigaction(SIGABRT
, &sv
, NULL
);
1532 sv
.sa_handler
= SigTrap
;
1533 sigaction(SIGINT
, &sv
, NULL
);
1534 sv
.sa_handler
= SigTrap
;
1535 sigaction(SIGSEGV
, &sv
, NULL
);
1538 /* Done with arguments */
1540 /* Set elap timer */
1543 /* Main loop -- Waits for stdin input before action */
1544 while (fgets(bufa
, sizeof(bufa
), stdin
) != NULL
) {
1545 if (edui_conf
.mode
& EDUI_MODE_KILL
)
1549 /* Elapse seconds */
1550 edui_elap
= edui_now
- t
;
1555 /* BINARY DEBUGGING *
1556 local_printfx("while() -> bufa[%" PRIuSIZE "]: %s", k, bufa);
1557 for (i = 0; i < k; ++i)
1558 local_printfx("%02X", bufa[i]);
1559 local_printfx("\n");
1560 * BINARY DEBUGGING */
1561 /* Check for CRLF */
1562 p
= strchr(bufa
, '\n');
1565 p
= strchr(bufa
, '\r');
1568 p
= strchr(bufa
, ' ');
1570 /* No space given, but group string is required --> ERR */
1571 if ((edui_conf
.mode
& EDUI_MODE_GROUP
) && (p
== NULL
)) {
1572 debug("while() -> Search group is missing. (required)\n");
1573 local_printfx("BH message=\"(Search Group Required)\"\n");
1578 /* Open LDAP connection */
1579 if (!(edui_ldap
.status
& LDAP_INIT_S
)) {
1580 InitLDAP(&edui_ldap
);
1581 debug("InitLDAP() -> %s\n", ErrLDAP(LDAP_ERR_SUCCESS
));
1582 if (edui_conf
.mode
& EDUI_MODE_PERSIST
) /* Setup persistent mode */
1583 edui_ldap
.status
|= LDAP_PERSIST_S
;
1585 if ((edui_ldap
.status
& LDAP_IDLE_S
) && (edui_elap
> 0)) {
1586 edui_ldap
.idle_time
= edui_ldap
.idle_time
+ edui_elap
;
1588 if ((edui_ldap
.status
& LDAP_PERSIST_S
) && (edui_ldap
.status
& LDAP_IDLE_S
) && (edui_ldap
.idle_time
> edui_conf
.persist_timeout
)) {
1589 debug("while() -> Connection timed out after %d seconds\n", (int)(edui_ldap
.idle_time
));
1590 x
= CloseLDAP(&edui_ldap
);
1591 debug("CloseLDAP(-) -> %s\n", ErrLDAP(x
));
1594 if (!(edui_ldap
.status
& LDAP_OPEN_S
)) {
1595 x
= OpenLDAP(&edui_ldap
, edui_conf
.host
, edui_conf
.port
);
1596 if (x
!= LDAP_ERR_SUCCESS
) {
1597 /* Failed to connect */
1598 debug("OpenLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1600 debug("OpenLDAP(-, %s, %d) -> %s\n", edui_conf
.host
, edui_conf
.port
, ErrLDAP(x
));
1601 x
= SetVerLDAP(&edui_ldap
, edui_conf
.ver
);
1602 if (x
!= LDAP_ERR_SUCCESS
) {
1603 /* Failed to set version */
1604 debug("SetVerLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1606 debug("SetVerLDAP(-, %d) -> %s\n", edui_conf
.ver
, ErrLDAP(x
));
1610 if (!(edui_ldap
.status
& LDAP_BIND_S
) && (edui_conf
.mode
& EDUI_MODE_TLS
)) {
1612 x
= BindLDAP(&edui_ldap
, edui_conf
.dn
, edui_conf
.passwd
, LDAP_AUTH_TLS
);
1613 if (x
!= LDAP_ERR_SUCCESS
) {
1614 /* Unable to bind */
1615 debug("BindLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1616 local_printfx("BH message=\"(BindLDAP: %s - %s)\"\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1619 debug("BindLDAP(-, %s, %s, (LDAP_AUTH_TLS)) -> %s\n", edui_conf
.dn
, edui_conf
.passwd
, ErrLDAP(x
));
1620 } else if (!(edui_ldap
.status
& LDAP_BIND_S
)) {
1621 if (edui_conf
.dn
[0] != '\0') {
1622 /* Simple binding - using dn / passwd for authorization */
1623 x
= BindLDAP(&edui_ldap
, edui_conf
.dn
, edui_conf
.passwd
, LDAP_AUTH_SIMPLE
);
1624 if (x
!= LDAP_ERR_SUCCESS
) {
1625 /* Unable to bind */
1626 debug("BindLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1627 local_printfx("BH message=\"(BindLDAP: %s - %s)\"\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1630 debug("BindLDAP(-, %s, %s, (LDAP_AUTH_SIMPLE)) -> %s\n", edui_conf
.dn
, edui_conf
.passwd
, ErrLDAP(x
));
1632 /* Anonymous binding */
1633 x
= BindLDAP(&edui_ldap
, edui_conf
.dn
, edui_conf
.passwd
, LDAP_AUTH_NONE
);
1634 if (x
!= LDAP_ERR_SUCCESS
) {
1635 /* Unable to bind */
1636 debug("BindLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1637 local_printfx("BH message=\"(BindLDAP: %s - %s)\"\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1640 debug("BindLDAP(-, -, -, (LDAP_AUTH_NONE)) -> %s\n", ErrLDAP(x
));
1644 if (edui_ldap
.status
& LDAP_PERSIST_S
) {
1645 x
= ResetLDAP(&edui_ldap
);
1646 if (x
!= LDAP_ERR_SUCCESS
) {
1647 /* Unable to reset */
1648 debug("ResetLDAP() -> %s\n", ErrLDAP(x
));
1650 debug("ResetLDAP() -> %s\n", ErrLDAP(x
));
1652 if (x
!= LDAP_ERR_SUCCESS
) {
1653 /* Everything failed --> ERR */
1654 debug("while() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1655 CloseLDAP(&edui_ldap
);
1656 local_printfx("BH message=\"(General Failure: %s)\"\n", ErrLDAP(x
));
1660 /* If we got a group string, split it */
1663 debug("StringSplit(%s, ' ', %s, %" PRIuSIZE
")\n", bufa
, bufb
, sizeof(bufb
));
1664 i
= StringSplit(bufa
, ' ', bufb
, sizeof(bufb
));
1666 debug("StringSplit(%s, %s) done. Result: %" PRIuSIZE
"\n", bufa
, bufb
, i
);
1667 /* Got a group to match against */
1668 x
= ConvertIP(&edui_ldap
, bufb
);
1670 debug("ConvertIP() -> %s\n", ErrLDAP(x
));
1671 local_printfx("BH message=\"(ConvertIP: %s)\"\n", ErrLDAP(x
));
1674 debug("ConvertIP(-, %s) -> Result[%d]: %s\n", bufb
, x
, edui_ldap
.search_ip
);
1675 x
= SearchFilterLDAP(&edui_ldap
, bufa
);
1677 debug("SearchFilterLDAP() -> %s\n", ErrLDAP(x
));
1678 local_printfx("BH message=\"(SearchFilterLDAP: %s)\"\n", ErrLDAP(x
));
1682 debug("SearchFilterLDAP(-, %s) -> Length: %u\n", bufa
, x
);
1683 x
= SearchLDAP(&edui_ldap
, edui_ldap
.scope
, edui_ldap
.search_filter
, (char **) &search_attrib
);
1684 if (x
!= LDAP_ERR_SUCCESS
) {
1685 debug("SearchLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1686 local_printfx("BH message=\"(SearchLDAP: %s)\"\n", ErrLDAP(x
));
1689 debug("SearchLDAP(-, %d, %s, -) -> %s\n", edui_conf
.scope
, edui_ldap
.search_filter
, ErrLDAP(x
));
1690 x
= SearchIPLDAP(&edui_ldap
);
1691 if (x
== LDAP_ERR_NOTFOUND
) {
1692 debug("SearchIPLDAP() -> %s\n", ErrLDAP(x
));
1693 local_printfx("ERR message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x
));
1694 } else if (x
== LDAP_ERR_SUCCESS
) {
1695 debug("SearchIPLDAP(-, %s) -> %s\n", edui_ldap
.userid
, ErrLDAP(x
));
1696 local_printfx("OK user=%s\n", edui_ldap
.userid
); /* Got userid --> OK user=<userid> */
1698 debug("SearchIPLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1699 local_printfx("BH message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x
));
1702 /* Clear for next query */
1703 memset(bufc
, '\0', sizeof(bufc
));
1707 debug("StringSplit() -> Error: %" PRIuSIZE
"\n", i
);
1708 local_printfx("BH message=\"(StringSplit Error %" PRIuSIZE
")\"\n", i
);
1711 /* No group to match against, only an IP */
1712 x
= ConvertIP(&edui_ldap
, bufa
);
1714 debug("ConvertIP() -> %s\n", ErrLDAP(x
));
1715 local_printfx("BH message=\"(ConvertIP: %s)\"\n", ErrLDAP(x
));
1717 debug("ConvertIP(-, %s) -> Result[%d]: %s\n", bufa
, x
, edui_ldap
.search_ip
);
1719 x
= SearchFilterLDAP(&edui_ldap
, NULL
);
1721 debug("SearchFilterLDAP() -> %s\n", ErrLDAP(x
));
1722 local_printfx("BH message=\"(SearchFilterLDAP: %s)\"\n", ErrLDAP(x
));
1725 debug("SearchFilterLDAP(-, NULL) -> Length: %u\n", x
);
1726 x
= SearchLDAP(&edui_ldap
, edui_ldap
.scope
, edui_ldap
.search_filter
, (char **) &search_attrib
);
1727 if (x
!= LDAP_ERR_SUCCESS
) {
1728 debug("SearchLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(x
));
1729 local_printfx("BH message=\"(SearchLDAP: %s)\"\n", ErrLDAP(x
));
1732 debug("SearchLDAP(-, %d, %s, -) -> %s\n", edui_conf
.scope
, edui_ldap
.search_filter
, ErrLDAP(x
));
1733 x
= SearchIPLDAP(&edui_ldap
);
1734 if (x
== LDAP_ERR_NOTFOUND
) {
1735 debug("SearchIPLDAP() -> %s\n", ErrLDAP(x
));
1736 local_printfx("ERR message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x
));
1737 } else if (x
== LDAP_ERR_SUCCESS
) {
1738 debug("SearchIPLDAP(-, %s) -> %s\n", edui_ldap
.userid
, ErrLDAP(x
));
1739 local_printfx("OK user=%s\n", edui_ldap
.userid
); /* Got a userid --> OK user=<userid> */
1740 } else if (x
!= LDAP_ERR_SUCCESS
) {
1741 debug("SearchIPLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x
), ldap_err2string(edui_ldap
.err
));
1742 local_printfx("BH message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x
));
1746 /* Clear for next query */
1747 memset(bufc
, '\0', sizeof(bufc
));
1751 /* Clear buffer and close for next data, if not persistent */
1753 memset(bufa
, '\0', sizeof(bufa
));
1754 if (!(edui_ldap
.status
& LDAP_PERSIST_S
)) {
1755 x
= CloseLDAP(&edui_ldap
);
1756 debug("CloseLDAP(-) -> %s\n", ErrLDAP(x
));
1760 debug("Terminating.\n");
1764 /* "main()" - function */
1766 main(int argc
, char **argv
)
1769 x
= MainSafe(argc
, argv
);