a580e12000-09-27Fredrik Hübinette (Hubbe) #pike __REAL_VERSION__
a20af62000-09-26Fredrik Hübinette (Hubbe) 
aa370a1999-04-24Johan Schön // LDAP client protocol implementation for Pike. //
834e552001-11-21Anders Johansson // $Id: client.pike,v 1.28 2001/11/21 16:22:42 anders Exp $
aa370a1999-04-24Johan Schön // // 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 ]
b68dde2000-02-12Honza Petrous // v1.13 2000-02-12 - fixed search NOT op bug (end revision normalized)
aa370a1999-04-24Johan Schön //
4ad4cd2000-02-17Honza Petrous // v1.14 2000-02-17 - added decoding of UTF8 strings for v3 protocol //
6de1dc2000-07-14Honza Petrous // newer versions - see CVS at roxen.com (hop) //
59a35e2000-07-25Honza Petrous // - corrected deUTF8 values in result // -
fc02d12000-07-20Honza Petrous //
aa370a1999-04-24Johan Schön // 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?)
92dbd31999-08-24Fredrik Hübinette (Hubbe) #if constant(.ldap_privates.ldap_der_decode)
aa370a1999-04-24Johan Schön  #include "ldap_globals.h" #include "ldap_errors.h"
5c83622001-09-17Honza Petrous import SSL.constants;
c9ab362001-06-21Honza Petrous #ifdef LDAP_PROTOCOL_PROFILE int _prof_gtim; #endif
aa370a1999-04-24Johan Schön // ------------------------ // 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())
af5dc72001-08-15Honza Petrous #define ASN1_DECODE_RESULTSTRING(X) (.ldap_privates.ldap_der_decode(X)->elements[1]->elements[2]->value) #define ASN1_DECODE_RESULTREFS(X) (.ldap_privates.ldap_der_decode(X)->elements[1]->elements[3]->elements)
aa370a1999-04-24Johan Schön #define ASN1_DECODE_ENTRIES(X) _New_decode(X)
c9ab362001-06-21Honza Petrous #define ASN1_DECODE_DN(X) (string)((X)->elements[0]->value)
aa370a1999-04-24Johan Schön #define ASN1_DECODE_RAWDEBUG(X) (.ldap_privates.ldap_der_decode(X)->debug_string())
c9ab362001-06-21Honza Petrous #define ASN1_GET_ATTR_ARRAY(X) (array)((X)->elements[1]->elements)
aa370a1999-04-24Johan Schön #define ASN1_GET_ATTR_NAME(X) ((X)->elements[0]->value)
5c83622001-09-17Honza Petrous  //! Contains the client implementation of the LDAP protocol. //! All of the version 2 protocol features are implemented //! but only the base parts of the version 3.
aa370a1999-04-24Johan Schön  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;
fc02d12000-07-20Honza Petrous  private mapping lauth = ([]);
aa370a1999-04-24Johan Schön 
5c83622001-09-17Honza Petrous  //! Contains the result of a LDAP search. //! //! @seealso //! @[LDAP.client.search], @[LDAP.client.result.fetch] //!
aa370a1999-04-24Johan Schön  class result // ------------------ { private int resultcode = LDAP_SUCCESS;
af5dc72001-08-15Honza Petrous  private string resultstring;
aa370a1999-04-24Johan Schön  private int entrycnt = 0; private int actnum = 0; private array(mapping(string:array(string))) entry = ({});
af5dc72001-08-15Honza Petrous  array(string) referrals;
aa370a1999-04-24Johan Schön 
fc02d12000-07-20Honza Petrous  private array _get_attr_values(int ver, object x) {
aa370a1999-04-24Johan Schön  array res = ({}); if(!sizeof(x->elements)) return(res); foreach(x->elements[1]->elements, object val1) res += ({ val1->value });
fc02d12000-07-20Honza Petrous  if(ver == 3) { // deUTF8 res = Array.map(res, utf8_to_string); }
aa370a1999-04-24Johan Schön  return(res); } private array _New_decode(array ar) { array res = ({}); array entry1; mapping attrs;
c9ab362001-06-21Honza Petrous  object oder;
aa370a1999-04-24Johan Schön  foreach(ar, string raw1) {
c9ab362001-06-21Honza Petrous  oder = (.ldap_privates.ldap_der_decode(raw1)->elements[1]); attrs = (["dn":({ASN1_DECODE_DN(oder)})]); entry1 = ASN1_GET_ATTR_ARRAY(oder);
aa370a1999-04-24Johan Schön  foreach(entry1, object attr1) {
fc02d12000-07-20Honza Petrous  attrs += ([ASN1_GET_ATTR_NAME(attr1):_get_attr_values(ldap_version, attr1)]);
aa370a1999-04-24Johan Schön  } res += ({attrs}); }
4ad4cd2000-02-17Honza Petrous 
aa370a1999-04-24Johan Schön  return (res); } // _New_decode
5c83622001-09-17Honza Petrous  //! //! You can't create instances of this object yourself. //! The only way to create it is via a search of a LDAP server.
aa370a1999-04-24Johan Schön  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("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)); resultstring = ASN1_DECODE_RESULTSTRING(rawres[lastel]);
af5dc72001-08-15Honza Petrous  DWRITE(sprintf("result.create: str=%s\n",resultstring)); #ifdef V3_REFERRALS
aa370a1999-04-24Johan Schön  // referral (v3 mode)
af5dc72001-08-15Honza Petrous  if(resultcode == 10) { referrals = ({}); foreach(ASN1_DECODE_RESULTREFS(rawres[lastel]), object ref1) referrals += ({ ref1->value }); DWRITE(sprintf("result.create: refs=%O\n",referrals)); } #endif
aa370a1999-04-24Johan Schön  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());
5c83622001-09-17Honza Petrous  } //! //! Returns error number of search result. //! //! @seealso //! @[LDAP.client.result.error_string] int error_number() { return(resultcode); }
aa370a1999-04-24Johan Schön 
5c83622001-09-17Honza Petrous  //! //! Returns error description of search result. //! //! @seealso //! @[LDAP.client.result.error_number] string error_string() { return((stringp(resultstring) && sizeof(resultstring)) ? resultstring : ldap_errlist[resultcode]); } //! //! Returns the number of entries. //! //! @seealso //! @[LDAP.client.result.count_entries] int num_entries() { return(entrycnt); }
aa370a1999-04-24Johan Schön 
5c83622001-09-17Honza Petrous  //! //! Returns the number of entries from current cursor //! possition till end of the list. //! //! @seealso //! @[LDAP.client.result.first], @[LDAP.client.result.next] int count_entries() { return(entrycnt - actnum); } //! Returns a mapping with an entry for each attribute. //! Each entry is an array of values of the attribute. //!
8e75a02001-11-05Honza Petrous  //! @param index
5c83622001-09-17Honza Petrous  //! Optional argument can be used for direct access //! to the entry other then currently pointed by cursor. int|mapping(string:array(string)) fetch(int|void idx) { if (!idx) idx = actnum + 1; if ((idx <= num_entries()) && (idx > 0)) { actnum = idx - 1;
aa370a1999-04-24Johan Schön  return(entry[actnum]); } return(0); }
5c83622001-09-17Honza Petrous  //! //! Returns distinguished name (DN) of the current entry //! in the result list. Notice that this is the same //! as fetch()->dn[0].
aa370a1999-04-24Johan Schön  string get_dn() { return(fetch()["dn"][0]); }
5c83622001-09-17Honza Petrous  //! //! Initialized the result cursor to the first entry //! in the result list. //! //! @seealso //! @[LDAP.client.result.next]
aa370a1999-04-24Johan Schön  void first() { actnum = 0; }
5c83622001-09-17Honza Petrous  //! //! Moves the result cursor to the next entry //! in the result list. Returns number of remained entries //! in the result list. Returns 0 at the end. //! //! @seealso //! @[LDAP.client.result.next]
aa370a1999-04-24Johan Schön  int next() { if (actnum < (num_entries()-1)) { actnum++; return(count_entries()); } return(0); }
5c83622001-09-17Honza Petrous 
aa370a1999-04-24Johan Schön  } // 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); }
fc02d12000-07-20Honza Petrous  if ((ldap_version == 3) && !binded) bind();
aa370a1999-04-24Johan Schön  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); }
5c83622001-09-17Honza Petrous  //! Several information about code itself and about active connection too mapping info; //! @decl void create()
8e75a02001-11-05Honza Petrous  //! @decl void create(string url) //! @decl void create(string url, object context)
5c83622001-09-17Honza Petrous  //! //! Create object. The first optional argument can be used later //! for subsequence operations. The second one can specify
8e75a02001-11-05Honza Petrous  //! TLS context of connection. The default context only allows //! 128-bit encryption methods, so you may need to provide your //! own context if your LDAP server supports only export encryption.
5c83622001-09-17Honza Petrous  //! //! @param url //! LDAP server URL in form //! @tt{"ldap://hostname/basedn?attrlist?scope?ext"@} //! //! @param context //! TLS context of connection //! //! @seealso //! @[LDAP.client.bind], @[LDAP.client.search] void create(string|void url, object|void context)
aa370a1999-04-24Johan Schön  {
834e552001-11-21Anders Johansson  info = ([ "code_revision" : ("$Revision: 1.28 $"/" ")[1] ]);
5c83622001-09-17Honza Petrous  if(!url || !sizeof(url)) url = LDAP_DEFAULT_URL;
fc02d12000-07-20Honza Petrous 
5c83622001-09-17Honza Petrous  lauth = parse_url(url);
fc02d12000-07-20Honza Petrous 
5c83622001-09-17Honza Petrous  if(!stringp(lauth->scheme) || ((lauth->scheme != "ldap") && (lauth->scheme != "ldaps"))) {
fc02d12000-07-20Honza Petrous  THROW(({"Unknown scheme in server URL.\n",backtrace()})); }
59a35e2000-07-25Honza Petrous  if(!lauth->host)
5c83622001-09-17Honza Petrous  lauth += ([ "host" : LDAP_DEFAULT_HOST ]);
59a35e2000-07-25Honza Petrous  if(!lauth->port)
5c83622001-09-17Honza Petrous  lauth += ([ "port" : lauth->scheme == "ldap" ? LDAP_DEFAULT_PORT : LDAPS_DEFAULT_PORT ]); if(lauth->scheme == "ldaps" && !context) { context = SSL.context(); // Allow only strong crypto context->preferred_suites = ({ SSL_rsa_with_idea_cbc_sha, SSL_rsa_with_rc4_128_sha, SSL_rsa_with_rc4_128_md5, SSL_rsa_with_3des_ede_cbc_sha, }); } if(!(::connect(lauth->host, lauth->port))) { //errno = ldapfd->errno(); seterr (LDAP_SERVER_DOWN); DWRITE("client.create: ERROR: can't open socket.\n"); //ldapfd->destroy(); //ldap=0; //ok = 0; //if(con_fail) // con_fail(this_object(), @extra_args);
aa370a1999-04-24Johan Schön  THROW(({"Failed to connect to LDAP server.\n",backtrace()})); }
5c83622001-09-17Honza Petrous  if(lauth->scheme == "ldaps") { context->random = Crypto.randomness.reasonably_random()->read; ::create(SSL.sslfile(::_fd, context, 1,1)); info->tls_version = ldapfd->version; } else ::create(::_fd); DWRITE("client.create: connected!\n");
aa370a1999-04-24Johan Schön  DWRITE(sprintf("client.create: remote = %s\n", query_address()));
59a35e2000-07-25Honza Petrous  DWRITE_HI("client.OPEN: " + lauth->host + ":" + (string)(lauth->port) + " - OK\n");
aa370a1999-04-24Johan Schön  binded = 0;
fc02d12000-07-20Honza Petrous  if(lauth->scope) set_scope(lauth->scope); if(lauth->basedn) set_basedn(lauth->basedn);
aa370a1999-04-24Johan Schön  } // 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)); }
5c83622001-09-17Honza Petrous  //! @decl int bind()
8e75a02001-11-05Honza Petrous  //! @decl int bind(string dn, string password) //! @decl int bind(string dn, string password, int version)
5c83622001-09-17Honza Petrous  //! //! Authenticates connection to the direcory. //! //! First form uses default value previously entered in create. //! //! Second form uses value from parameters: //! //! @param dn //! The distinguished name (DN) of an entry aginst which will //! be made authentication. //! @param password //! Password used for authentication. //! //! Third form allows specify the version of LDAP protocol used //! by connection to the LDAP server. //! //! @param version //! Only @tt{2@} or @tt{3@} can be entered. //! //! @note //! Only simple authentication type is implemented. So be warned //! clear text passwords are sent to the directory server. int bind (string|void dn, string|void password, int|void version) {
aa370a1999-04-24Johan Schön  int id; mixed raw; object rv;
5c83622001-09-17Honza Petrous  if (!version) version = LDAP_DEFAULT_VERSION;
aa370a1999-04-24Johan Schön  if (chk_ver()) return(-ldap_errno);
5c83622001-09-17Honza Petrous  if (!stringp(dn)) dn = mappingp(lauth->ext) ? lauth->ext->bindname||"" : "";
aa370a1999-04-24Johan Schön  if (!stringp(password)) password = "";
5c83622001-09-17Honza Petrous  ldap_version = version;
aa370a1999-04-24Johan Schön  if(ldap_version == 3) {
5c83622001-09-17Honza Petrous  dn = string_to_utf8(dn);
6f3cc51999-08-12Marcus Comstedt  password = string_to_utf8(password);
aa370a1999-04-24Johan Schön  }
5c83622001-09-17Honza Petrous  if(intp(raw = send_bind_op(dn, password))) {
aa370a1999-04-24Johan Schön  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()); }
5c83622001-09-17Honza Petrous  //! //! Unbinds from the directory and close the connection.
aa370a1999-04-24Johan Schön  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))); }
5c83622001-09-17Honza Petrous  //! //! Deletes entry from the LDAP server. //! //! @param dn //! The distinguished name of deleted entry.
aa370a1999-04-24Johan Schön  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(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn);
aa370a1999-04-24Johan Schön  } 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)); }
5c83622001-09-17Honza Petrous  //! //! Compares given attribute value with one in the directory. //! //! @param dn //! The distinguished name of compared entry. //! //! @param aval //! The mapping of compared attributes and theirs values.
aa370a1999-04-24Johan Schön  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(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn); aval = Array.map(aval, string_to_utf8);
aa370a1999-04-24Johan Schön  } 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)); }
8e75a02001-11-05Honza Petrous  //! The Add Operation allows a client to request the addition //! of an entry into the directory //! //! @param dn //! The Distinguished Name of the entry to be added. //! //! @param attrs //! The mapping of attributes and their values that make up the content //! of the entry being added. //!
aa370a1999-04-24Johan Schön  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(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn); array(string) keys = indices(attrs); array(array(string)) vals = values(attrs); attrs = mkmapping(Array.map(keys, string_to_utf8), Array.map(vals, Array.map, string_to_utf8));
aa370a1999-04-24Johan Schön  } 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);
8e75a02001-11-05Honza Petrous  }
aa370a1999-04-24Johan Schön  /*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");
834e552001-11-21Anders Johansson  if (!sizeof(filter)) return make_simple_filter(filter);
aa370a1999-04-24Johan Schön  // strip leading and trailing spaces
834e552001-11-21Anders Johansson  filter = String.trim_all_whites(filter);
aa370a1999-04-24Johan Schön  // 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..]); 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])))
b68dde2000-02-12Honza Petrous  return(ASN1_CONTEXT_SEQUENCE(2, ({ ohlp}) ));
aa370a1999-04-24Johan Schön  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 = ({}); 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
aa40f42000-10-11Honza Petrous  object|int search (string|void filter, int|void attrsonly, array(string)|void attrs) {
aa370a1999-04-24Johan Schön 
c9ab362001-06-21Honza Petrous  int id,nv;
aa370a1999-04-24Johan Schön  mixed raw; array(string) rawarr = ({}); mixed rv;
59a35e2000-07-25Honza Petrous  filter=filter||lauth->filter; // default from LDAP URI
aa370a1999-04-24Johan Schön  DWRITE_HI("client.SEARCH: " + (string)filter + "\n"); if (chk_ver()) return(-ldap_errno); if (chk_binded()) return(-ldap_errno); if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  filter = string_to_utf8(filter);
aa370a1999-04-24Johan Schön  }
c9ab362001-06-21Honza Petrous #ifdef LDAP_PROTOCOL_PROFILE _prof_gtim = gauge{ #endif
aa370a1999-04-24Johan Schön  if(intp(raw = send_search_op(ldap_basedn, ldap_scope, ldap_deref, ldap_sizelimit, ldap_timelimit, attrsonly, filter,
02c9462001-04-03Honza Petrous  attrs||lauth->attributes))) {
aa370a1999-04-24Johan Schön  THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); }
c9ab362001-06-21Honza Petrous #ifdef LDAP_PROTOCOL_PROFILE }; DWRITE_PROF("send_search_op: %O\n", _prof_gtim); #endif
aa370a1999-04-24Johan Schön  rawarr = ({raw});
c9ab362001-06-21Honza Petrous #ifdef LDAP_PROTOCOL_PROFILE _prof_gtim = gauge{ #endif
aa370a1999-04-24Johan Schön  while (ASN1_DECODE_RESULTAPP(raw) != 5) {
c9ab362001-06-21Honza Petrous #ifdef LDAP_PROTOCOL_PROFILEx DWRITE_PROF("readmsg: %O\n", gauge { raw = readmsg(id); }); #else
aa370a1999-04-24Johan Schön  raw = readmsg(id);
c9ab362001-06-21Honza Petrous #endif
aa370a1999-04-24Johan Schön  if (intp(raw)) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); }
af5dc72001-08-15Honza Petrous  rawarr += ({raw});
aa370a1999-04-24Johan Schön  } // while
c9ab362001-06-21Honza Petrous #ifdef LDAP_PROTOCOL_PROFILE }; DWRITE_PROF("rawarr++: %O\n", _prof_gtim); #endif
aa370a1999-04-24Johan Schön 
c9ab362001-06-21Honza Petrous #ifdef LDAP_PROTOCOL_PROFILE _prof_gtim = gauge{ rv = result(rawarr); }; DWRITE_PROF("result: %O\n", _prof_gtim); #else
aa370a1999-04-24Johan Schön  rv = result(rawarr);
c9ab362001-06-21Honza Petrous #endif
aa370a1999-04-24Johan Schön  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(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  base_dn = string_to_utf8(base_dn);
aa370a1999-04-24Johan Schön  } ldap_basedn = base_dn; DWRITE_HI("client.SET_BASEDN = " + base_dn + "\n"); return(old_dn); }
8e75a02001-11-05Honza Petrous  //! //! Sets value of scope for search operation. //! //! @param scope //! Value can be integer or its corresponding string value. //! 0: base, 1: one, 2: sub //! int set_scope (int|string scope) {
aa370a1999-04-24Johan Schön  int old_scope = ldap_scope;
8e75a02001-11-05Honza Petrous  // support for string based values if(stringp(scope)) switch (lower_case(scope)) { case "sub": scope = 2; break; case "one": scope = 1; break; case "base": scope = 0; break; default: return (-1); } else if(scope != 0 && scope != 1 && scope != 2) return (-1);
aa370a1999-04-24Johan Schön  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)); }
8e75a02001-11-05Honza Petrous  private int|string send_modifydn_op(string dn, string newrdn, int deleteoldrdn, string newsuperior) {
aa370a1999-04-24Johan Schön 
8e75a02001-11-05Honza Petrous  object msgval; array seq=({ Standards.ASN1.Types.asn1_octet_string(dn), Standards.ASN1.Types.asn1_octet_string(newrdn), Standards.ASN1.Types.asn1_boolean(deleteoldrdn) }); if(newsuperior) seq+=({Standards.ASN1.Types.asn1_octet_string(newsuperior)}); msgval = ASN1_APPLICATION_SEQUENCE(12, seq); return (do_op(msgval)); } //! The Modify DN Operation allows a client to change the leftmost //! (least significant) component of the name of an entry in the directory, //! or to move a subtree of entries to a new location in the directory. //! //! @param dn //! DN of source object //! //! @param newrdn //! RDN of destination //! //! @param deleteoldrdn //! The parameter controls whether the old RDN attribute values //! are to be retained as attributes of the entry, or deleted //! from the entry. //! //! @param newsuperior //! If present, this is the Distinguished Name of the entry //! which becomes the immediate superior of the existing entry. //! int modifydn (string dn, string newrdn, int deleteoldrdn, string|void newsuperior) { mixed raw; object rv; if (chk_ver()) return(-ldap_errno); if (chk_binded()) return(-ldap_errno); if (chk_dn(dn)) return(-ldap_errno); if(ldap_version == 3) { dn = string_to_utf8(dn); newrdn = string_to_utf8(newrdn); if(newsuperior) newsuperior = string_to_utf8(newsuperior); } if(intp(raw = send_modifydn_op(dn, newrdn, deleteoldrdn, newsuperior))) { THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); } rv = result(({raw})); DWRITE_HI(sprintf("client.MODIFYDN: %s\n", rv->error_string())); return (seterr (rv->error_number())); } //modifydn //! The Modify Operation allows a client to request that a modification //! of an entry be performed on its behalf by a server. //! //! @param dn //! The distinguished name of modified entry. //! //! @param attropval //! The mapping of attributes with requested operation and attribute's //! values. //! //! attropval=([ attribute: ({operation, value1, value2, ...}) ]) //! //! where operation is one of the following: //! 0 (LDAP_OPERATION_ADD) - //! add values listed to the given attribute, creating the attribute //! if necessary //! 1 (LDAP_OPERATION_DELETE) - //! delete values listed from the given attribute, removing the entire //! attribute if no values are listed, or if all current values //! of the attribute are listed for deletion //! 2 (LDAP_OPERATION_REPLACE) - //! replace all existing values of the given attribute with the new //! values listed, creating the attribute if it did not already exist. //! A replace with no value will delete the entire attribute if it //! exists, and is ignored if the attribute does not exist //!
aa370a1999-04-24Johan Schön  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(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  dn = string_to_utf8(dn); array(string) keys = indices(attropval); array(array(mixed)) vals = values(attropval); attropval = mkmapping(Array.map(keys, string_to_utf8), Array.map(vals, Array.map, lambda(mixed x) { return (stringp(x)? string_to_utf8(x) : x); }));
aa370a1999-04-24Johan Schön  } 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
6de1dc2000-07-14Honza Petrous  // API function // // parse_url(string ldapuri) // // ldapuri: LDAP URL mapping|int parse_url (string ldapuri) { // ldap://machine.at.home.cz:123/c=cz?attr1,attr2,attr3?sub?(uid=*)?!bindname=uid=hop,dc=unibase,dc=cz" string url=ldapuri, s, scheme; array ar; mapping res; s = (url / ":")[0]; url = url[sizeof(s)..]; res = ([ "scheme" : s ]); #ifdef LDAP_URL_STRICT if (url[..2] != "://") return(-1); #endif s = (url[3..] / "/")[0]; url = url[sizeof(s)+4..]; res += ([ "host" : (s / ":")[0] ]); if(sizeof(s / ":") > 1)
59a35e2000-07-25Honza Petrous  res += ([ "port" : (int)((s / ":")[1]) ]);
6de1dc2000-07-14Honza Petrous  ar = url / "?"; switch (sizeof(ar)) {
fc02d12000-07-20Honza Petrous  case 5: if (sizeof(ar[4])) { mapping extensions = ([]); foreach(ar[4] / ",", string ext) { int ix = predef::search(ext, "="); if(ix) extensions += ([ ext[..(ix-1)] : replace(ext[ix+1..],QUOTED_COMMA, ",") ]); } if (sizeof(extensions)) res += ([ "ext" : extensions ]); } //case 5: res += ([ "ext" : ar[4] ]);
6de1dc2000-07-14Honza Petrous  case 4: res += ([ "filter" : ar[3] ]);
fc02d12000-07-20Honza Petrous  case 3: switch (ar[2]) { case "sub": res += ([ "scope" : 2 ]); break; case "one": res += ([ "scope" : 1 ]); break; default: res += ([ "scope" : 0]); // = "base" }
6de1dc2000-07-14Honza Petrous  case 2: res += sizeof(ar[1]) ? ([ "attributes" : ar[1] / "," ]) : ([]);
fc02d12000-07-20Honza Petrous  case 1: res += ([ "basedn" : ar[0] ]);
6de1dc2000-07-14Honza Petrous  } return (res); } //parse_uri
92dbd31999-08-24Fredrik Hübinette (Hubbe) #endif