/*
* Copyright (C) 1996-2024 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
* Please see the COPYING and CONTRIBUTORS files for details.
*/
/*
* Copyright (C) 2009-2011 Chad E. Naugle
*
********************************************************************************
*
* This file is part of ext_edirectory_userip_acl.
*
* ext_edirectory_userip_acl is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* ext_edirectory_userip_acl is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with squid_edir_iplookup. If not, see .
*
********************************************************************************
*
* ext_edirectory_userip_acl.cc -- Rev 2011-03-28
*
* - Misc code cleanups using "static", and 64-bit SLES fix for SearchFilterLDAP()
*
*/
/* Squid-3.X includes */
#include "squid.h"
#include "helper/protocol_defines.h"
#include "rfc1738.h"
#include "util.h"
#define EDUI_PROGRAM_NAME "ext_edirectory_userip_acl"
#define EDUI_PROGRAM_VERSION "2.1"
/* System includes */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include
#include
#include
#include
#include
#include
#ifdef HAVE_ARPA_INET_H
#include
#endif
#define LDAP_DEPRECATED 1 /* Set flag for enabling classic ldap functions */
#ifdef HAVE_LBER_H
#include
#endif
#ifdef HAVE_LDAP_H
#include
#endif
#ifdef HAVE_NETDB_H
#include
#endif
#ifdef HAVE_SYS_SOCKET_H
#include
#endif
#ifdef HAVE_NETINET_IN_H
#include
#endif
#ifdef HELPER_INPUT_BUFFER
#define EDUI_MAXLEN HELPER_INPUT_BUFFER
#else
#define EDUI_MAXLEN 4096 /* Modified to improve performance, unless HELPER_INPUT_BUFFER exists */
#endif
/* ldap compile options */
#define USE_LDAP_INIT
#ifndef NETSCAPE_SSL
# define NETSCAPE_SSL
#endif
/* define LDAP_AUTH_TLS
* - ldap.h Hack for cleaner code, if it does not provide it.
*/
#ifdef NETSCAPE_SSL
# ifndef LDAP_AUTH_TLS
# define LDAP_AUTH_TLS ((ber_tag_t) 0xb3U)
# endif
#endif
/* conf_t - status flags */
#define EDUI_MODE_INIT 0x01
#define EDUI_MODE_DEBUG 0x02 /* Replace with Squid's debug system */
#define EDUI_MODE_TLS 0x04
#define EDUI_MODE_IPV4 0x08
#define EDUI_MODE_IPV6 0x10
#define EDUI_MODE_GROUP 0x20 /* Group is REQUIRED */
#define EDUI_MODE_PERSIST 0x40 /* Persistent LDAP connections */
#define EDUI_MODE_KILL 0x80
/* conf_t - Program configuration struct typedef */
typedef struct {
char program[EDUI_MAXLEN];
char basedn[EDUI_MAXLEN];
char host[EDUI_MAXLEN];
char attrib[EDUI_MAXLEN];
char dn[EDUI_MAXLEN];
char passwd[EDUI_MAXLEN];
char search_filter[EDUI_MAXLEN]; /* Base search_filter that gets copied to edui_ldap_t */
int ver;
int scope;
int port;
time_t persist_timeout;
unsigned int mode;
} edui_conf_t;
/* edui_ldap_t - status flags */
#define LDAP_INIT_S 0x0001
#define LDAP_OPEN_S 0x0002
#define LDAP_BIND_S 0x0004
#define LDAP_SEARCH_S 0x0008 /* We got data */
#define LDAP_VAL_S 0x0010 /* Data has been copied to l->val */
#define LDAP_CLOSE_S 0x0020
#define LDAP_PERSIST_S 0x0040 /* Persistent connection */
#define LDAP_IDLE_S 0x0080 /* Connection is idle */
#define LDAP_SSL_S 0x0100
#define LDAP_TLS_S 0x0200
#define LDAP_IPV4_S 0x0400 /* Search IP is IPv4 */
#define LDAP_IPV6_S 0x0800 /* Search IP is IPv6 */
/* edui_ldap_t - Meaningful error codes */
#define LDAP_ERR_NULL -1 /* Null edui_ldap_t pointer */
#define LDAP_ERR_POINTER -2 /* Null l->lp pointer */
#define LDAP_ERR_PARAM -3 /* Null or Missing parameters */
#define LDAP_ERR_INIT -4 /* Not initialized */
#define LDAP_ERR_OPEN -5 /* Not open */
#define LDAP_ERR_CONNECT -6 /* Unable to connect */
#define LDAP_ERR_BIND -7 /* Not bound */
#define LDAP_ERR_SEARCHED -8 /* Already Searched */
#define LDAP_ERR_NOT_SEARCHED -9 /* Not searching */
#define LDAP_ERR_INVALID -10 /* Invalid parameter */
#define LDAP_ERR_OOB -11 /* Out of bounds value */
#define LDAP_ERR_PERSIST -12 /* Persistent mode is not active */
#define LDAP_ERR_DATA -13 /* Required data missing */
#define LDAP_ERR_NOTFOUND -14 /* Item not found */
#define LDAP_ERR_OTHER -15 /* Other Generic Error condition */
#define LDAP_ERR_FAILED -16 /* Operation failed */
#define LDAP_ERR_SUCCESS -17 /* Operation successful */
/* edui_ldap_t - struct typedef */
typedef struct {
LDAP *lp;
LDAPMessage *lm;
struct berval **val;
char basedn[EDUI_MAXLEN];
char host[EDUI_MAXLEN];
char dn[EDUI_MAXLEN];
char passwd[EDUI_MAXLEN];
char search_filter[EDUI_MAXLEN]; /* search_group gets appended here by GroupLDAP */
char search_ip[EDUI_MAXLEN]; /* Could be IPv4 or IPv6, set by ConvertIP */
char userid[EDUI_MAXLEN]; /* Resulting userid */
unsigned int status;
unsigned int port;
unsigned long type; /* Type of bind */
int ver;
int scope;
int err; /* LDAP error code */
time_t idle_time;
int num_ent; /* Number of entry's found via search */
int num_val; /* Number of value's found via getval */
} edui_ldap_t;
/* Global function prototypes */
static void local_printfx(const char *,...);
static int StringSplit(char *, char, char *, size_t);
static int BinarySplit(void *, size_t, char, void *, size_t);
static void DisplayVersion();
static void DisplayUsage();
static void InitConf();
static void DisplayConf();
static void InitLDAP(edui_ldap_t *);
static int OpenLDAP(edui_ldap_t *, char *, unsigned int);
static int CloseLDAP(edui_ldap_t *);
static int SetVerLDAP(edui_ldap_t *, int);
static int BindLDAP(edui_ldap_t *, char *, char *, unsigned int);
static int ConvertIP(edui_ldap_t *, char *);
static int ResetLDAP(edui_ldap_t *);
static int SearchFilterLDAP(edui_ldap_t *, char *);
static int SearchLDAP(edui_ldap_t *, int, char *, char **);
static int SearchIPLDAP(edui_ldap_t *);
const char *ErrLDAP(int);
extern "C" void SigTrap(int);
/* Global variables */
const char *search_attrib[] = { "cn", "uid", "networkAddress", "groupMembership", nullptr };
static edui_conf_t edui_conf;
static edui_ldap_t edui_ldap;
time_t edui_now;
time_t edui_elap;
/* local_printfx() -
*
* Print formatted message to stderr AND stdout, without preformatting.
*
* - Exists as a hack, because SEND_OK() does not appear to work here.
*
*/
static void
local_printfx(const char *msg,...)
{
char prog[EDUI_MAXLEN], dbuf[EDUI_MAXLEN];
size_t sz, x;
va_list ap;
if (edui_conf.program[0] == '\0')
xstrncpy(prog, EDUI_PROGRAM_NAME, sizeof(prog));
else
xstrncpy(prog, edui_conf.program, sizeof(prog));
if (msg == nullptr) {
/* FAIL */
debug("local_printfx() FAILURE, no data.\n");
return;
}
sz = sizeof(dbuf);
va_start(ap, msg);
x = vsnprintf(dbuf, sz, msg, ap);
va_end(ap);
if (x > 0) {
dbuf[x] = '\0';
++x;
fputs(dbuf, stdout);
*(dbuf) = '\0';
} else {
/* FAIL */
debug("local_printfx() FAILURE: %" PRIuSIZE "\n", x);
}
/* stdout needs to be flushed for it to work with Squid */
fflush(stdout);
}
/*
* StringSplit() -
*
* Breaks down string, splitting out element into , and removing it from string.
* Will not exceed size tolerances.
*
*/
static int
StringSplit(char *In_Str, char chr, char *Out_Str, size_t Out_Sz)
{
if ((In_Str == nullptr) || (Out_Str == nullptr))
return (-1);
size_t In_Len = strlen(In_Str) + 1;
// find the char delimiter position...
char *p = In_Str;
while (*p != chr && *p != '\0' && (In_Str+In_Len) > p) {
++p;
}
size_t i = (p-In_Str);
// token to big for the output buffer
if (i >= Out_Sz)
return (-2);
// wipe the unused Out_Obj area
memset(Out_Str+i, 0, Out_Sz-i);
// copy token from In_Str to Out_Str
memcpy(Out_Str, In_Str, i);
// omit the delimiter
if (*p == chr) {
++p;
++i;
} else {
// chr not found (or \0 found first). Wipe whole input buffer.
memset(In_Str, 0, In_Len);
// return (-3);
// Returning <0 breaks current ConvertIP() code for last object
return (i);
}
// move the unused In_Str forward
memmove(In_Str, p, In_Len-i);
// wipe the end of In_Str
memset(In_Str+In_Len-i, 0, i);
return (i-1);
}
/*
* BinarySplit() -
*
* Breaks down Binary Block, splitting out element into , and removing it from Block, padding remainder with '\0'.
* Will not exceed size tolerances.
*
*/
static int
BinarySplit(void *In_Obj, size_t In_Sz, char chr, void *Out_Obj, size_t Out_Sz)
{
// check tolerances
if ((In_Obj == nullptr) || (Out_Obj == nullptr))
return (-1);
char *in = static_cast(In_Obj);
char *out = static_cast(Out_Obj);
// find the char delimiter position...
char *p = static_cast(In_Obj);
while (*p != chr && (in+In_Sz) > p) {
++p;
}
size_t i = (p-in);
// token to big for the output buffer
if (i > Out_Sz)
return (-2);
// wipe the unused Out_Obj area
memset(out+i, 0, Out_Sz-i);
// copy token from In_Obj to Out_Obj
memcpy(Out_Obj, In_Obj, i);
// omit the delimiter
if (*p == chr) {
++p;
++i;
} else {
// chr not found
memset(In_Obj, 0, In_Sz);
// return (-3);
// Returning <0 breaks current code for last object
return (i);
}
// move the unused In_Obj forward
memmove(In_Obj, p, In_Sz-i);
// wipe the end of In_Obj
memset(in+In_Sz-i, 0, i);
return (i-1);
}
/* Displays version information */
static void
DisplayVersion()
{
local_printfx("Squid eDirectory IP Lookup Helper %s. Copyright (C) 2009-2011 Chad E. Naugle\n", EDUI_PROGRAM_VERSION);
}
/* Displays program usage information */
static void
DisplayUsage()
{
DisplayVersion();
local_printfx("\n");
local_printfx("Usage: %s\n", edui_conf.program);
local_printfx(" -H -p [-Z] [-P] [-v 3] -b -s \n");
local_printfx(" -D -W -F [-G] \n\n");
local_printfx(" -d : Debug Mode.\n");
local_printfx(" -4 : Force Addresses to be in IPv4 (127.0.0.1 format).\n");
local_printfx(" -6 : Force Addresses to be in IPv6 (::1 format).\n");
local_printfx(" -H : Specify hostname/ip of server.\n");
local_printfx(" -p : Specify port number. (Range 1-65535)\n");
local_printfx(" -Z : Enable TLS security.\n");
local_printfx(" -P : Use persistent connections.\n");
local_printfx(" -t : Timeout factor for persistent connections. (Default is 60 sec, set to 0 for never timeout)\n");
local_printfx(" -v <1,2,3> : Set LDAP version to 1, 2, or 3.\n");
local_printfx(" -b : Specify Base DN. (ie. \"o=ORG\")\n");
local_printfx(" -s : Specify LDAP Search Scope (base, one, sub; defaults to 'one').\n");
local_printfx(" -D : Specify Binding DN. (ie. cn=squid,o=ORG)\n");
local_printfx(" -W : Specify Binding password.\n");
local_printfx(" -u : Set userid attribute (Defaults to \"cn\").\n");
local_printfx(" -F : Specify LDAP search filter. (ie. \"(objectClass=User)\")\n");
local_printfx(" -G : Specify if LDAP search group is required. (ie. \"groupMembership=\")\n");
local_printfx(" -V : Display version & exit.\n");
local_printfx(" -h : This screen & exit.\n");
local_printfx("\n");
}
/* Initializes program's configuration parameters */
static void
InitConf()
{
*(edui_conf.program) = '\0';
*(edui_conf.basedn) = '\0';
*(edui_conf.host) = '\0';
*(edui_conf.attrib) = '\0';
*(edui_conf.dn) = '\0';
*(edui_conf.passwd) = '\0';
*(edui_conf.search_filter) = '\0';
edui_conf.scope = -1;
edui_conf.ver = -1;
edui_conf.port = -1;
edui_conf.persist_timeout = -1;
edui_conf.mode = 0;
edui_conf.mode |= EDUI_MODE_INIT;
}
/* Displays running configuration */
static void
DisplayConf()
{
if (!(edui_conf.mode & EDUI_MODE_DEBUG))
return;
DisplayVersion();
local_printfx("\n");
local_printfx("Configuration:\n");
local_printfx(" EDUI_MAXLEN: %u\n", EDUI_MAXLEN);
if (edui_conf.mode & EDUI_MODE_DEBUG)
local_printfx(" Debug mode: ON\n");
else
local_printfx(" Debug mode: OFF\n");
if (edui_conf.mode & EDUI_MODE_IPV4)
local_printfx(" Address format: IPv4 (127.0.0.1)\n");
else if (edui_conf.mode & EDUI_MODE_IPV6)
local_printfx(" Address format: IPv6 (::1)\n");
else
local_printfx(" Address format: Not enforced.\n");
if (edui_conf.host[0] != '\0')
local_printfx(" Hostname: %s\n", edui_conf.host);
else
local_printfx(" Hostname: localhost\n");
if (edui_conf.port > 0)
local_printfx(" Port: %d\n", edui_conf.port);
else
local_printfx(" Port: %d\n", LDAP_PORT);
if (edui_conf.mode & EDUI_MODE_TLS)
local_printfx(" TLS mode: ON\n");
else
local_printfx(" TLS mode: OFF\n");
if (edui_conf.mode & EDUI_MODE_PERSIST) {
local_printfx(" Persistent mode: ON\n");
if (edui_conf.persist_timeout > 0)
local_printfx(" Persistent mode idle timeout: %ld\n", static_cast(edui_conf.persist_timeout));
else
local_printfx(" Persistent mode idle timeout: OFF\n");
} else
local_printfx(" Persistent mode: OFF\n");
local_printfx(" LDAP Version: %d\n", edui_conf.ver);
if (edui_conf.basedn[0] != '\0')
local_printfx(" Base DN: %s\n", edui_conf.basedn);
else
local_printfx(" Base DN: None\n");
if (edui_conf.dn[0] != '\0')
local_printfx(" Binding DN: %s\n", edui_conf.dn);
else
local_printfx(" Binding DN: Anonymous\n");
if (edui_conf.passwd[0] != '\0')
local_printfx(" Binding Password: %s\n", edui_conf.passwd);
else
local_printfx(" Binding Password: None\n");
switch (edui_conf.scope) {
case 0:
local_printfx(" Search Scope: base\n");
break;
case 1:
local_printfx(" Search Scope: one level\n");
break;
case 2:
local_printfx(" Search Scope: subtree\n");
break;
default:
local_printfx(" Search Scope: base\n");
break;
}
if (edui_conf.attrib[0] != '\0')
local_printfx(" Search Attribute: %s\n", edui_conf.attrib);
else
local_printfx(" Search Attribute: cn\n");
if (edui_conf.search_filter[0] != '\0')
local_printfx(" Search Filter: %s\n", edui_conf.search_filter);
else
local_printfx(" Search Filter: (&(objectClass=User)(networkAddress=*))\n");
if (edui_conf.mode & EDUI_MODE_GROUP)
local_printfx(" Search Group Required: Yes\n");
else
local_printfx(" Search Group Required: No\n");
local_printfx("\n");
}
/* InitLDAP() -
*
* Initialize LDAP structure for use, zeroing out all variables.
*
*/
static void
InitLDAP(edui_ldap_t *l)
{
if (l == nullptr) return;
l->lp = nullptr;
if (l->lm != nullptr)
ldap_msgfree(l->lm);
if (l->val != nullptr)
ldap_value_free_len(l->val);
l->lm = nullptr;
l->val = nullptr;
*(l->basedn) = '\0';
*(l->host) = '\0';
*(l->dn) = '\0';
*(l->passwd) = '\0';
*(l->search_filter) = '\0';
*(l->userid) = '\0';
memset(l->search_ip, '\0', sizeof(l->search_ip));
l->status = 0;
l->status |= LDAP_INIT_S;
l->port = 0;
l->scope = -1;
l->type = 0;
l->err = -1; /* Set error to LDAP_SUCCESS by default */
l->ver = 0;
l->idle_time = 0;
l->num_ent = 0; /* Number of entries in l->lm */
l->num_val = 0; /* Number of entries in l->val */
/* Set default settings from conf */
if (edui_conf.basedn[0] != '\0')
xstrncpy(l->basedn, edui_conf.basedn, sizeof(l->basedn));
if (edui_conf.host[0] != '\0')
xstrncpy(l->host, edui_conf.host, sizeof(l->host));
if (edui_conf.port != 0)
l->port = edui_conf.port;
if (edui_conf.dn[0] != '\0')
xstrncpy(l->dn, edui_conf.dn, sizeof(l->dn));
if (edui_conf.passwd[0] != '\0')
xstrncpy(l->passwd, edui_conf.passwd, sizeof(l->passwd));
if (edui_conf.search_filter[0] != '\0')
xstrncpy(l->search_filter, edui_conf.search_filter, sizeof(l->search_filter));
if (!(edui_conf.scope < 0))
l->scope = edui_conf.scope;
}
/* OpenLDAP() -
*
* Build LDAP struct with hostname and port, and ready it for binding.
*
*/
static int
OpenLDAP(edui_ldap_t *l, char *h, unsigned int p)
{
if ((l == nullptr) || (h == nullptr)) return LDAP_ERR_NULL;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized, or might be in use */
if (l->status & LDAP_OPEN_S) return LDAP_ERR_OPEN; /* Already open */
if (l->status & LDAP_BIND_S) return LDAP_ERR_BIND; /* Already bound */
xstrncpy(l->host, h, sizeof(l->host));
if (p > 0)
l->port = p;
else
l->port = LDAP_PORT; /* Default is port 389 */
#ifdef NETSCAPE_SSL
if (l->port == LDAPS_PORT)
l->status |= (LDAP_SSL_S | LDAP_TLS_S); /* SSL Port: 636 */
#endif
#ifdef USE_LDAP_INIT
l->lp = ldap_init(l->host, l->port);
#else
l->lp = ldap_open(l->host, l->port);
#endif
if (l->lp == nullptr) {
l->err = LDAP_CONNECT_ERROR;
return LDAP_ERR_CONNECT; /* Unable to connect */
} else {
/* set status */
// l->status &= ~(LDAP_INIT_S);
l->status |= LDAP_OPEN_S;
l->err = LDAP_SUCCESS;
return LDAP_ERR_SUCCESS;
}
}
/* CloseLDAP() -
*
* Close LDAP connection, and clean up data structure.
*
*/
static int
CloseLDAP(edui_ldap_t *l)
{
int s;
if (l == nullptr) return LDAP_ERR_NULL;
if (l->lp == nullptr) return LDAP_ERR_NULL;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Connection not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Connection not open */
if (l->lm != nullptr) {
ldap_msgfree(l->lm);
l->lm = nullptr;
}
if (l->val != nullptr) {
ldap_value_free_len(l->val);
l->val = nullptr;
}
/* okay, so it's open, close it - No need to check other criteria */
s = ldap_unbind(l->lp);
if (s == LDAP_SUCCESS) {
l->status = LDAP_INIT_S;
l->idle_time = 0;
l->err = s; /* Set LDAP error code */
return LDAP_ERR_SUCCESS;
} else {
l->err = s; /* Set LDAP error code */
return LDAP_ERR_FAILED;
}
}
/* SetVerLDAP() -
*
* Set LDAP version number for connection to of 1, 2, or 3
*
*/
static int
SetVerLDAP(edui_ldap_t *l, int v)
{
int x;
if (l == nullptr) return LDAP_ERR_NULL;
if ((v > 3) || (v < 1)) return LDAP_ERR_PARAM;
if (l->lp == nullptr) return LDAP_ERR_POINTER;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Not open */
if (l->status & LDAP_BIND_S) return LDAP_ERR_BIND; /* Already bound */
/* set version */
x = ldap_set_option(l->lp, LDAP_OPT_PROTOCOL_VERSION, &v);
if (x == LDAP_SUCCESS) {
l->ver = v;
l->err = x; /* Set LDAP error code */
return LDAP_ERR_SUCCESS;
} else {
l->err = x; /* Set LDAP error code */
return LDAP_ERR_FAILED;
}
}
/* BindLDAP() -
*
* Bind LDAP connection (Open) using optional dn and password, of
*
*/
static int
BindLDAP(edui_ldap_t *l, char *dn, char *pw, unsigned int t)
{
int s;
if (l == nullptr) return LDAP_ERR_NULL;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Not open */
if (l->status & LDAP_BIND_S) return LDAP_ERR_BIND; /* Already bound */
if (l->lp == nullptr) return LDAP_ERR_POINTER; /* Error */
/* Copy details - dn and pw CAN be NULL for anonymous and/or TLS */
if (dn != nullptr) {
if (strlen(dn) >= sizeof(l->dn))
return LDAP_ERR_OOB; /* DN too large */
if ((l->basedn[0] != '\0') && (strstr(dn, l->basedn) == nullptr)) {
/* We got a basedn, but it's not part of dn */
const int x = snprintf(l->dn, sizeof(l->dn)-1, "%s,%s", dn, l->basedn);
if (x < 0 || static_cast(x) >= sizeof(l->dn))
return LDAP_ERR_OOB; /* DN too large */
} else
xstrncpy(l->dn, dn, sizeof(l->dn));
}
if (pw != nullptr)
xstrncpy(l->passwd, pw, sizeof(l->passwd));
/* Type */
switch (t) {
case LDAP_AUTH_NONE:
l->type = t;
break;
case LDAP_AUTH_SIMPLE:
l->type = t;
break;
case LDAP_AUTH_SASL:
l->type = t;
break;
#ifdef LDAP_AUTH_KRBV4
case LDAP_AUTH_KRBV4:
l->type = t;
break;
#endif
#ifdef LDAP_AUTH_KRBV41
case LDAP_AUTH_KRBV41:
l->type = t;
break;
#endif
#ifdef LDAP_AUTH_KRBV42
case LDAP_AUTH_KRBV42:
l->type = t;
break;
#endif
#ifdef LDAP_AUTH_TLS
case LDAP_AUTH_TLS: /* Added for chicken switch to TLS-enabled without using SSL */
l->type = t;
break;
#endif
default:
l->type = LDAP_AUTH_NONE;
break; /* Default to anonymous bind */
}
/* Bind */
#if defined(LDAP_AUTH_TLS) && defined(NETSCAPE_SSL) && HAVE_LDAP_START_TLS_S
if (l->type == LDAP_AUTH_TLS)
s = ldap_start_tls_s(l->lp, nullptr, nullptr);
else
#endif
s = ldap_bind_s(l->lp, l->dn, l->passwd, l->type);
if (s == LDAP_SUCCESS) {
l->status |= LDAP_BIND_S; /* Success */
l->err = s; /* Set LDAP error code */
return LDAP_ERR_SUCCESS;
} else {
l->err = s; /* Set LDAP error code */
return LDAP_ERR_FAILED;
}
}
// XXX: duplicate (partial) of Ip::Address::lookupHostIp
/**
* Convert the IP address string representation in src to
* its binary representation.
*
* \return binary representation of the src IP address.
* Must be free'd using freeaddrinfo().
*/
static struct addrinfo *
makeIpBinary(const char *src)
{
struct addrinfo want;
memset(&want, 0, sizeof(want));
want.ai_flags = AI_NUMERICHOST; // prevent actual DNS lookups!
struct addrinfo *dst = nullptr;
if (getaddrinfo(src, nullptr, &want, &dst) != 0) {
// not an IP address
/* free any memory getaddrinfo() dynamically allocated. */
if (dst)
freeaddrinfo(dst);
return nullptr;
}
return dst;
}
/**
* Convert srcLen bytes from src into HEX and store into dst, which
* has a maximum content size of dstSize including c-string terminator.
* The dst value produced will be a 0-terminated c-string.
*
* \retval N length of dst written (excluding c-string terminator)
* \retval -11 (LDAP_ERR_OOB) buffer overflow detected
*/
static int
makeHexString(char *dst, const int dstSize, const char *src, const int srcLen)
{
// HEX encoding doubles the amount of bytes/octets copied
if ((srcLen*2) >= dstSize)
return LDAP_ERR_OOB; // cannot copy that many
*dst = 0;
for (int k = 0; k < srcLen; ++k) {
int c = static_cast(src[k]);
if (c < 0)
c = c + 256;
char hexc[4];
const int hlen = snprintf(hexc, sizeof(hexc), "%02X", c);
if (hlen < 0 || static_cast(hlen) > sizeof(hexc)) // should be impossible
return LDAP_ERR_OOB;
strcat(dst, hexc);
}
return strlen(dst);
}
/*
* ConvertIP() -
*
* Take an IPv4 address in dot-decimal or IPv6 notation, and convert to 2-digit HEX stored in l->search_ip
* This is the networkAddress that we search LDAP for.
*/
static int
ConvertIP(edui_ldap_t *l, char *ip)
{
void *y, *z;
if (l == nullptr) return LDAP_ERR_NULL;
if (ip == nullptr) return LDAP_ERR_PARAM;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Not open */
if (!(l->status & LDAP_BIND_S)) return LDAP_ERR_BIND; /* Not bound */
y = memchr((void *)ip, ':', EDUI_MAXLEN);
z = memchr((void *)ip, '.', EDUI_MAXLEN);
if ((y != nullptr) && (z != nullptr)) {
y = nullptr;
z = nullptr;
return LDAP_ERR_INVALID;
}
if ((y != nullptr) && (edui_conf.mode & EDUI_MODE_IPV4)) {
/* IPv4 Mode forced */
return LDAP_ERR_INVALID;
} else if (y != nullptr) {
/* Set IPv6 mode */
if (l->status & LDAP_IPV4_S)
l->status &= ~(LDAP_IPV4_S);
if (!(l->status & LDAP_IPV6_S))
l->status |= (LDAP_IPV6_S);
y = nullptr;
}
if ((z != nullptr) && (edui_conf.mode & EDUI_MODE_IPV6)) {
/* IPv6 Mode forced */
return LDAP_ERR_INVALID;
} else if (z != nullptr) {
/* Set IPv4 mode */
if (l->status & LDAP_IPV6_S)
l->status &= ~(LDAP_IPV6_S);
if (!(l->status & LDAP_IPV4_S))
l->status |= (LDAP_IPV4_S);
z = nullptr;
}
size_t s = LDAP_ERR_INVALID;
if (struct addrinfo *dst = makeIpBinary(ip)) {
if (dst->ai_family == AF_INET6) {
struct sockaddr_in6 *sia = reinterpret_cast(dst->ai_addr);
const char *ia = reinterpret_cast(sia->sin6_addr.s6_addr);
s = makeHexString(l->search_ip, sizeof(l->search_ip), ia, 16); // IPv6 = 16-byte address
} else if (dst->ai_family == AF_INET) {
struct sockaddr_in *sia = reinterpret_cast(dst->ai_addr);
const char *ia = reinterpret_cast(&(sia->sin_addr));
s = makeHexString(l->search_ip, sizeof(l->search_ip), ia, 4); // IPv4 = 4-byte address
} // else leave s with LDAP_ERR_INVALID value
freeaddrinfo(dst);
}
return s;
}
/* ResetLDAP() -
*
* Resets LDAP connection for next search query.
*
*/
static int
ResetLDAP(edui_ldap_t *l)
{
if (l == nullptr) return LDAP_ERR_NULL;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Not open */
if (!(l->status & LDAP_BIND_S)) return LDAP_ERR_BIND; /* Not bound */
if (!(l->status & LDAP_PERSIST_S)) return LDAP_ERR_PERSIST; /* Not persistent */
/* Cleanup data struct */
if (l->status & LDAP_VAL_S)
l->status &= ~(LDAP_VAL_S);
if (l->status & LDAP_SEARCH_S)
l->status &= ~(LDAP_SEARCH_S);
if (l->status & LDAP_IPV4_S)
l->status &= ~(LDAP_IPV4_S);
if (l->status & LDAP_IPV6_S)
l->status &= ~(LDAP_IPV6_S);
if (l->lm != nullptr) {
ldap_msgfree(l->lm);
l->lm = nullptr;
}
if (l->val != nullptr) {
ldap_value_free_len(l->val);
l->val = nullptr;
}
memset(l->search_ip, '\0', sizeof(l->search_ip));
*(l->search_filter) = '\0';
xstrncpy(l->search_filter, edui_conf.search_filter, sizeof(l->search_filter));
*(l->userid) = '\0';
if (!(l->status & LDAP_IDLE_S))
l->status |= LDAP_IDLE_S; /* Set idle mode */
l->num_ent = 0;
l->num_val = 0;
l->err = LDAP_SUCCESS;
return LDAP_ERR_SUCCESS;
}
/*
* SearchFilterLDAP() -
*
* Build LDAP Search Filter string and copy to l->search_filter
*
*/
static int
SearchFilterLDAP(edui_ldap_t *l, char *group)
{
size_t i, j, s;
int swi;
char bufa[EDUI_MAXLEN], bufb[EDUI_MAXLEN], bufc[EDUI_MAXLEN], bufd[EDUI_MAXLEN], bufg[EDUI_MAXLEN];
if (l == nullptr) return LDAP_ERR_NULL;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Not open */
if (!(l->status & LDAP_BIND_S)) return LDAP_ERR_BIND; /* Not Bound */
if (l->search_ip[0] == '\0') return LDAP_ERR_DATA; /* Search IP is required */
/* Zero out if not already */
memset(bufa, '\0', sizeof(bufa));
// using memset() for 'bufa' fixes the 64-bit issue
*(bufb) = '\0';
*(bufc) = '\0';
*(bufd) = '\0';
*(bufg) = '\0';
s = strlen(l->search_ip);
bufc[0] = '\134';
swi = 0;
j = 1;
for (i = 0; i < s; ++i) {
if (swi == 2) {
bufc[j] = '\134';
++j;
bufc[j] = l->search_ip[i];
++j;
swi = 1;
} else {
bufc[j] = l->search_ip[i];
++j;
++swi;
}
}
if (group == nullptr) {
/* No groupMembership= to add, yay! */
/* networkAddress */
if (l->status & LDAP_IPV4_S) {
const int ln = snprintf(bufd, sizeof(bufd), "(networkAddress=8\\23\\00\\00%s)(networkAddress=9\\23\\00\\00%s)", bufc, bufc);
if (ln < 0 || static_cast(ln) >= sizeof(bufd))
return LDAP_ERR_OOB;
} else if (l->status & LDAP_IPV6_S) {
const int ln = snprintf(bufd, sizeof(bufd), "(networkAddress=10\\23\\00\\00%s)(networkAddress=11\\23\\00\\00%s)", bufc, bufc);
if (ln < 0 || static_cast(ln) >= sizeof(bufd))
return LDAP_ERR_OOB;
}
const int x = snprintf(bufa, sizeof(bufa), "(&%s(|(networkAddress=1\\23%s)%s))", edui_conf.search_filter, bufc, bufd);
if (x < 0 || static_cast(x) >= sizeof(bufa))
return LDAP_ERR_OOB;
} else {
/* Needs groupMembership= to add... */
/* groupMembership -- NOTE: Squid *MUST* provide "cn=" from squid.conf */
if ((l->basedn[0] != '\0') && (strstr(group, l->basedn) == nullptr)) {
const int ln = snprintf(bufg, sizeof(bufg), ",%s", l->basedn);
if (ln < 0 || static_cast(ln) >= sizeof(bufd))
return LDAP_ERR_OOB;
}
/* networkAddress */
if (l->status & LDAP_IPV4_S) {
const int ln = snprintf(bufd, sizeof(bufd), "(networkAddress=8\\23\\00\\00%s)(networkAddress=9\\23\\00\\00%s)", bufc, bufc);
if (ln < 0 || static_cast(ln) >= sizeof(bufd))
return LDAP_ERR_OOB;
} else if (l->status & LDAP_IPV6_S) {
const int ln = snprintf(bufd, sizeof(bufd), "(networkAddress=10\\23\\00\\00%s)(networkAddress=11\\23\\00\\00%s)", bufc, bufc);
if (ln < 0 || static_cast(ln) >= sizeof(bufd))
return LDAP_ERR_OOB;
}
const int x = snprintf(bufa, sizeof(bufa), "(&(&%s(groupMembership=%s%s)(|(networkAddress=1\\23%s)%s)))", edui_conf.search_filter, group, bufg, bufc, bufd);
if (x < 0 || static_cast(x) >= sizeof(bufa))
return LDAP_ERR_OOB;
}
s = strlen(bufa);
xstrncpy(l->search_filter, bufa, sizeof(l->search_filter));
return s;
}
/*
* SearchLDAP() -
*
* Initate LDAP query, under levels, filtering matches with and optionally
* will generally be networkAddress ...
*
*/
static int
SearchLDAP(edui_ldap_t *l, int scope, char *filter, char **attrs)
{
int s;
char ft[EDUI_MAXLEN];
if (l == nullptr) return LDAP_ERR_NULL;
if ((scope < 0) || (filter == nullptr)) return LDAP_ERR_PARAM; /* If attrs is NULL, then all attrs will return */
if (l->lp == nullptr) return LDAP_ERR_POINTER;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Not open */
if (!(l->status & LDAP_BIND_S)) return LDAP_ERR_BIND; /* Not bound */
if (l->status & LDAP_SEARCH_S) return LDAP_ERR_SEARCHED; /* Already searching */
if (l->basedn[0] == '\0') return LDAP_ERR_DATA; /* We require a basedn */
if (l->lm != nullptr)
ldap_msgfree(l->lm); /* Make sure l->lm is empty */
xstrncpy(ft, filter, sizeof(ft));
/* We have a binded connection, with a free l->lm, so let's get this done */
switch (scope) {
case 0:
s = ldap_search_s(l->lp, l->basedn, LDAP_SCOPE_BASE, ft, attrs, 0, &(l->lm));
break;
case 1:
s = ldap_search_s(l->lp, l->basedn, LDAP_SCOPE_ONELEVEL, ft, attrs, 0, &(l->lm));
break;
case 2:
s = ldap_search_s(l->lp, l->basedn, LDAP_SCOPE_SUBTREE, ft, attrs, 0, &(l->lm));
break;
default:
/* Only search ONE by default */
s = ldap_search_s(l->lp, l->basedn, LDAP_SCOPE_ONELEVEL, ft, attrs, 0, &(l->lm));
break;
}
if (s == LDAP_SUCCESS) {
l->status |= (LDAP_SEARCH_S); /* Mark as searched */
l->err = s;
l->idle_time = 0; /* Connection in use, reset idle timer */
l->num_ent = ldap_count_entries(l->lp, l->lm); /* Counted */
return LDAP_ERR_SUCCESS;
} else {
l->err = s;
l->num_ent = (-1);
return LDAP_ERR_FAILED;
}
}
/*
* SearchIPLDAP() -
*
* Scan LDAP and get all networkAddress Values, and see if they match l->search_ip
* Actual IP matching routine for eDirectory
*
*/
static int
SearchIPLDAP(edui_ldap_t *l)
{
ber_len_t i, x;
ber_len_t j;
ber_len_t z;
char bufa[EDUI_MAXLEN];
char bufb[EDUI_MAXLEN];
LDAPMessage *ent;
if (l == nullptr) return LDAP_ERR_NULL;
if (l->lp == nullptr) return LDAP_ERR_POINTER;
if (!(l->status & LDAP_INIT_S)) return LDAP_ERR_INIT; /* Not initialized */
if (!(l->status & LDAP_OPEN_S)) return LDAP_ERR_OPEN; /* Not open */
if (!(l->status & LDAP_BIND_S)) return LDAP_ERR_BIND; /* Not bound */
if (!(l->status & LDAP_SEARCH_S)) return LDAP_ERR_NOT_SEARCHED; /* Not searched */
if (l->num_ent <= 0) {
debug("l->num_ent: %d\n", l->num_ent);
return LDAP_ERR_DATA; /* No entries found */
}
if (l->val != nullptr)
ldap_value_free_len(l->val); /* Clear data before populating */
l->num_val = 0;
if (l->status & LDAP_VAL_S)
l->status &= ~(LDAP_VAL_S); /* Clear VAL bit */
if (edui_conf.attrib[0] == '\0')
xstrncpy(edui_conf.attrib, "cn", sizeof(edui_conf.attrib)); /* Make sure edui_conf.attrib is set */
/* Sift through entries */
struct berval **ber = nullptr;
for (ent = ldap_first_entry(l->lp, l->lm); ent != nullptr; ent = ldap_next_entry(l->lp, ent)) {
l->val = ldap_get_values_len(l->lp, ent, "networkAddress");
ber = ldap_get_values_len(l->lp, ent, edui_conf.attrib); /* edui_conf.attrib is the mapping */
if (l->val != nullptr) {
x = ldap_count_values_len(l->val); /* We got x values ... */
l->num_val = x;
if (x > 0) {
/* Display all values */
for (i = 0; i < x; ++i) {
j = l->val[i]->bv_len;
memcpy(bufa, l->val[i]->bv_val, j);
z = BinarySplit(bufa, j, '#', bufb, sizeof(bufb));
/* BINARY DEBUGGING *
local_printfx("value[%" PRIuSIZE "]: BinarySplit(", (size_t) i);
for (k = 0; k < z; ++k) {
c = (int) bufb[k];
if (c < 0)
c = c + 256;
local_printfx("%02X", c);
}
local_printfx(", ");
for (k = 0; k < (j - z - 1); ++k) {
c = (int) bufa[k];
if (c < 0)
c = c + 256;
local_printfx("%02X", c);
}
local_printfx("): %" PRIuSIZE "\n", (size_t) z);
* BINARY DEBUGGING */
z = j - z - 1;
j = atoi(bufb);
if (j == 1) {
/* IPv4 address (eDirectory 8.7 and below) */
/* bufa is the address, just compare it */
if (!(l->status & LDAP_IPV4_S) || (l->status & LDAP_IPV6_S))
break; /* Not looking for IPv4 */
const int blen = makeHexString(bufb, sizeof(bufb), bufa, z);
if (blen < 0)
return blen;
/* Compare value with IP */
if (memcmp(l->search_ip, bufb, blen) == 0) {
/* We got a match! - Scan 'ber' for 'cn' values */
z = ldap_count_values_len(ber);
for (j = 0; j < z; ++j) {
// broken? xstrncpy(l->userid, ber[j]->bv_val, min(sizeof(l->userid),static_cast(ber[j]->bv_len)));
xstrncpy(l->userid, ber[j]->bv_val, sizeof(l->userid));
/* Using bv_len of min() breaks the result by 2 chars */
}
ldap_value_free_len(l->val);
l->val = nullptr;
ldap_value_free_len(ber);
ber = nullptr;
l->num_val = 0;
l->err = LDAP_SUCCESS;
l->status &= ~(LDAP_SEARCH_S);
return LDAP_ERR_SUCCESS; /* We got our userid */
}
/* Not matched, continue */
} else if ((j == 8) || (j == 9)) {
/* IPv4 (UDP/TCP) address (eDirectory 8.8 and higher) */
/* bufa + 2 is the address (skip 2 digit port) */
if (!(l->status & LDAP_IPV4_S) || (l->status & LDAP_IPV6_S))
break; /* Not looking for IPv4 */
const int blen = makeHexString(bufb, sizeof(bufb), &bufa[2], z);
if (blen < 0)
return blen;
/* Compare value with IP */
if (memcmp(l->search_ip, bufb, blen) == 0) {
/* We got a match! - Scan 'ber' for 'cn' values */
z = ldap_count_values_len(ber);
for (j = 0; j < z; ++j) {
// broken? xstrncpy(l->userid, ber[j]->bv_val, min(sizeof(l->userid),static_cast(ber[j]->bv_len)));
xstrncpy(l->userid, ber[j]->bv_val, sizeof(l->userid));
/* Using bv_len of min() breaks the result by 2 chars */
}
ldap_value_free_len(l->val);
l->val = nullptr;
ldap_value_free_len(ber);
ber = nullptr;
l->num_val = 0;
l->err = LDAP_SUCCESS;
l->status &= ~(LDAP_SEARCH_S);
return LDAP_ERR_SUCCESS; /* We got our userid */
}
/* Not matched, continue */
} else if ((j == 10) || (j == 11)) {
/* IPv6 (UDP/TCP) address (eDirectory 8.8 and higher) */
/* bufa + 2 is the address (skip 2 digit port) */
if (!(l->status & LDAP_IPV6_S))
break; /* Not looking for IPv6 */
const int blen = makeHexString(bufb, sizeof(bufb), &bufa[2], z);
if (blen < 0)
return blen;
/* Compare value with IP */
if (memcmp(l->search_ip, bufb, blen) == 0) {
/* We got a match! - Scan 'ber' for 'cn' values */
z = ldap_count_values_len(ber);
for (j = 0; j < z; ++j) {
// broken? xstrncpy(l->userid, ber[j]->bv_val, min(sizeof(l->userid),static_cast(ber[j]->bv_len)));
xstrncpy(l->userid, ber[j]->bv_val, sizeof(l->userid));
/* Using bv_len of min() breaks the result by 2 chars */
}
ldap_value_free_len(l->val);
l->val = nullptr;
ldap_value_free_len(ber);
ber = nullptr;
l->num_val = 0;
l->err = LDAP_SUCCESS;
l->status &= ~(LDAP_SEARCH_S);
return LDAP_ERR_SUCCESS; /* We got our userid */
}
/* Not matched, continue */
}
// else {
/* Others are unsupported */
// }
}
if (ber != nullptr) {
ldap_value_free_len(ber);
ber = nullptr;
}
}
ldap_value_free_len(l->val);
l->val = nullptr;
}
if (ber != nullptr) {
ldap_value_free_len(ber);
ber = nullptr;
}
/* Attr not found, continue */
}
/* No entries found using given attr */
if (l->val != nullptr) {
ldap_value_free_len(l->val);
l->val = nullptr;
}
if (l->lm != nullptr) {
ldap_msgfree(l->lm);
l->lm = nullptr;
}
l->num_ent = 0;
l->num_val = 0;
l->err = LDAP_NO_SUCH_OBJECT;
l->status &= ~(LDAP_SEARCH_S);
return LDAP_ERR_NOTFOUND; /* Not found ... Sorry :) */
}
/*
* ErrLDAP() -
*
* Returns error description of error code
*
*/
const char
*ErrLDAP(int e)
{
switch (e) {
case LDAP_ERR_NULL:
return "Null pointer provided";
case LDAP_ERR_POINTER:
return "Null LDAP pointer";
case LDAP_ERR_PARAM:
return "Null or Missing parameter(s)";
case LDAP_ERR_INIT:
return "LDAP data not initialized";
case LDAP_ERR_OPEN:
return "LDAP connection is not active";
case LDAP_ERR_CONNECT:
return "Unable to connect to LDAP host";
case LDAP_ERR_BIND:
return "LDAP connection is not bound";
case LDAP_ERR_SEARCHED:
return "LDAP connection has already been searched";
case LDAP_ERR_NOT_SEARCHED:
return "LDAP connection has not been searched";
case LDAP_ERR_INVALID:
return "Invalid parameters";
case LDAP_ERR_OOB:
return "Parameter is out of bounds";
case LDAP_ERR_PERSIST:
return "Persistent mode is not active";
case LDAP_ERR_DATA:
return "Required data has not been found";
case LDAP_ERR_NOTFOUND:
return "Item or object has not been found";
case LDAP_ERR_OTHER:
return "An unknown error has occurred";
case LDAP_ERR_FAILED:
return "Operation has failed";
case LDAP_ERR_SUCCESS:
return "Operation is successful";
default:
return "An unknown error has occurred";
}
}
/*
* SigTrap() -
*
* Traps signal codes by number, and gracefully shuts down.
*
*/
extern "C" void
SigTrap(int s)
{
if (!(edui_conf.mode & EDUI_MODE_KILL))
edui_conf.mode |= EDUI_MODE_KILL;
/* Clean Up */
if (edui_ldap.status & LDAP_OPEN_S)
CloseLDAP(&edui_ldap);
debug("Terminating, Signal: %d\n", s);
exit(EXIT_SUCCESS);
}
/*
* MainSafe() -
*
* "Safe" version of main()
*
*/
static int
MainSafe(int argc, char **argv)
{
char bufa[EDUI_MAXLEN], bufb[EDUI_MAXLEN], *p = nullptr;
char bufc[EDUI_MAXLEN];
char sfmod[EDUI_MAXLEN];
int x;
size_t i, j, s, k;
time_t t;
struct sigaction sv;
/* Init */
k = (size_t) argc;
memset(bufa, '\0', sizeof(bufa));
memset(bufb, '\0', sizeof(bufb));
memset(bufc, '\0', sizeof(bufc));
memset(sfmod, '\0', sizeof(sfmod));
memset(&sv, 0, sizeof(sv));
InitConf();
xstrncpy(edui_conf.program, argv[0], sizeof(edui_conf.program));
edui_now = -1;
t = -1;
/* Scan args */
if (k > 1) {
for (i = 1; i < k; ++i) {
/* Classic / novelty usage schemes */
if (!strcmp(argv[i], "--help")) {
DisplayUsage();
return 1;
} else if (!strcmp(argv[i], "--usage")) {
DisplayUsage();
return 1;
} else if (!strcmp(argv[i], "--version")) {
DisplayVersion();
return 1;
} else if (argv[i][0] == '-') {
s = strlen(argv[i]);
for (j = 1; j < s; ++j) {
switch (argv[i][j]) {
case 'h':
DisplayUsage();
return 1;
case 'V':
DisplayVersion();
return 1;
case 'd':
if (!(edui_conf.mode & EDUI_MODE_DEBUG))
edui_conf.mode |= EDUI_MODE_DEBUG; /* Don't set mode more than once */
debug_enabled = 1; /* Official Squid-3 Debug Mode */
break;
case '4':
if (!(edui_conf.mode & EDUI_MODE_IPV4) || !(edui_conf.mode & EDUI_MODE_IPV6))
edui_conf.mode |= EDUI_MODE_IPV4; /* Don't set mode more than once */
break;
case '6':
if (!(edui_conf.mode & EDUI_MODE_IPV4) || !(edui_conf.mode & EDUI_MODE_IPV6))
edui_conf.mode |= EDUI_MODE_IPV6; /* Don't set mode more than once */
break;
case 'Z':
if (!(edui_conf.mode & EDUI_MODE_TLS))
edui_conf.mode |= EDUI_MODE_TLS; /* Don't set mode more than once */
break;
case 'P':
if (!(edui_conf.mode & EDUI_MODE_PERSIST))
edui_conf.mode |= EDUI_MODE_PERSIST; /* Don't set mode more than once */
break;
case 'v':
++i; /* Set LDAP version */
if (argv[i] != nullptr) {
edui_conf.ver = atoi(argv[i]);
if (edui_conf.ver < 1)
edui_conf.ver = 1;
else if (edui_conf.ver > 3)
edui_conf.ver = 3;
} else {
local_printfx("No parameters given for 'v'.\n");
DisplayUsage();
return 1;
}
break;
case 't':
++i; /* Set Persistent timeout */
if (argv[i] != nullptr) {
edui_conf.persist_timeout = atoi(argv[i]);
if (edui_conf.persist_timeout < 0)
edui_conf.persist_timeout = 0;
} else {
local_printfx("No parameters given for 't'.\n");
DisplayUsage();
return 1;
}
break;
case 'b':
++i; /* Set Base DN */
if (argv[i] != nullptr)
xstrncpy(edui_conf.basedn, argv[i], sizeof(edui_conf.basedn));
else {
local_printfx("No parameters given for 'b'.\n");
DisplayUsage();
return 1;
}
break;
case 'H':
++i; /* Set Hostname */
if (argv[i] != nullptr)
xstrncpy(edui_conf.host, argv[i], sizeof(edui_conf.host));
else {
local_printfx("No parameters given for 'H'.\n");
DisplayUsage();
return 1;
}
break;
case 'p':
++i; /* Set port */
if (argv[i] != nullptr)
edui_conf.port = atoi(argv[i]);
else {
local_printfx("No parameters given for 'p'.\n");
DisplayUsage();
return 1;
}
break;
case 'D':
++i; /* Set Bind DN */
if (argv[i] != nullptr)
xstrncpy(edui_conf.dn, argv[i], sizeof(edui_conf.dn));
else {
local_printfx("No parameters given for 'D'.\n");
DisplayUsage();
return 1;
}
break;
case 'W':
++i; /* Set Bind PWD */
if (argv[i] != nullptr)
xstrncpy(edui_conf.passwd, argv[i], sizeof(edui_conf.passwd));
else {
local_printfx("No parameters given for 'W'.\n");
DisplayUsage();
return 1;
}
break;
case 'F':
++i; /* Set Search Filter */
if (argv[i] != nullptr)
xstrncpy(edui_conf.search_filter, argv[i], sizeof(edui_conf.search_filter));
else {
local_printfx("No parameters given for 'F'.\n");
DisplayUsage();
return 1;
}
break;
case 'G':
if (!(edui_conf.mode & EDUI_MODE_GROUP))
edui_conf.mode |= EDUI_MODE_GROUP; /* Don't set mode more than once */
break;
case 's':
++i; /* Set Scope Level */
if (argv[i] != nullptr) {
if (!strncmp(argv[i], "base", 4))
edui_conf.scope = 0;
else if (!strncmp(argv[i], "one", 4))
edui_conf.scope = 1;
else if (!strncmp(argv[i], "sub", 4))
edui_conf.scope = 2;
else
edui_conf.scope = 1; /* Default is 'one' */
} else {
local_printfx("No parameters given for 's'.\n");
DisplayUsage();
return 1;
}
break;
case 'u':
++i; /* Set Search Attribute */
if (argv[i] != nullptr) {
xstrncpy(edui_conf.attrib, argv[i], sizeof(edui_conf.attrib));
} else {
local_printfx("No parameters given for 'u'.\n");
DisplayUsage();
return 1;
}
break;
case '-': /* We got a second '-' ... ignore */
break;
default:
local_printfx("Invalid parameter - '%c'.\n", argv[i][j]);
break;
}
}
} else {
/* Incorrect parameter, display usage */
DisplayUsage();
return 1;
}
}
}
/* Set predefined required parameters if none are given, localhost:LDAP_PORT, etc */
if (edui_conf.host[0] == '\0') /* Default to localhost */
xstrncpy(edui_conf.host, "localhost", sizeof(edui_conf.host));
if (edui_conf.port < 0)
edui_conf.port = LDAP_PORT; /* Default: LDAP_PORT */
if ((edui_conf.mode & EDUI_MODE_IPV4) && (edui_conf.mode & EDUI_MODE_IPV6))
edui_conf.mode &= ~(EDUI_MODE_IPV6); /* Default to IPv4 */
if (edui_conf.ver < 0)
edui_conf.ver = 2;
if (!(edui_conf.mode & EDUI_MODE_TLS))
edui_conf.mode |= EDUI_MODE_TLS; /* eDirectory requires TLS mode */
if ((edui_conf.mode & EDUI_MODE_TLS) && (edui_conf.ver < 3))
edui_conf.ver = 3; /* TLS requires version 3 */
if (edui_conf.persist_timeout < 0)
edui_conf.persist_timeout = 600; /* Default: 600 seconds (10 minutes) */
if (edui_conf.scope < 0)
edui_conf.scope = 1; /* Default: one */
if (edui_conf.search_filter[0] == '\0')
xstrncpy(edui_conf.search_filter, "(&(objectclass=User)(networkAddress=*))", sizeof(edui_conf.search_filter));
if (edui_conf.attrib[0] == '\0')
xstrncpy(edui_conf.attrib, "cn", sizeof(edui_conf.attrib));
if (edui_conf.basedn[0] == '\0') {
local_printfx("FATAL: No '-b' option provided (Base DN).\n");
DisplayUsage();
return 1;
}
/* Trap the following signals */
sigemptyset(&sv.sa_mask);
sv.sa_handler = SigTrap;
sigaction(SIGTERM, &sv, nullptr);
sv.sa_handler = SigTrap;
sigaction(SIGHUP, &sv, nullptr);
sv.sa_handler = SigTrap;
sigaction(SIGABRT, &sv, nullptr);
sv.sa_handler = SigTrap;
sigaction(SIGINT, &sv, nullptr);
sv.sa_handler = SigTrap;
sigaction(SIGSEGV, &sv, nullptr);
DisplayConf();
/* Done with arguments */
/* Set elap timer */
time(&edui_now);
t = edui_now;
/* Main loop -- Waits for stdin input before action */
while (fgets(bufa, sizeof(bufa), stdin) != nullptr) {
if (edui_conf.mode & EDUI_MODE_KILL)
break;
time(&edui_now);
if (t < edui_now) {
/* Elapse seconds */
edui_elap = edui_now - t;
t = edui_now;
} else
edui_elap = 0;
k = strlen(bufa);
/* BINARY DEBUGGING *
local_printfx("while() -> bufa[%" PRIuSIZE "]: %s", k, bufa);
for (i = 0; i < k; ++i)
local_printfx("%02X", static_cast(static_cast(bufa[i])));
local_printfx("\n");
* BINARY DEBUGGING */
/* Check for CRLF */
p = strchr(bufa, '\n');
if (p != nullptr)
*p = '\0';
p = strchr(bufa, '\r');
if (p != nullptr)
*p = '\0';
p = strchr(bufa, ' ');
/* No space given, but group string is required --> ERR */
if ((edui_conf.mode & EDUI_MODE_GROUP) && (p == nullptr)) {
debug("while() -> Search group is missing. (required)\n");
local_printfx("BH message=\"(Search Group Required)\"\n");
continue;
}
x = 0;
/* Open LDAP connection */
if (!(edui_ldap.status & LDAP_INIT_S)) {
InitLDAP(&edui_ldap);
debug("InitLDAP() -> %s\n", ErrLDAP(LDAP_ERR_SUCCESS));
if (edui_conf.mode & EDUI_MODE_PERSIST) /* Setup persistent mode */
edui_ldap.status |= LDAP_PERSIST_S;
}
if ((edui_ldap.status & LDAP_IDLE_S) && (edui_elap > 0)) {
edui_ldap.idle_time = edui_ldap.idle_time + edui_elap;
}
if ((edui_ldap.status & LDAP_PERSIST_S) && (edui_ldap.status & LDAP_IDLE_S) && (edui_ldap.idle_time > edui_conf.persist_timeout)) {
debug("while() -> Connection timed out after %d seconds\n", (int)(edui_ldap.idle_time));
x = CloseLDAP(&edui_ldap);
debug("CloseLDAP(-) -> %s\n", ErrLDAP(x));
}
edui_ldap.err = -1;
if (!(edui_ldap.status & LDAP_OPEN_S)) {
x = OpenLDAP(&edui_ldap, edui_conf.host, edui_conf.port);
if (x != LDAP_ERR_SUCCESS) {
/* Failed to connect */
debug("OpenLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
} else {
debug("OpenLDAP(-, %s, %d) -> %s\n", edui_conf.host, edui_conf.port, ErrLDAP(x));
x = SetVerLDAP(&edui_ldap, edui_conf.ver);
if (x != LDAP_ERR_SUCCESS) {
/* Failed to set version */
debug("SetVerLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
} else
debug("SetVerLDAP(-, %d) -> %s\n", edui_conf.ver, ErrLDAP(x));
}
}
edui_ldap.err = -1;
if (!(edui_ldap.status & LDAP_BIND_S) && (edui_conf.mode & EDUI_MODE_TLS)) {
/* TLS binding */
x = BindLDAP(&edui_ldap, edui_conf.dn, edui_conf.passwd, LDAP_AUTH_TLS);
if (x != LDAP_ERR_SUCCESS) {
/* Unable to bind */
debug("BindLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
local_printfx("BH message=\"(BindLDAP: %s - %s)\"\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
continue;
} else
debug("BindLDAP(-, %s, %s, (LDAP_AUTH_TLS)) -> %s\n", edui_conf.dn, edui_conf.passwd, ErrLDAP(x));
} else if (!(edui_ldap.status & LDAP_BIND_S)) {
if (edui_conf.dn[0] != '\0') {
/* Simple binding - using dn / passwd for authorization */
x = BindLDAP(&edui_ldap, edui_conf.dn, edui_conf.passwd, LDAP_AUTH_SIMPLE);
if (x != LDAP_ERR_SUCCESS) {
/* Unable to bind */
debug("BindLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
local_printfx("BH message=\"(BindLDAP: %s - %s)\"\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
continue;
} else
debug("BindLDAP(-, %s, %s, (LDAP_AUTH_SIMPLE)) -> %s\n", edui_conf.dn, edui_conf.passwd, ErrLDAP(x));
} else {
/* Anonymous binding */
x = BindLDAP(&edui_ldap, edui_conf.dn, edui_conf.passwd, LDAP_AUTH_NONE);
if (x != LDAP_ERR_SUCCESS) {
/* Unable to bind */
debug("BindLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
local_printfx("BH message=\"(BindLDAP: %s - %s)\"\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
continue;
} else
debug("BindLDAP(-, -, -, (LDAP_AUTH_NONE)) -> %s\n", ErrLDAP(x));
}
}
edui_ldap.err = -1;
if (edui_ldap.status & LDAP_PERSIST_S) {
x = ResetLDAP(&edui_ldap);
if (x != LDAP_ERR_SUCCESS) {
/* Unable to reset */
debug("ResetLDAP() -> %s\n", ErrLDAP(x));
} else
debug("ResetLDAP() -> %s\n", ErrLDAP(x));
}
if (x != LDAP_ERR_SUCCESS) {
/* Everything failed --> ERR */
debug("while() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
CloseLDAP(&edui_ldap);
local_printfx("BH message=\"(General Failure: %s)\"\n", ErrLDAP(x));
continue;
}
edui_ldap.err = -1;
/* If we got a group string, split it */
if (p != nullptr) {
/* Split string */
debug("StringSplit(%s, ' ', %s, %" PRIuSIZE ")\n", bufa, bufb, sizeof(bufb));
i = StringSplit(bufa, ' ', bufb, sizeof(bufb));
if (i > 0) {
debug("StringSplit(%s, %s) done. Result: %" PRIuSIZE "\n", bufa, bufb, i);
/* Got a group to match against */
x = ConvertIP(&edui_ldap, bufb);
if (x < 0) {
debug("ConvertIP() -> %s\n", ErrLDAP(x));
local_printfx("BH message=\"(ConvertIP: %s)\"\n", ErrLDAP(x));
} else {
edui_ldap.err = -1;
debug("ConvertIP(-, %s) -> Result[%d]: %s\n", bufb, x, edui_ldap.search_ip);
x = SearchFilterLDAP(&edui_ldap, bufa);
if (x < 0) {
debug("SearchFilterLDAP() -> %s\n", ErrLDAP(x));
local_printfx("BH message=\"(SearchFilterLDAP: %s)\"\n", ErrLDAP(x));
} else {
/* Do Search */
edui_ldap.err = -1;
debug("SearchFilterLDAP(-, %s) -> Length: %u\n", bufa, x);
x = SearchLDAP(&edui_ldap, edui_ldap.scope, edui_ldap.search_filter, (char **) &search_attrib);
if (x != LDAP_ERR_SUCCESS) {
debug("SearchLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
local_printfx("BH message=\"(SearchLDAP: %s)\"\n", ErrLDAP(x));
} else {
edui_ldap.err = -1;
debug("SearchLDAP(-, %d, %s, -) -> %s\n", edui_conf.scope, edui_ldap.search_filter, ErrLDAP(x));
x = SearchIPLDAP(&edui_ldap);
if (x == LDAP_ERR_NOTFOUND) {
debug("SearchIPLDAP() -> %s\n", ErrLDAP(x));
local_printfx("ERR message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x));
} else if (x == LDAP_ERR_SUCCESS) {
debug("SearchIPLDAP(-, %s) -> %s\n", edui_ldap.userid, ErrLDAP(x));
local_printfx("OK user=%s\n", edui_ldap.userid); /* Got userid --> OK user= */
} else {
debug("SearchIPLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
local_printfx("BH message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x));
}
}
/* Clear for next query */
memset(bufc, '\0', sizeof(bufc));
}
}
} else {
debug("StringSplit() -> Error: %" PRIuSIZE "\n", i);
local_printfx("BH message=\"(StringSplit Error %" PRIuSIZE ")\"\n", i);
}
} else {
/* No group to match against, only an IP */
x = ConvertIP(&edui_ldap, bufa);
if (x < 0) {
debug("ConvertIP() -> %s\n", ErrLDAP(x));
local_printfx("BH message=\"(ConvertIP: %s)\"\n", ErrLDAP(x));
} else {
debug("ConvertIP(-, %s) -> Result[%d]: %s\n", bufa, x, edui_ldap.search_ip);
/* Do search */
x = SearchFilterLDAP(&edui_ldap, nullptr);
if (x < 0) {
debug("SearchFilterLDAP() -> %s\n", ErrLDAP(x));
local_printfx("BH message=\"(SearchFilterLDAP: %s)\"\n", ErrLDAP(x));
} else {
edui_ldap.err = -1;
debug("SearchFilterLDAP(-, nullptr) -> Length: %u\n", x);
x = SearchLDAP(&edui_ldap, edui_ldap.scope, edui_ldap.search_filter, (char **) &search_attrib);
if (x != LDAP_ERR_SUCCESS) {
debug("SearchLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(x));
local_printfx("BH message=\"(SearchLDAP: %s)\"\n", ErrLDAP(x));
} else {
edui_ldap.err = -1;
debug("SearchLDAP(-, %d, %s, -) -> %s\n", edui_conf.scope, edui_ldap.search_filter, ErrLDAP(x));
x = SearchIPLDAP(&edui_ldap);
if (x == LDAP_ERR_NOTFOUND) {
debug("SearchIPLDAP() -> %s\n", ErrLDAP(x));
local_printfx("ERR message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x));
} else if (x == LDAP_ERR_SUCCESS) {
debug("SearchIPLDAP(-, %s) -> %s\n", edui_ldap.userid, ErrLDAP(x));
local_printfx("OK user=%s\n", edui_ldap.userid); /* Got a userid --> OK user= */
} else if (x != LDAP_ERR_SUCCESS) {
debug("SearchIPLDAP() -> %s (LDAP: %s)\n", ErrLDAP(x), ldap_err2string(edui_ldap.err));
local_printfx("BH message=\"(SearchIPLDAP: %s)\"\n", ErrLDAP(x));
}
}
}
/* Clear for next query */
memset(bufc, '\0', sizeof(bufc));
}
}
/* Clear buffer and close for next data, if not persistent */
edui_ldap.err = -1;
memset(bufa, '\0', sizeof(bufa));
if (!(edui_ldap.status & LDAP_PERSIST_S)) {
x = CloseLDAP(&edui_ldap);
debug("CloseLDAP(-) -> %s\n", ErrLDAP(x));
}
}
debug("Terminating.\n");
return 1;
}
/* "main()" - function */
int
main(int argc, char **argv)
{
int x;
x = MainSafe(argc, argv);
return x;
}