From aa370a5f6f6469d50791b8e064580870bd3dbc92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20Sch=C3=B6n?= <js@opera.com> Date: Sat, 24 Apr 1999 18:43:30 +0200 Subject: [PATCH] Adding LDAP module Rev: lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1.1 Rev: lib/modules/Protocols.pmod/LDAP.pmod/ldap_errors.h:1.1 Rev: lib/modules/Protocols.pmod/LDAP.pmod/ldap_globals.h:1.1 Rev: lib/modules/Protocols.pmod/LDAP.pmod/ldap_privates.pmod:1.1 Rev: lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike:1.1 --- .gitattributes | 4 + .../Protocols.pmod/LDAP.pmod/client.pike | 936 ++++++++++++++++++ .../Protocols.pmod/LDAP.pmod/ldap_errors.h | 226 +++++ .../Protocols.pmod/LDAP.pmod/ldap_globals.h | 39 + .../LDAP.pmod/ldap_privates.pmod | 259 +++++ .../Protocols.pmod/LDAP.pmod/protocol.pike | 470 +++++++++ 6 files changed, 1934 insertions(+) create mode 100644 lib/modules/Protocols.pmod/LDAP.pmod/client.pike create mode 100644 lib/modules/Protocols.pmod/LDAP.pmod/ldap_errors.h create mode 100644 lib/modules/Protocols.pmod/LDAP.pmod/ldap_globals.h create mode 100644 lib/modules/Protocols.pmod/LDAP.pmod/ldap_privates.pmod create mode 100644 lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike diff --git a/.gitattributes b/.gitattributes index 0c9815343d..6ce5b4fa10 100644 --- a/.gitattributes +++ b/.gitattributes @@ -39,6 +39,10 @@ testfont binary /lib/modules/LR.pmod/scanner.pike foreign_ident /lib/modules/MIME.pmod foreign_ident /lib/modules/Protocols.pmod/Ident.pmod foreign_ident +/lib/modules/Protocols.pmod/LDAP.pmod/client.pike foreign_ident +/lib/modules/Protocols.pmod/LDAP.pmod/ldap_errors.h foreign_ident +/lib/modules/Protocols.pmod/LDAP.pmod/ldap_privates.pmod foreign_ident +/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike foreign_ident /lib/modules/Protocols.pmod/LPD.pmod foreign_ident /lib/modules/Protocols.pmod/Line.pmod foreign_ident /lib/modules/Protocols.pmod/Ports.pmod foreign_ident diff --git a/lib/modules/Protocols.pmod/LDAP.pmod/client.pike b/lib/modules/Protocols.pmod/LDAP.pmod/client.pike new file mode 100644 index 0000000000..d617e341c3 --- /dev/null +++ b/lib/modules/Protocols.pmod/LDAP.pmod/client.pike @@ -0,0 +1,936 @@ +// LDAP client protocol implementation for Pike. +// +// $Id: client.pike,v 1.1 1999/04/24 16:43:28 js Exp $ +// +// Honza Petrous, hop@unibase.cz +// +// ---------------------------------------------------------------------- +// +// History: +// +// v0.0 1998-05-25 Starting up! +// v1.0 1998-06-21 Core functions (open, bind, unbind, delete, add, +// compare, search), only V2 operations, +// some additional restrictions +// v1.1u 1998-07-03 Unfinished version (for testing) +// v1.2a 1998-07-14 New beta code (I love jump over versions like Roxen ;) +// - added asn1_enumerated, asn1_boolean, asn1_nulllist +// and asn1_application_null +// - search op: corrected ASN1 type +// - unbind op: corrected ASN1 type +// v1.2d 1998-08 - added logging of bad LDAP PDU in readmsg +// v1.3 1998-09-18 - resolved "null attribute list" bug in search +// (Now is compatible with MS Exchange ;-) +// v1.9 1999-03 - redesign of LDAP.pmod +// !!! Library uses new ASN1 API => uncompatible !!! +// !!! with Pike 0.5 (needed Pike 0.6 or higher) !!! +// - sliced library code to the several files: +// * LDAP.pmod/protocol.pike +// (low level code) +// * LDAP.pmod/client.pike +// (main code) +// * LDAP.pmod/ldap_errors.h +// (error array) +// * LDAP.pmod/ldap_globals.h +// (global defininitions) +// * LDAP.pmod/ldap_privates.pmod +// (ASN.1 LDAP-related classes and hacks) +// - changed default of 'ldap_scope' to 0 +// - search filter now correctly processed '\(' & '\)' +// [! Still unimplemented escaped conditions chars!] +// +// v1.10 1999-03-28 - moved core to the new 'protocol' code +// 1999-03-28 - rewritten ldap_[op] startup code +// +// v1.11 1999-04-10 - search filter now processed multiple wild '*' chars +// [ Escaping untested, yet ] +// +// Specifications: +// +// RFC 1558 (search filter representations) +// RFC 1777,1778,1779 (version2 spec) +// RFC 1823 (v2 API) +// RFC 2251,2252,2253,2254,2255,2256 (version3 spec) +// draft-ietf-asid-ldap-c-api-00.txt (v3 API) +// RFC2279 (UTF-8) +// +// Interesting, applicable +// RFC 2307 (LDAP as network information services; draft?) + + + +#include "ldap_globals.h" + +#include "ldap_errors.h" + +// ------------------------ + +// ASN.1 decode macros +#define ASN1_DECODE_RESULTAPP(X) (.ldap_privates.ldap_der_decode(X)->elements[1]->get_tag()) +#define ASN1_DECODE_RESULTCODE(X) (int)(.ldap_privates.ldap_der_decode(X)->elements[1]->elements[0]->value->cast_to_int()) +#define ASN1_DECODE_RESULTSTRING(X) (.ldap_privates.ldap_der_decode(X)->elements[1]->elements[1]->value) +#define ASN1_DECODE_ENTRIES(X) _New_decode(X) +#define ASN1_DECODE_DN(X) (string)(.ldap_privates.ldap_der_decode(X)->elements[1]->elements[0]->value) +#define ASN1_DECODE_RAWDEBUG(X) (.ldap_privates.ldap_der_decode(X)->debug_string()) +#define ASN1_GET_ATTR_ARRAY(X) (array)(.ldap_privates.ldap_der_decode(X)->elements[1]->elements[1]->elements[1..]) +#define ASN1_GET_ATTR_NAME(X) ((X)->elements[0]->value) + + inherit .protocol; + + private int binded = 0; // flag for v2 operations + private string ldap_basedn = ""; // baseDN + private int ldap_scope = 0; // 0: base, 1: onelevel, 2: subtree + private int ldap_deref = 0; // 0: ... + private int ldap_sizelimit = 0; + private int ldap_timelimit = 0; + + + + class result // ------------------ + { + + private int resultcode = LDAP_SUCCESS; + //private string resultstring = LDAP_SUCCESS_STR; + private int entrycnt = 0; + private int actnum = 0; + private array(mapping(string:array(string))) entry = ({}); + + int error_number() { return(resultcode); } + + string error_string() { return(ldap_errlist[resultcode]); } + + int num_entries() { return(entrycnt); } + + int count_entries() { return(entrycnt - actnum); } + + + private array _get_attr_values(object x) { + + array res = ({}); + + if(!sizeof(x->elements)) + return(res); + foreach(x->elements[1]->elements, object val1) + res += ({ val1->value }); + return(res); + } + + private array _New_decode(array ar) { + + array res = ({}); + array entry1; + mapping attrs; + + foreach(ar, string raw1) { + attrs = (["dn":({ASN1_DECODE_DN(raw1)})]); + entry1 = ASN1_GET_ATTR_ARRAY(raw1); + foreach(entry1, object attr1) { + attrs += ([ASN1_GET_ATTR_NAME(attr1):_get_attr_values(attr1)]); + } + res += ({attrs}); + } + return (res); + } // _New_decode + + object|int create(array rawres, int|void stuff) { + // rawres: array of result in raw format, but WITHOUT LDAP PDU !!! + // stuff: 1=bind result; ... + + int lastel = sizeof(rawres) - 1; + + if (lastel < 0) { + ::seterr (LDAP_LOCAL_ERROR); + THROW(({"LDAP: Internal error.\n",backtrace()})); + return(-::ldap_errno); + } + //DWRITE(sprintf("DEB: lastel=%d\n",lastel)); + DWRITE(sprintf("result.create: rawres=%O\n",rawres[lastel])); + + // The last element of 'rawres' is result itself + resultcode = ASN1_DECODE_RESULTCODE(rawres[lastel]); + DWRITE(sprintf("result.create: code=%d\n",resultcode)); + #if 0 + resultstring = ASN1_DECODE_RESULTSTRING(rawres[lastel]); + DWRITE(sprintf("result.create: resstr=%s\n",resultstring)); + #endif + // referral (v3 mode) + // ... + DWRITE(sprintf("result.create: elements=%d\n",lastel+1)); + if (lastel) { // Have we any entry? + entry = ASN1_DECODE_ENTRIES(rawres[..lastel-1]); + entrycnt = sizeof(entry); //num_entries(); + } + +#if 0 + // Context specific proccessing of 'rawres' + switch(stuff) { + case 1: DWRITE("result.create: stuff=1\n"); + break; + default: DWRITE(sprintf("result.create: stuff=%d\n", stuff)); + + } +#endif + + return(this_object()); + + } // create + + int|mapping(string:array(string)) fetch(int|void ix) { + + if (!ix) + ix = actnum + 1; + if ((ix <= num_entries()) && (ix > 0)) { + actnum = ix - 1; + return(entry[actnum]); + } + return(0); + } + + string get_dn() { return(fetch()["dn"][0]); } + + void first() { actnum = 0; } + + int next() { + if (actnum < (num_entries()-1)) { + actnum++; + return(count_entries()); + } + return(0); + } + + } // end of class 'result' --------------- + + // helper functions + + private int chk_ver() { + + if ((ldap_version != 2) && (ldap_version != 3)) { + seterr (LDAP_PROTOCOL_ERROR); + THROW(({"LDAP: Unknown/unsupported protocol version.\n",backtrace()})); + return(-ldap_errno); + } + return(0); + } + + private int chk_binded() { + // For version 2: we must be 'binded' first !!! + + if ((ldap_version == 2) && !binded) { + seterr (LDAP_PROTOCOL_ERROR); + THROW(({"LDAP: Must binded first.\n",backtrace()})); + return(-ldap_errno); + } + return(0); + } + + private int chk_dn(string dn) { + + if ((!dn) || (!sizeof(dn))) { + seterr (LDAP_INVALID_DN_SYNTAX); + THROW(({"LDAP: Invalid DN syntax.\n",backtrace()})); + return(-ldap_errno); + } + return(0); + } + + // API function (ldap_open) + // + // create(string|void server) + // + // server: server name (hostname or IP, default: 127.0.0.1) + // with optional port number (default: 389) + void create(string|void server) + { + int port = LDAP_DEFAULT_PORT; + + if(!server || !sizeof(server)) + server = (string)LDAP_DEFAULT_HOST; + else + if(predef::search(server,":")>0) { + port = (int)((server / ":")[1]); + server = (server / ":")[0]; + } + + ::create(server, port); + if(!::connected) + { + THROW(({"Failed to connect to LDAP server.\n",backtrace()})); + } + DWRITE(sprintf("client.create: remote = %s\n", query_address())); + DWRITE_HI("client.OPEN: " + server + " - OK\n"); + + binded = 0; + + } // create + + private mixed send_bind_op(string name, string password) { + // Simple BIND operation + + object msgval, vers, namedn, auth, app; + + vers = Standards.ASN1.Types.asn1_integer(ldap_version); + namedn = Standards.ASN1.Types.asn1_octet_string(name); + auth = ASN1_CONTEXT_OCTET_STRING(0, password); + // SASL credentials ommited + + msgval = ASN1_APPLICATION_SEQUENCE(0, ({vers, namedn, auth})); + + return (do_op(msgval)); + } + + // API function (ldap_bind) + // + // bind(string|void name, string|void password, int|void proto) + // + // name: + // password: + // proto: protocol version, supported 2 and 3 + int bind (string|void name, string|void password, int|void proto) { + + int id; + mixed raw; + object rv; + + if (!proto) + proto = LDAP_DEFAULT_VERSION; + if (chk_ver()) + return(-ldap_errno); + if (!stringp(name)) + name = ""; + if (!stringp(password)) + password = ""; + ldap_version = proto; +#if UTF8_SUPPORT + if(ldap_version == 3) { + dn = Standards.Unicode.UTF8.encode(dn, charset); + password = Standards.Unicode.UTF8.encode(password, charset); + } +#endif + if(intp(raw = send_bind_op(name, password))) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + + rv = result(({raw}),1); + if (!rv->error_number()) + binded = 1; + DWRITE_HI(sprintf("client.BIND: %s\n", rv->error_string())); + return (seterr (rv->error_number())); + + } // bind + + private int send_unbind_op() { + // UNBIND operation + + writemsg(ASN1_APPLICATION_OCTET_STRING(2, "")); + + //ldap::close(); + + return (1); + } + + void destroy() { + + //send_unbind_op(); + destruct(this_object()); + } + + // API function (ldap_unbind) + // + // unbind() + // + int unbind () { + + if (send_unbind_op() < 1) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + binded = 0; + DWRITE_HI("client.UNBIND: OK\n"); + + } // unbind + + private int|string send_op_withdn(int op, string dn) { + // DELETE, ... + + return (do_op(ASN1_APPLICATION_OCTET_STRING(op, dn))); + + } + + // API function (ldap_delete) + // + // delete(string dn) + // + // dn: DN of deleted object + int delete (string dn) { + + int id; + mixed raw; + object rv; + + if (chk_ver()) + return(-ldap_errno); + if (chk_binded()) + return(-ldap_errno); + if (chk_dn(dn)) + return(-ldap_errno); +#if UTF8_SUPPORT + if(ldap_version == 3) { + dn = Standards.Unicode.UTF8.encode(dn, charset); + } +#endif + if(intp(raw = send_op_withdn(10, dn))) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + + rv = result(({raw})); + DWRITE_HI(sprintf("client.DELETE: %s\n", rv->error_string())); + return (seterr (rv->error_number())); + + } // delete + + private int|string send_compare_op(string dn, array(string) aval) { + // COMPARE + + object msgval; + + msgval = ASN1_APPLICATION_SEQUENCE(14, + ({ Standards.ASN1.Types.asn1_octet_string(dn), + Standards.ASN1.Types.asn1_sequence( + ({ Standards.ASN1.Types.asn1_octet_string(aval[0]), + Standards.ASN1.Types.asn1_octet_string(aval[1]) + })) + }) + ); + + return (do_op(msgval)); + } + + + // API function (ldap_compare) + // + // compare(string dn, array(string) aval) + // + // dn: DN of compared object + // aval: attribute value + int compare (string dn, array(string) aval) { + + int id; + mixed raw; + object rv; + + // if (!aval || sizeof(aval)<2) + // error + if (chk_ver()) + return(-ldap_errno); + if (chk_binded()) + return(-ldap_errno); + if (chk_dn(dn)) + return(-ldap_errno); +#if UTF8_SUPPORT + if(ldap_version == 3) { + dn = Standards.Unicode.UTF8.encode(dn, charset); + aval = Standards.Unicode.UTF8.encode(aval, charset); + } +#endif + if(intp(raw = send_compare_op(dn, aval))) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + + rv = result(({raw})); + DWRITE_HI(sprintf("client.COMPARE: %s\n", rv->error_string())); + return (seterr (rv->error_number())); + + } // compare + + private int|string send_add_op(string dn, mapping(string:array(string)) attrs) { + // ADD + + object msgval; + string atype; + array(object) oatt = ({}); + + foreach(indices(attrs), atype) { + string aval; + array(object) ohlp = ({}); + + foreach(values(attrs[atype]), aval) + ohlp += ({Standards.ASN1.Types.asn1_octet_string(aval)}); + oatt += ({Standards.ASN1.Types.asn1_sequence( + ({Standards.ASN1.Types.asn1_octet_string(atype), + Standards.ASN1.Types.asn1_set(ohlp) + })) + }); + } + + msgval = ASN1_APPLICATION_SEQUENCE(8, + ({Standards.ASN1.Types.asn1_octet_string(dn), + Standards.ASN1.Types.asn1_sequence(oatt) + })); + + return (do_op(msgval)); + } + + + // API function (ldap_add) + // + // add(string dn, mapping(string:array(string)) attrs) + // + // dn: DN of compared object + // ttrs: attribute(s) and value + int add (string dn, mapping(string:array(string)) attrs) { + + int id; + mixed raw; + object rv; + + if (chk_ver()) + return(-ldap_errno); + if (chk_binded()) + return(-ldap_errno); + if (chk_dn(dn)) + return(-ldap_errno); +#if UTF8_SUPPORT + if(ldap_version == 3) { + dn = Standards.Unicode.UTF8.encode(dn, charset); + attrs = Standards.Unicode.UTF8.encode(attrs, charset); + } +#endif + if(intp(raw = send_add_op(dn, attrs))) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + + rv = result(({raw})); + DWRITE_HI(sprintf("client.ADD: %s\n", rv->error_string())); + return (seterr (rv->error_number())); + + } // add + + private static array(string) filter_get_sub1expr(string fstr) { + // returns one-level brackets enclosed expressions + + array(string) rvarr = ({}); + int leftflg = 0, nskip = 0; + + for(int ix=0; ix<sizeof(fstr); ix++) + if((fstr[ix] == '(') && (!ix || (fstr[ix-1] != '\\'))) { + leftflg = ix+1; + while(++ix < sizeof(fstr)) { + if((fstr[ix] == '(') && (fstr[ix-1] != '\\')) { + nskip++; // deeper expr. + continue; + } + if((fstr[ix] == ')') && (fstr[ix-1] != '\\')) + if(nskip) { // we are deeply? + nskip--; + continue; + } else { // ok, here is end of expr. + rvarr += ({fstr[leftflg..(ix-1)]}); + leftflg = 0; + break; + } + } // while + } + if(leftflg) { + ; // silent error: Missed right-enclosed bracket ! + } + //DWRITE(sprintf("client.sub1: arr=%O\n",rvarr)); + return(rvarr); +} + + /*private*/ object make_simple_filter(string filter) { + // filter expression parser - only simple expressions! + + object rv; + int op, ix; + + DWRITE(sprintf("client.make_simple_filter: filter: [%s]\n", filter)); + if((op = predef::search(filter, ">=")) > 0) { // greater or equal + DWRITE("client.make_simple_filter: [>=]\n"); + return(ASN1_CONTEXT_SEQUENCE(5, + ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), + Standards.ASN1.Types.asn1_octet_string(filter[(op+2)..]) + }))); + } + if((op = predef::search(filter, "<=")) > 0) { // less or equal + DWRITE("client.make_simple_filter: [<=]\n"); + return(ASN1_CONTEXT_SEQUENCE(6, + ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), + Standards.ASN1.Types.asn1_octet_string(filter[(op+2)..]) + }))); + } + if((op = predef::search(filter, "=")) > 0) { // equal, substring + if((filter[-2] == '=') && (filter[-1] == '*')) // any (=*) + return(make_simple_filter(filter[..op-1])); + ix = predef::search(filter[(op+1)..], "*"); + if ((ix != -1) && (filter[op+ix] != '\\')) { // substring + object ohlp; + array oarr = ({}), ahlp = ({}); + array filtval = filter[(op+1)..] / "*"; + + // escape processing + for(int cnt = 0; cnt < sizeof(filtval); cnt++) { + if(cnt) { + if(sizeof(filtval[cnt-1]) && filtval[cnt-1][-1] == '\\') + ahlp[-1] = reverse(reverse(ahlp[-1])[1..]) + filtval[cnt]; + else + ahlp += ({ filtval[cnt] }); + } else + ahlp = ({ filtval[cnt] }); + } // for + + // filter elements processing (left, center & right) + ix = sizeof(ahlp); + for (int cnt = 0; cnt < ix; cnt++) + if(!cnt) { // leftmost element + if(sizeof(ahlp[0])) + oarr = ({ASN1_CONTEXT_OCTET_STRING(0, ahlp[0])}); + } else + if(cnt == ix-1) { // rightmost element + if(sizeof(ahlp[ix-1])) + oarr += ({ASN1_CONTEXT_OCTET_STRING(2, ahlp[ix-1])}); + } else { // inside element + if(sizeof(ahlp[cnt])) + oarr += ({ASN1_CONTEXT_OCTET_STRING(1, ahlp[cnt])}); + } + // for + + DWRITE(sprintf("client.make_simple_filter: substring: [%s]:\n%O\n", filter, ahlp)); + return(ASN1_CONTEXT_SEQUENCE(4, + ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), + Standards.ASN1.Types.asn1_sequence(oarr) + }))); + } else { // equal + DWRITE("client.make_simple_filter: [=]\n"); + return(ASN1_CONTEXT_SEQUENCE(3, + ({Standards.ASN1.Types.asn1_octet_string(filter[..(op-1)]), + Standards.ASN1.Types.asn1_octet_string(filter[(op+1)..]) + }))); + } + } // if equal,substring + // present + DWRITE("client.make_simple_filter: [present]\n"); + return(ASN1_CONTEXT_OCTET_STRING(7, filter)); +} + + + /*private*/ object|int make_filter(string filter) { + // filter expression parser + + object ohlp; + array(object) oarr = ({}); + int op ; + + DWRITE("client.make_filter: filter=["+filter+"]\n"); + // strip leading and trailing spaces + while(filter[0] == ' ') + filter = filter[1..]; + while(filter[-1] == ' ') + filter = reverse(reverse(filter)[1..]); + + // strip leading and trailing brackets +#if 1 + if(filter[0] == '(') { + int ix; + string f2 = reverse(filter[1..]); + if((ix = predef::search(f2, ")")) > -1) { + filter = reverse(f2[ix+1..]); +//DWRITE("DEB: make_filter: filter=["+filter+"]\n"); + return(make_filter(filter)); + } + return(-1); // error in filter expr. + } +#endif + + op = -1; + + DWRITE(sprintf("client.make_filter: ftype=%c\n",filter[0])); + switch (filter[0]) { + case '&': // and + case '|': // or + foreach(filter_get_sub1expr(filter[1..]), string sub1expr) + if (objectp(ohlp = make_filter(sub1expr))) + oarr += ({ohlp}); + else + return(0); // error: Filter parameter error! + DWRITE(sprintf("client.make_filter: expr_cnt=%d\n",sizeof(oarr))); + //ohlp = Standards.ASN1.Encode.asn1_set(@oarr); + op = 0; + if (filter[0] == '|') + op = 1; + return(ASN1_CONTEXT_SET(op, oarr)); + case '!': // not + if (objectp(ohlp = make_filter(filter_get_sub1expr(filter[1..])[0]))) + return(ASN1_CONTEXT_SEQUENCE(2, ohlp)); + else + return(0); // error: Filter parameter error! + break; + default : // we assume simple filter + return(make_simple_filter(filter)); + } +} + + private int|string send_search_op(string basedn, int scope, int deref, + int sizelimit, int timelimit, int attrsonly, + string filter, void|array(string) attrs){ + // SEARCH + // limitations: !!! sizelimit and timelimit should be unsigned int !!! + + object msgval, ofilt; + array(object) ohlp; + + if(!objectp(ofilt = make_filter(filter))) { + return(-seterr(LDAP_FILTER_ERROR)); + } + ohlp = ({ofilt}); + if (arrayp(attrs)) { //explicitly defined attributes + array(object) o2 = ({}); + //DWRITE(sprintf("DEB: attrs=%O\n",attrs)); + foreach(attrs, string s2) + o2 += ({Standards.ASN1.Types.asn1_octet_string(s2)}); + ohlp += ({Standards.ASN1.Types.asn1_sequence(o2)}); + } else + ohlp += ({Standards.ASN1.Types.asn1_sequence(({}))}); + + msgval = ASN1_APPLICATION_SEQUENCE(3, + ({ Standards.ASN1.Types.asn1_octet_string(basedn), + ASN1_ENUMERATED(scope), + ASN1_ENUMERATED(deref), + Standards.ASN1.Types.asn1_integer(sizelimit), + Standards.ASN1.Types.asn1_integer(timelimit), + ASN1_BOOLEAN(attrsonly ? -1 : 0), + @ohlp + })) ; + + return (do_op(msgval)); + } + + + // API function (ldap_search) + // + // search(string filter, int|void attrsonly, array(string)|void attrs) + // + // filter: search filter + // attrsonly: flag + // attrsy: attribute(s) name + object|int search (string filter, int|void attrsonly, array(string)|void attrs) { + + int id; + mixed raw; + array(string) rawarr = ({}); + mixed rv; + + DWRITE_HI("client.SEARCH: " + (string)filter + "\n"); + if (chk_ver()) + return(-ldap_errno); + if (chk_binded()) + return(-ldap_errno); +#if UTF8_SUPPORT + if(ldap_version == 3) { + filter = Standards.Unicode.UTF8.encode(filter, charset); + } +#endif + if(intp(raw = send_search_op(ldap_basedn, ldap_scope, ldap_deref, + ldap_sizelimit, ldap_timelimit, attrsonly, filter, + attrs))) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + + rawarr = ({raw}); + while (ASN1_DECODE_RESULTAPP(raw) != 5) { + raw = readmsg(id); + if (intp(raw)) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + if((ASN1_DECODE_RESULTAPP(raw) == 4) || (ASN1_DECODE_RESULTAPP(raw) == 5)) + rawarr += ({raw}); + else { + DWRITE(sprintf("client.search: APP[%d]\n",ASN1_DECODE_RESULTAPP(raw))); + // in version 3 - referrals + } + } // while + + rv = result(rawarr); + if(objectp(rv)) + seterr (rv->error_number()); + //if (rv->error_number() || !rv->num_entries()) // if error or entries=0 + // rv = rv->error_number(); + + DWRITE_HI(sprintf("client.SEARCH: %s (entries: %d)\n", rv->error_string(), rv->num_entries())); + return(rv); + + } // search + + + // API function (ldap_setbasedn) + // + // set_basedn(string base_dn) + // + // base_dn: base DN for search + string set_basedn (string base_dn) { + + string old_dn = ldap_basedn; + +#if UTF8_SUPPORT + if(ldap_version == 3) { + base_dn = Standards.Unicode.UTF8.encode(base_dn, charset); + } +#endif + ldap_basedn = base_dn; + DWRITE_HI("client.SET_BASEDN = " + base_dn + "\n"); + return(old_dn); + } + + // API function (ldap_setscope) + // + // set_scope(string base_dn) + // + // scope: scope; 0: base, 1: onelevel, 2: subtree + int set_scope (int scope) { + + int old_scope = ldap_scope; + + ldap_scope = scope; + DWRITE_HI("client.SET_SCOPE = " + (string)scope + "\n"); + return(old_scope); + } + + // API function (ldap_setoption) + // + // set_option(int option_type, mixed value) + // + // option_type: LDAP_OPT_xxx + // value: new value for option + int set_option (int opttype, int value) { + + DWRITE_HI("client.SET_OPTION: " + (string)opttype + " = " + (string)value + "\n"); + switch (opttype) { + case 1: // LDAP_OPT_DEREF + //if (intp(value)) + ldap_deref = value; + //else + // return(-1); + break; + case 2: // LDAP_OPT_SIZELIMIT + //if (intp(value)) + ldap_sizelimit = value; + //else + // return(-1); + break; + case 3: // LDAP_OPT_TIMELIMIT + //if (intp(value)) + ldap_timelimit = value; + //else + // return(-1); + break; + case 4: // LDAP_OPT_REFERRALS + default: return(-1); + } + + + return(0); + } + + // API function (ldap_getoption) + // + // get_option(int option_type) + // + // option_type: LDAP_OPT_xxx + int get_option (int opttype) { + + + DWRITE_HI("client.GET_OPTION: " + (string)opttype + "\n"); + switch (opttype) { + case 1: // LDAP_OPT_DEREF + return(ldap_deref); + case 2: // LDAP_OPT_SIZELIMIT + return(ldap_sizelimit); + case 3: // LDAP_OPT_TIMELIMIT + return(ldap_timelimit); + case 4: // LDAP_OPT_REFERRALS + } + + return(-1); + } + + + private int|string send_modify_op(string dn, + mapping(string:array(mixed)) attropval) { + // MODIFY + + object o, msgval; + string atype; + array(object) oatt = ({}), attrarr; + + + foreach(indices(attropval), atype) { + if(!intp((attropval[atype])[0])) + return(seterr (LDAP_PROTOCOL_ERROR)); + attrarr = ({}); + for(int ix=1; ix<sizeof(attropval[atype]); ix++) + attrarr += ({Standards.ASN1.Types.asn1_octet_string( + (attropval[atype])[ix])}); +// if(sizeof(attrarr)) // attributevalue ? + o = Standards.ASN1.Types.asn1_sequence( + ({Standards.ASN1.Types.asn1_octet_string(atype), + Standards.ASN1.Types.asn1_set(attrarr) + })); +// else +// o = Standards.ASN1.Encode.asn1_sequence( +// Standards.ASN1.Encode.asn1_octet_string(atype)); + oatt += ({Standards.ASN1.Types.asn1_sequence( + ({ASN1_ENUMERATED((attropval[atype])[0]), + o + }))}); + } //foreach + + msgval = ASN1_APPLICATION_SEQUENCE(6, + ({ Standards.ASN1.Types.asn1_octet_string(dn), + Standards.ASN1.Types.asn1_sequence(oatt) + })); + + return (do_op(msgval)); + } + + + // API function (ldap_modify) + // + // modify(string dn, mapping(string:array(mix)) attropval) + // + // dn: DN of compared object + // attropval: attribute(s), operation and value(s) + int modify (string dn, mapping(string:array(mixed)) attropval) { + + int id; + mixed raw; + object rv; + + if (chk_ver()) + return(-ldap_errno); + if (chk_binded()) + return(-ldap_errno); + if (chk_dn(dn)) + return(-ldap_errno); +#if UTF8_SUPPORT + if(ldap_version == 3) { + dn = Standards.Unicode.UTF8.encode(dn, charset); + attrs = Standards.Unicode.UTF8.encode(attropval, charset); + } +#endif + if(intp(raw = send_modify_op(dn, attropval))) { + THROW(({error_string()+"\n",backtrace()})); + return(-ldap_errno); + } + + rv = result(({raw})); + DWRITE_HI(sprintf("client.MODIFY: %s\n", rv->error_string())); + return (seterr (rv->error_number())); + + } // modify + + diff --git a/lib/modules/Protocols.pmod/LDAP.pmod/ldap_errors.h b/lib/modules/Protocols.pmod/LDAP.pmod/ldap_errors.h new file mode 100644 index 0000000000..d18deeb7f7 --- /dev/null +++ b/lib/modules/Protocols.pmod/LDAP.pmod/ldap_errors.h @@ -0,0 +1,226 @@ +// LDAP client protocol implementation for Pike. +// +// $Id: ldap_errors.h,v 1.1 1999/04/24 16:43:29 js Exp $ +// +// Honza Petrous, hop@unibase.cz +// +// ---------------------------------------------------------------------- +// +// ToDo List: +// +// - v2 operations: +// modify +// +// History: +// +// v1.0 1998-06-21 Core functions (open, bind, unbind, delete, add, +// compare, search), only V2 operations, +// + + + + +/* + * possible error codes we can return + */ + +#define LDAP_SUCCESS 0x00 /* 0 */ +#define LDAP_SUCCESS_STR "Success" +#define LDAP_OPERATIONS_ERROR 0x01 /* 1 */ +#define LDAP_OPERATIONS_ERROR_STR "Operations error" +#define LDAP_PROTOCOL_ERROR 0x02 /* 2 */ +#define LDAP_PROTOCOL_ERROR_STR "Protocol error" +#define LDAP_TIMELIMIT_EXCEEDED 0x03 /* 3 */ +#define LDAP_TIMELIMIT_EXCEEDED_STR "Timilimit exceeded" +#define LDAP_SIZELIMIT_EXCEEDED 0x04 /* 4 */ +#define LDAP_SIZELIMIT_EXCEEDED_STR "Sizelimit exceeded" +#define LDAP_COMPARE_FALSE 0x05 /* 5 */ +#define LDAP_COMPARE_FALSE_STR "Compare false" +#define LDAP_COMPARE_TRUE 0x06 /* 6 */ +#define LDAP_COMPARE_TRUE_STR "Compare true" +#define LDAP_AUTH_METHOD_NOT_SUPPORTED 0x07 /* 7 */ +#define LDAP_AUTH_METHOD_NOT_SUPPORTED_STR "Auth method not supported" +#define LDAP_STRONG_AUTH_NOT_SUPPORTED LDAP_AUTH_METHOD_NOT_SUPPORTED +#define LDAP_STRONG_AUTH_REQUIRED 0x08 /* 8 */ +#define LDAP_STRONG_AUTH_REQUIRED_STR "Strong auth required" +#define LDAP_PARTIAL_RESULTS 0x09 /* 9 (UMich LDAPv2 extn) */ +#define LDAP_PARTIAL_RESULTS_STR "Partial results" +#define LDAP_REFERRAL 0x0a /* 10 - LDAPv3 */ +#define LDAP_REFERRAL_STR "Referral" +#define LDAP_ADMINLIMIT_EXCEEDED 0x0b /* 11 - LDAPv3 */ +#define LDAP_ADMINLIMIT_EXCEEDED_STR "Adminlimit exceeded" +#define LDAP_UNAVAILABLE_CRITICAL_EXTENSION 0x0c /* 12 - LDAPv3 */ +#define LDAP_UNAVAILABLE_CRITICAL_EXTENSION_STR "Unavailable critical extension" +#define LDAP_CONFIDENTIALITY_REQUIRED 0x0d /* 13 */ +#define LDAP_CONFIDENTIALITY_REQUIRED_STR "Confidentiality required" +#define LDAP_SASL_BIND_IN_PROGRESS 0x0e /* 14 - LDAPv3 */ +#define LDAP_SASL_BIND_IN_PROGRESS_STR "SASL bind in progress" + +#define LDAP_NO_SUCH_ATTRIBUTE 0x10 /* 16 */ +#define LDAP_NO_SUCH_ATTRIBUTE_STR "No such attribute" +#define LDAP_UNDEFINED_TYPE 0x11 /* 17 */ +#define LDAP_UNDEFINED_TYPE_STR "Undefined type" +#define LDAP_INAPPROPRIATE_MATCHING 0x12 /* 18 */ +#define LDAP_INAPPROPRIATE_MATCHING_STR "Inappropriate matching" +#define LDAP_CONSTRAINT_VIOLATION 0x13 /* 19 */ +#define LDAP_CONSTRAINT_VIOLATION_STR "Constraint violation" +#define LDAP_TYPE_OR_VALUE_EXISTS 0x14 /* 20 */ +#define LDAP_TYPE_OR_VALUE_EXISTS_STR "Type or value exists" +#define LDAP_INVALID_SYNTAX 0x15 /* 21 */ +#define LDAP_INVALID_SYNTAX_STR "Invalid syntax" + +#define LDAP_NO_SUCH_OBJECT 0x20 /* 32 */ +#define LDAP_NO_SUCH_OBJECT_STR "No such object" +#define LDAP_ALIAS_PROBLEM 0x21 /* 33 */ +#define LDAP_ALIAS_PROBLEM_STR "Alias problem" +#define LDAP_INVALID_DN_SYNTAX 0x22 /* 34 */ +#define LDAP_INVALID_DN_SYNTAX_STR "Invalid DN syntax" +#define LDAP_IS_LEAF 0x23 /* 35 (not used in LDAPv3) */ +#define LDAP_IS_LEAF_STR "Is leaf" +#define LDAP_ALIAS_DEREF_PROBLEM 0x24 /* 36 */ +#define LDAP_ALIAS_DEREF_PROBLEM_STR "Alias deref problem" + +//#define NAME_ERROR(n) ((n & 0xf0) == 0x20) + +#define LDAP_INAPPROPRIATE_AUTH 0x30 /* 48 */ +#define LDAP_INAPPROPRIATE_AUTH_STR "Inappropriate auth" +#define LDAP_INVALID_CREDENTIALS 0x31 /* 49 */ +#define LDAP_INVALID_CREDENTIALS_STR "Invalid credentials" +#define LDAP_INSUFFICIENT_ACCESS 0x32 /* 50 */ +#define LDAP_INSUFFICIENT_ACCESS_STR "Insufficient access" +#define LDAP_BUSY 0x33 /* 51 */ +#define LDAP_BUSY_STR "Busy" +#define LDAP_UNAVAILABLE 0x34 /* 52 */ +#define LDAP_UNAVAILABLE_STR "Unavailable" +#define LDAP_UNWILLING_TO_PERFORM 0x35 /* 53 */ +#define LDAP_UNWILLING_TO_PERFORM_STR "Unwilling to perform" +#define LDAP_LOOP_DETECT 0x36 /* 54 */ +#define LDAP_LOOP_DETECT_STR "Loop detect" + +#define LDAP_SORT_CONTROL_MISSING 0x3C /* 60 */ +#define LDAP_SORT_CONTROL_MISSING_STR "Sort control missing" + +#define LDAP_NAMING_VIOLATION 0x40 /* 64 */ +#define LDAP_NAMING_VIOLATION_STR "Naming violation" +#define LDAP_OBJECT_CLASS_VIOLATION 0x41 /* 65 */ +#define LDAP_OBJECT_CLASS_VIOLATION_STR "Object class violation" +#define LDAP_NOT_ALLOWED_ON_NONLEAF 0x42 /* 66 */ +#define LDAP_NOT_ALLOWED_ON_NONLEAF_STR "Not allowed on nonleaf" +#define LDAP_NOT_ALLOWED_ON_RDN 0x43 /* 67 */ +#define LDAP_NOT_ALLOWED_ON_RDN_STR "Not allowed on RDN" +#define LDAP_ALREADY_EXISTS 0x44 /* 68 */ +#define LDAP_ALREADY_EXISTS_STR "Already exists" +#define LDAP_NO_OBJECT_CLASS_MODS 0x45 /* 69 */ +#define LDAP_NO_OBJECT_CLASS_MODS_STR "No object class mods" +#define LDAP_RESULTS_TOO_LARGE 0x46 /* 70 - CLDAP */ +#define LDAP_RESULTS_TOO_LARGE_STR "Results too large" +#define LDAP_AFFECTS_MULTIPLE_DSAS 0x47 /* 71 */ +#define LDAP_AFFECTS_MULTIPLE_DSAS_STR "Affects multiple DSAS" + +#define LDAP_OTHER 0x50 /* 80 */ +#define LDAP_OTHER_STR "Other str" +#define LDAP_SERVER_DOWN 0x51 /* 81 */ +#define LDAP_SERVER_DOWN_STR "Server is down" +#define LDAP_LOCAL_ERROR 0x52 /* 82 */ +#define LDAP_LOCAL_ERROR_STR "Internal/local error" +#define LDAP_ENCODING_ERROR 0x53 /* 83 */ +#define LDAP_ENCODING_ERROR_STR "Encoding error" +#define LDAP_DECODING_ERROR 0x54 /* 84 */ +#define LDAP_DECODING_ERROR_STR "Decoding error" +#define LDAP_TIMEOUT 0x55 /* 85 */ +#define LDAP_TIMEOUT_STR "Timeout" +#define LDAP_AUTH_UNKNOWN 0x56 /* 86 */ +#define LDAP_AUTH_UNKNOWN_STR "Auth unknown" +#define LDAP_FILTER_ERROR 0x57 /* 87 */ +#define LDAP_FILTER_ERROR_STR "Filter error" +#define LDAP_USER_CANCELLED 0x58 /* 88 */ +#define LDAP_USER_CANCELLED_STR "User cancelled" +#define LDAP_PARAM_ERROR 0x59 /* 89 */ +#define LDAP_PARAM_ERROR_STR "Param error" +#define LDAP_NO_MEMORY 0x5a /* 90 */ +#define LDAP_NO_MEMORY_STR "No memory" +#define LDAP_CONNECT_ERROR 0x5b /* 91 */ +#define LDAP_CONNECT_ERROR_STR "Connect error" +#define LDAP_NOT_SUPPORTED 0x5c /* 92 - LDAPv3 */ +#define LDAP_NOT_SUPPORTED_STR "Not supported" +#define LDAP_CONTROL_NOT_FOUND 0x5d /* 93 - LDAPv3 */ +#define LDAP_CONTROL_NOT_FOUND_STR "Control not found" +#define LDAP_NO_RESULTS_RETURNED 0x5e /* 94 - LDAPv3 */ +#define LDAP_NO_RESULTS_RETURNED_STR "No results returned" +#define LDAP_MORE_RESULTS_TO_RETURN 0x5f /* 95 - LDAPv3 */ +#define LDAP_MORE_RESULTS_TO_RETURN_STR "More results to return" +#define LDAP_CLIENT_LOOP 0x60 /* 96 - LDAPv3 */ +#define LDAP_CLIENT_LOOP_STR "Client loop" +#define LDAP_REFERRAL_LIMIT_EXCEEDED 0x61 /* 97 - LDAPv3 */ +#define LDAP_REFERRAL_LIMIT_EXCEEDED_STR "Referral limit exceeded" + +/*static* mapping(int:string)*/ constant ldap_errlist = ([ + LDAP_SUCCESS : LDAP_SUCCESS_STR, + LDAP_OPERATIONS_ERROR : LDAP_OPERATIONS_ERROR_STR, + LDAP_PROTOCOL_ERROR : LDAP_PROTOCOL_ERROR_STR, + LDAP_TIMELIMIT_EXCEEDED : LDAP_TIMELIMIT_EXCEEDED_STR, + LDAP_SIZELIMIT_EXCEEDED : LDAP_SIZELIMIT_EXCEEDED_STR, + LDAP_COMPARE_FALSE : LDAP_COMPARE_FALSE_STR, + LDAP_COMPARE_TRUE : LDAP_COMPARE_TRUE_STR, + LDAP_AUTH_METHOD_NOT_SUPPORTED : LDAP_AUTH_METHOD_NOT_SUPPORTED_STR, + LDAP_STRONG_AUTH_REQUIRED : LDAP_STRONG_AUTH_REQUIRED_STR, + LDAP_PARTIAL_RESULTS : LDAP_PARTIAL_RESULTS_STR, + LDAP_REFERRAL : LDAP_REFERRAL_STR, + LDAP_ADMINLIMIT_EXCEEDED : LDAP_ADMINLIMIT_EXCEEDED_STR, + LDAP_UNAVAILABLE_CRITICAL_EXTENSION: LDAP_UNAVAILABLE_CRITICAL_EXTENSION_STR, + LDAP_CONFIDENTIALITY_REQUIRED : LDAP_CONFIDENTIALITY_REQUIRED_STR, + LDAP_SASL_BIND_IN_PROGRESS : LDAP_SASL_BIND_IN_PROGRESS_STR, + + LDAP_NO_SUCH_ATTRIBUTE : LDAP_NO_SUCH_ATTRIBUTE_STR, + LDAP_UNDEFINED_TYPE : LDAP_UNDEFINED_TYPE_STR, + LDAP_INAPPROPRIATE_MATCHING : LDAP_INAPPROPRIATE_MATCHING_STR, + LDAP_CONSTRAINT_VIOLATION : LDAP_CONSTRAINT_VIOLATION_STR, + LDAP_TYPE_OR_VALUE_EXISTS : LDAP_TYPE_OR_VALUE_EXISTS_STR, + LDAP_INVALID_SYNTAX : LDAP_INVALID_SYNTAX_STR, + + LDAP_NO_SUCH_OBJECT : LDAP_NO_SUCH_OBJECT_STR, + LDAP_ALIAS_PROBLEM : LDAP_ALIAS_PROBLEM_STR, + LDAP_INVALID_DN_SYNTAX : LDAP_INVALID_DN_SYNTAX_STR, + LDAP_IS_LEAF : LDAP_IS_LEAF_STR, + LDAP_ALIAS_DEREF_PROBLEM : LDAP_ALIAS_DEREF_PROBLEM_STR, + + LDAP_INAPPROPRIATE_AUTH : LDAP_INAPPROPRIATE_AUTH_STR, + LDAP_INVALID_CREDENTIALS : LDAP_INVALID_CREDENTIALS_STR, + LDAP_INSUFFICIENT_ACCESS : LDAP_INSUFFICIENT_ACCESS_STR, + LDAP_BUSY : LDAP_BUSY_STR, + LDAP_UNAVAILABLE : LDAP_UNAVAILABLE_STR, + LDAP_UNWILLING_TO_PERFORM : LDAP_UNWILLING_TO_PERFORM_STR, + LDAP_LOOP_DETECT : LDAP_LOOP_DETECT_STR, + + LDAP_SORT_CONTROL_MISSING : LDAP_SORT_CONTROL_MISSING_STR, + + LDAP_NAMING_VIOLATION : LDAP_NAMING_VIOLATION_STR, + LDAP_OBJECT_CLASS_VIOLATION : LDAP_OBJECT_CLASS_VIOLATION_STR, + LDAP_NOT_ALLOWED_ON_NONLEAF : LDAP_NOT_ALLOWED_ON_NONLEAF_STR, + LDAP_NOT_ALLOWED_ON_RDN : LDAP_NOT_ALLOWED_ON_RDN_STR, + LDAP_ALREADY_EXISTS : LDAP_ALREADY_EXISTS_STR, + LDAP_NO_OBJECT_CLASS_MODS : LDAP_NO_OBJECT_CLASS_MODS_STR, + LDAP_ALREADY_EXISTS : LDAP_ALREADY_EXISTS_STR, + LDAP_NO_OBJECT_CLASS_MODS : LDAP_NO_OBJECT_CLASS_MODS_STR, + LDAP_RESULTS_TOO_LARGE : LDAP_RESULTS_TOO_LARGE_STR, + LDAP_AFFECTS_MULTIPLE_DSAS : LDAP_AFFECTS_MULTIPLE_DSAS_STR, + + LDAP_OTHER : LDAP_OTHER_STR, + LDAP_SERVER_DOWN : LDAP_SERVER_DOWN_STR, + LDAP_LOCAL_ERROR : LDAP_LOCAL_ERROR_STR, + LDAP_ENCODING_ERROR : LDAP_ENCODING_ERROR_STR, + LDAP_DECODING_ERROR : LDAP_DECODING_ERROR_STR, + LDAP_TIMEOUT : LDAP_TIMEOUT_STR, + LDAP_AUTH_UNKNOWN : LDAP_AUTH_UNKNOWN_STR, + LDAP_FILTER_ERROR : LDAP_FILTER_ERROR_STR, + LDAP_USER_CANCELLED : LDAP_USER_CANCELLED_STR, + LDAP_PARAM_ERROR : LDAP_PARAM_ERROR_STR, + LDAP_NO_MEMORY : LDAP_NO_MEMORY_STR, + LDAP_CONNECT_ERROR : LDAP_CONNECT_ERROR_STR, + LDAP_NOT_SUPPORTED : LDAP_NOT_SUPPORTED_STR, + LDAP_CONTROL_NOT_FOUND : LDAP_CONTROL_NOT_FOUND_STR, + LDAP_NO_RESULTS_RETURNED : LDAP_NO_RESULTS_RETURNED_STR, + LDAP_MORE_RESULTS_TO_RETURN : LDAP_MORE_RESULTS_TO_RETURN_STR, + LDAP_CLIENT_LOOP : LDAP_CLIENT_LOOP_STR, + LDAP_REFERRAL_LIMIT_EXCEEDED : LDAP_REFERRAL_LIMIT_EXCEEDED_STR]); +/**/ diff --git a/lib/modules/Protocols.pmod/LDAP.pmod/ldap_globals.h b/lib/modules/Protocols.pmod/LDAP.pmod/ldap_globals.h new file mode 100644 index 0000000000..6d40ffcd54 --- /dev/null +++ b/lib/modules/Protocols.pmod/LDAP.pmod/ldap_globals.h @@ -0,0 +1,39 @@ + + +// --------------- Standards.ASN1.Types private add-on -------------------- +// This is very poor defined own ASN.1 objects (not enough time to clean it!) + +//import .ldap_privates; + +#define ASN1_BOOLEAN .ldap_privates.asn1_boolean +#define ASN1_ENUMERATED .ldap_privates.asn1_enumerated + +#define ASN1_APPLICATION_SEQUENCE .ldap_privates.asn1_application_sequence +#define ASN1_APPLICATION_OCTET_STRING .ldap_privates.asn1_application_octet_string +#define ASN1_CONTEXT_SEQUENCE .ldap_privates.asn1_context_sequence +#define ASN1_CONTEXT_INTEGER .ldap_privates.asn1_context_integer +#define ASN1_CONTEXT_OCTET_STRING .ldap_privates.asn1_context_octet_string +#define ASN1_CONTEXT_SET .ldap_privates.asn1_context_set + +// ------------- end of ASN.1 API hack ----------------------------- + + +#define LDAP_DEFAULT_PORT 389 +#define LDAP_DEFAULT_HOST "127.0.0.1" +#define LDAP_DEFAULT_VERSION 2 + +#define UTF8_SUPPORT 0 +#if UTF8_SUPPORT +#define LDAP_DEFAULT_CHARSET "iso-8859-1" +#endif + +// --- Debug low level operations --- +#define DWRITE(X) +//#define DWRITE(X) werror("Protocols.LDAP: "+X) +// --- Debug high level operations --- +//#define DWRITE_HI(X) +#define DWRITE_HI(X) werror("Protocols.LDAP: "+X) +// --- Enable run-time error --- +//#define THROW(X) +#define THROW(X) throw(X) + diff --git a/lib/modules/Protocols.pmod/LDAP.pmod/ldap_privates.pmod b/lib/modules/Protocols.pmod/LDAP.pmod/ldap_privates.pmod new file mode 100644 index 0000000000..ef154c3074 --- /dev/null +++ b/lib/modules/Protocols.pmod/LDAP.pmod/ldap_privates.pmod @@ -0,0 +1,259 @@ +// LDAP client protocol implementation for Pike. +// +// $Id: ldap_privates.pmod,v 1.1 1999/04/24 16:43:30 js Exp $ +// +// Honza Petrous, hop@unibase.cz +// +// ---------------------------------------------------------------------- +// +// ToDo List: +// +// - v2 operations: +// modify +// +// History: +// +// v2.0 1999-02-19 Create separate file. Implementation the following +// classes: +// - asn1_enumerated +// - asn1_boolean +// - asn1_application_sequence +// - asn1_application_octet_string +// - asn1_context_integer +// - asn1_context_octet_string +// - asn1_context_sequence +// - asn1_context_set +// - ldap_der_decode +// + + + +// --------------- Standards.ASN1.Types private add-on -------------------- +// This is very poor defined own ASN.1 objects (not enough time to clean it!) +//import Standards.ASN1.Encode; + +class asn1_enumerated +{ + inherit Standards.ASN1.Types.asn1_integer; + constant tag = 10; + constant type_name = "ENUMERATED"; +} + +class asn1_boolean +{ + inherit Standards.ASN1.Types.asn1_object; + constant tag = 1; + constant type_name = "BOOLEAN"; + int value; + + object init(int n) { + if(n) + n=0xff; + value = n; + return (this_object()); + } + + string der_encode() { return build_der(value? "\377" : "\0"); } + + object decode_primitive(string contents) { + record_der(contents); + value = ( contents != "\0" ); + return this_object(); + } + + string debug_string() { + return value ? "TRUE" : "FALSE"; + } + +} + +class asn1_application_sequence +{ + inherit Standards.ASN1.Types.asn1_sequence; + constant cls = 1; + constant type_name = "APPLICATION SEQUENCE"; + int tagx; + + int get_tag() { return tagx; } + + void init(int tagid, array args) { + ::init(args); + tagx = tagid; + } + +} + +class asn1_application_octet_string +{ + inherit Standards.ASN1.Types.asn1_octet_string; + constant cls = 1; + constant type_name = "APPLICATION OCTET_STRING"; + int tagx; + + int get_tag() { return tagx; } + + void init(int tagid, string arg) { + ::value = arg; + tagx = tagid; + } + +} + +class asn1_context_integer +{ + inherit Standards.ASN1.Types.asn1_integer; + constant cls = 2; + constant type_name = "CONTEXT INTEGER"; + int tagx; + + int get_tag() { return tagx; } + + void init(int tagid, int arg) { + ::init(arg); + tagx = tagid; + } +} + +class asn1_context_octet_string +{ + inherit Standards.ASN1.Types.asn1_octet_string; + constant cls = 2; + constant type_name = "CONTEXT OCTET_STRING"; + int tagx; + + int get_tag() { return tagx; } + + void init(int tagid, string arg) { + ::init(arg); + tagx = tagid; + } +} + + +class asn1_context_sequence +{ + inherit Standards.ASN1.Types.asn1_sequence; + constant cls = 2; + constant type_name = "CONTEXT SEQUENCE"; + int tagx; + + int get_tag() { return tagx; } + + void init(int tagid, array arg) { + ::init(arg); + tagx = tagid; + } + +} + +class asn1_context_set +{ + inherit Standards.ASN1.Types.asn1_set; + constant cls = 2; + constant type_name = "CONTEXT SET"; + int tagx; + + int get_tag() { return tagx; } + + void init(int tagid, array arg) { + ::init(arg); + tagx = tagid; + } + +} + +#if 1 +object|mapping der_decode(object data, mapping types) +{ + int raw_tag = data->get_uint(1); + int len; + string contents; + + if ( (raw_tag & 0x1f) == 0x1f) + error("ASN1.Decode: High tag numbers is not supported\n"); + + int len = data->get_uint(1); + if (len & 0x80) + len = data->get_uint(len & 0x7f); + + contents = data->get_fix_string(len); + + int tag = raw_tag & 0xdf; // Class and tag bits + + program p = types[tag]; + + if (raw_tag & 0x20) + { + /* Constructed encoding */ + + array elements = ({ }); + object struct = ADT.struct(contents); + + if (!p) + { + while (!struct->is_empty()) + elements += ({ der_decode(struct, types) }); + + return Standards.ASN1.Decode.constructed(tag, contents, elements); + } + + object res = p(); + + // hop: For non-universal classes we provide tag number + if(tag & 0xC0) + res = p(tag & 0x1F, ({})); + + res->begin_decode_constructed(contents); + + int i; + + /* Ask object which types it expects for field i, decode it, and + * record the decoded object */ + for(i = 0; !struct->is_empty(); i++) + { + res->decode_constructed_element + (i, der_decode(struct, + res->element_types(i, types))); + } + return res->end_decode_constructed(i); + } + else + { + /* Primitive encoding */ + return p ? p()->decode_primitive(contents) + : Standards.ASN1.Decode.primitive(tag, contents); + } +} +#endif + +static mapping ldap_type_proc = + ([ 1 : asn1_boolean, + 2 : Standards.ASN1.Types.asn1_integer, + 3 : Standards.ASN1.Types.asn1_bit_string, + 4 : Standards.ASN1.Types.asn1_octet_string, + 5 : Standards.ASN1.Types.asn1_null, + 6 : Standards.ASN1.Types.asn1_identifier, + // 9 : asn1_real, + 10 : asn1_enumerated, + 16 : Standards.ASN1.Types.asn1_sequence, + 17 : Standards.ASN1.Types.asn1_set, + 19 : Standards.ASN1.Types.asn1_printable_string, + 20 : Standards.ASN1.Types.asn1_T61_string, + 23 : Standards.ASN1.Types.asn1_utc, + 65 : asn1_application_sequence, + 68 : asn1_application_sequence, + 69 : asn1_application_sequence, + 71 : asn1_application_sequence, + 73 : asn1_application_sequence, + 75 : asn1_application_sequence, + 77 : asn1_application_sequence, + 79 : asn1_application_sequence + ]); + +object|mapping ldap_der_decode(string data) +{ + return der_decode(ADT.struct(data), ldap_type_proc); +} + +// ------------- end of ASN.1 API hack ----------------------------- + diff --git a/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike b/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike new file mode 100644 index 0000000000..eb8a52715b --- /dev/null +++ b/lib/modules/Protocols.pmod/LDAP.pmod/protocol.pike @@ -0,0 +1,470 @@ +// LDAP client protocol implementation for Pike. +// +// $Id: protocol.pike,v 1.1 1999/04/24 16:43:30 js Exp $ +// +// Honza Petrous, hop@unibase.cz +// +// ---------------------------------------------------------------------- +// +// History: +// +// v1.9 1999-02-19 created separate file +// - improved checking of readed bytes in 'readmsg' +// v1.9.1.1 +// 1999-03-21 - changed FILE to File (unbuffered) +// - some inactive code +// +// v1.10 1999-03-28 - rewritten methods 'readmsg' & 'writemsg' +// to new method 'xchgmsg' +// - added core for async operation +// + + +#include "ldap_globals.h" + +#include "ldap_errors.h" + + inherit Stdio.File : ldap; + + // private variables + int next_id = 1; // message id counter + int ldap_version = LDAP_DEFAULT_VERSION; // actually used protocol vers. + string ldap_rem_errstr = LDAP_SUCCESS_STR; // last remote error description + int ldap_errno = LDAP_SUCCESS; // last error code + + /*private*/ string readbuf=""; // read buffer + private int ok; // read buffer status + /*private*/ string writebuf=""; // write buffer +// private written; // count of written chars + private function con_ok, con_fail; // async callback functions + object conthread; // thread connection + array extra_args; // not used, yet +// /*private*/ int errno; + int connected = 0; + + int seterr(int errno) { + // Sets ldap_err* variables and returns errno + + //ldap_rem_errstr = errstr; + ldap_errno = errno; + return(errno); + } + + int error_number() { return(ldap_errno); } + + string error_string() { return(ldap_errlist[ldap_errno]); } + + array error() { return(({error_number(), error_string()})); } + + + static void read_answer() { + // ---------------------- + // Reads LDAP PDU (with defined msgid) from the server + + int msglen = 0, ix, ofs; + string s, shlp; + + if(strlen(readbuf) < 2) + readbuf = ldap::read(2); // 1. byte = 0x0C, 2. byte = msglen + if (intp(readbuf) || (strlen(readbuf) < 2)) { + seterr (LDAP_TIMEOUT); + DWRITE_HI("protocol.read_anwer: ERROR: connection timeout.\n"); + THROW(({"LDAP: connection timeout.\n",backtrace()})); + //return(-ldap_errno); + return; + } + if (readbuf[0] != '0') { + seterr (LDAP_PROTOCOL_ERROR); + DWRITE_HI("protocol.read_anwer: ERROR: retv=<"+sprintf("%O",readbuf)+">\n"); + THROW(({"LDAP: Protocol mismatch.\n",backtrace()})); + //return(-ldap_errno); + return; + } + DWRITE(sprintf("protocol.read_anwer: sizeof = %d\n", sizeof(readbuf))); + + msglen = readbuf[1]; + ofs = 2; + if (msglen & 0x80) { // > 0x7f + if (msglen == 0x80) { // RFC not allows unexplicitly defined length + seterr (LDAP_PROTOCOL_ERROR); + THROW(({"LDAP: Protocol mismatch.\n",backtrace()})); + //return(-ldap_errno); + return; + } + ofs = (msglen & 0x7f) + 2; + ix = ofs - strlen(readbuf); + if(ix > 0) + s = ldap::read(ix); + if (!s || (strlen(s) < ix)) { + seterr (LDAP_PROTOCOL_ERROR); + THROW(({"LDAP: Protocol mismatch.\n",backtrace()})); + //return(-ldap_errno); + return; + } + readbuf += s; + msglen = 0; // !!! RESTRICTION: 2^32 !!! + shlp = reverse(readbuf[2..ofs]); + for (ix=0; ix<strlen(shlp); ix++) { + msglen += shlp[ix]*(1<<(ix*8)); + } + } + ix = (ofs + msglen) - strlen(readbuf); + if(ix > 0) + s = ldap::read(ix); + if (!s || (strlen(s) < ix)) { + seterr (LDAP_SERVER_DOWN); + THROW(({"LDAP: connection closed by server.\n",backtrace()})); + //return(-ldap_errno); + return; + } + readbuf += s; + //DWRITE(sprintf("protocol.read_anwer: %s\n", .ldap_privates.ldap_der_decode(readbuf)->debug_string())); + DWRITE("protocol.read_anwer: ok=1.\n"); + ok = 1; + + remove_call_out(async_timeout); + + if(con_ok) + con_ok(this_object(), @extra_args); + } + + static void connect(string server, int port) { + // ----------------------------------------- + +//DWRITE("DEB: connect ...\n"); + if(catch { ldap::connect(server, port); }) { + //if(!(errno = ldap::errno())) { + if(!ldap::errno()) { + seterr (LDAP_PARAM_ERROR); // or _CONNECT_ERROR ? + THROW(({"LDAP: connection parameter error.\n",backtrace()})); + } + //::destroy(); + //ldap=0; + ok = 0; + return; + } + } + + static void async_close() { + // ---------------------- + +DWRITE("DEB: async_close ...\n"); + ldap::set_blocking(); + read_answer(); + } + + static int is_whole_pdu() { + // ---------------------- + // Check if LDAP PDU is complete in 'readbuf' + + int msglen, ix, ofs; + string shlp; + + if (strlen(readbuf) < 3) + return(0); // PDU have min. 3 bytes + + if (readbuf[0] != '0') + return(1); // PDU has bad header -> forced execution + + msglen = readbuf[1]; + if (msglen & 0x80) { // > 0x7f + if (msglen == 0x80) + return(1); // forced execution + ofs = (msglen & 0x7f) + 2; + ix = ofs - strlen(readbuf); + if(ix > 0) + return(0); // incomplete PDU + msglen = 0; + shlp = reverse(readbuf[2..ofs]); + for (ix=0; ix<strlen(shlp); ix++) { + msglen += shlp[ix]*(1<<(ix*8)); + } + } + ix = (ofs + msglen) - strlen(readbuf); + if(ix > 0) + return(0); // incomplete PDU + + return(1); + } + + static void async_read(mixed id, string data) { + // ------------------------------------------ + +DWRITE("DEB: async_read ...\n"); + readbuf += data; + if(is_whole_pdu()) { + ldap::set_blocking(); + read_answer(); + } + } + + static void async_write() { + // ---------------------- + +DWRITE("DEB: async_write ...\n"); + ldap::set_blocking(); + ldap::write(writebuf); // ??? NEmusi se testovat uspesnost ?? + ldap::set_nonblocking(async_read, 0, async_close); + } + + static void async_connected() { + // -------------------------- + +DWRITE("DEB: async_connected ...\n"); + ldap::set_nonblocking(async_read, async_write, async_close); + //ldap::write(""); // ??? What is it? Initiator !?? + } + + static void async_failed() { + // ----------------------- + +DWRITE("DEB: async_failed ...\n"); + seterr(LDAP_SERVER_DOWN); + ok = 0; + + if(con_fail) + con_fail(this_object(), @extra_args); + remove_call_out(async_timeout); + } + + static void async_timeout() { + // ------------------------ + + seterr (LDAP_TIMEOUT); +DWRITE("protocol.async_timeout: ERROR: connection timeout.\n"); + //THROW(({"LDAP: connection timeout.\n",backtrace()})); + catch { + ldap::close(); + //ldap::destroy(); + }; + async_failed(); + } + + void async_got_host(string server, int port) { + // ----------------------------------------- + + if(!server) { + async_failed(); + //ldap::destroy() + return; + } + if(!ldap::open_socket()) { + seterr (LDAP_SERVER_DOWN); + DWRITE("protocol.async_got_host: ERROR: can't open socket.\n"); + THROW(({"LDAP: can't open socket.\n",backtrace()})); + return; + } + + ldap::set_nonblocking(0, async_connected, async_failed); + DWRITE("protocol.async_got_host: connected!\n"); + + if(catch { ldap::connect(server, port); }) { + //errno = ldap::errno(); + seterr (LDAP_SERVER_DOWN); + DWRITE("protocol.async_got_host: ERROR: can't open socket.\n"); + //ldap::destroy(); + //ldap=0; + ok = 0; + async_failed(); + } + } + + void async_fetch_read(mixed id, string data) { + // ----------------------------------------- + + readbuf += data; + } + + void async_fetch_close() { + // --------------------- + + ldap::set_blocking(); + //ldap::destroy(); + //ldap=0; + if(con_ok) + con_ok(@extra_args); + } + + + + +/*********** API methods **************/ + + object set_callbacks(function _ok, + function _fail, + mixed ...extra) { + // ----------------------------------- + + extra_args = extra; + con_ok = _ok; + con_fail = _fail; + return(this_object()); + } + + object thread_create(string server, int port) { + // ------------------------------------------ + + return (0); // unimplemented !!! + } + + object async_create(string server, int port) { + // ----------------------------------------- + + return (0); // unimplemented !!! +#if 0 + call_out(async_timeout); + dns_lookup_async(server, async_got_host, port); + return(this_object()); +#endif + } + + void create(string server, int port) { + // --------------------------------- + + if(!ldap::connect(server, port)) { + //errno = ldap::errno(); + seterr (LDAP_SERVER_DOWN); + DWRITE("protocol.create: ERROR: can't open socket.\n"); + //ldap::destroy(); + //ldap=0; + ok = 0; + if(con_fail) + con_fail(this_object(), @extra_args); + } + + connected = 1; + DWRITE("protocol.create: connected!\n"); + + } + + + string|int do_op(object msgop) { + // --------------------------- + // Make LDAP PDU envelope for 'msgop', send it and read answer ... + + object msgval; + object msgid; + int rv = 0, msgnum; + string s; + + //THREAD_LOCK + msgnum = next_id++; + //THREAD_UNLOCK + msgid = Standards.ASN1.Types.asn1_integer(msgnum); + msgval = Standards.ASN1.Types.asn1_sequence(({msgid, msgop})); + + if (objectp(msgval)) { + DWRITE(sprintf("protocol.do_op: msg = [%d]\n",sizeof(msgval->get_der()))); + } else + DWRITE("protocol.do_op: msg is null!\n"); + + // call_out + writebuf = msgval->get_der(); + rv = ldap::write(writebuf); // !!!!! - jak rozlisit async a sync ???? + // call_out + if (rv < 2) { + seterr (LDAP_SERVER_DOWN); + THROW(({"LDAP: connection closed by server.\n",backtrace()})); + return(-ldap_errno); + } + DWRITE(sprintf("protocol.do_op: write OK [%d bytes].\n",rv)); + msgval = 0; msgid = 0; + writebuf= ""; + readbuf= ""; // !!! NEni to pozde ? + + //`()(); + + read_answer(); + // now is all in 'readbuf' + + return(readbuf); + + } + +/* ------------ legacy support -----------------*/ + + string|int readmsg(int msgid) { + // Reads LDAP PDU (with defined msgid) from server, checks msgid ... + + int msglen = 0, ix; + string retv, s, shlp; + + retv = ldap::read(2); // 1. byte = 0x0C, 2. byte = msglen + if (intp(retv) && (retv == -1)) { + seterr (LDAP_TIMEOUT); + DWRITE_HI("protocol.readmsg: ERROR: connection timeout.\n"); + THROW(({"LDAP: connection timeout.\n",backtrace()})); + return(-ldap_errno); + } + if (!retv || (sizeof(retv) != 2) || (retv[0] != '0')) { + seterr (LDAP_PROTOCOL_ERROR); + DWRITE_HI("protocol.readmsg: ERROR: retv=<"+sprintf("%O",retv)+">\n"); + THROW(({"LDAP: Protocol mismatch.\n",backtrace()})); + return(-ldap_errno); + } + DWRITE(sprintf("protocol.readmsg: sizeof = %d\n", sizeof(retv))); + + msglen = retv[1]; + if (msglen & 0x80) { // > 0x7f + if (msglen == 0x80) { // RFC not allows unexplicitly defined length + seterr (LDAP_PROTOCOL_ERROR); + THROW(({"LDAP: Protocol mismatch.\n",backtrace()})); + return(-ldap_errno); + } + s = ldap::read(msglen & 0x7f); + if (!s) { + seterr (LDAP_PROTOCOL_ERROR); + THROW(({"LDAP: Protocol mismatch.\n",backtrace()})); + return(-ldap_errno); + } + retv += s; + msglen = 0; // !!! RESTRICTION: 2^32 !!! + shlp = reverse(s); + for (ix=0; ix<sizeof(shlp); ix++) { + msglen += shlp[ix]*(1<<(ix*8)); + } + } + s = ldap::read(msglen); + if (!s | (sizeof(s) < msglen)) { + seterr (LDAP_SERVER_DOWN); + THROW(({"LDAP: connection closed by server.\n",backtrace()})); + return(-ldap_errno); + } + retv += s; + DWRITE(sprintf("protocol.readmsg: %s\n", .ldap_privates.ldap_der_decode(retv)->debug_string())); + return (retv); + } + + int writemsg(object msgop) { + // Make LDAP PDU envelope for 'msgop' and send it + + object msgval; + object msgid; + int rv = 0, msgnum; + string s; + + //THREAD_LOCK + msgnum = next_id++; + //THREAD_UNLOCK + msgid = Standards.ASN1.Types.asn1_integer(msgnum); + msgval = Standards.ASN1.Types.asn1_sequence(({msgid, msgop})); + + if (objectp(msgval)) { + DWRITE(sprintf("protocol.writemsg: msg = [%d]\n",sizeof(msgval->get_der()))); + } else + DWRITE("protocol.writemsg: msg is null!\n"); + + // call_out + rv = ldap::write(msgval->get_der()); + // call_out + if (rv < 2) { + seterr (LDAP_SERVER_DOWN); + THROW(({"LDAP: connection closed by server.\n",backtrace()})); + return(-ldap_errno); + } + DWRITE(sprintf("protocol.writemsg: write OK [%d bytes].\n",rv)); + msgval = 0; msgid = 0; + return (msgnum); + } + + -- GitLab