pike.git / lib / modules / Protocols.pmod / LDAP.pmod / client.pike

version» Context lines:

pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1:   #pike __REAL_VERSION__      // LDAP client protocol implementation for Pike.   // - // $Id: client.pike,v 1.67 2005/01/26 15:06:44 mast Exp $ + // $Id: client.pike,v 1.68 2005/02/03 16:55:07 mast 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,
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:363:    //! @expr{"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)    {    -  info = ([ "code_revision" : ("$Revision: 1.67 $"/" ")[1] ]); +  info = ([ "code_revision" : ("$Revision: 1.68 $"/" ")[1] ]);       if(!url || !sizeof(url))    url = LDAP_DEFAULT_URL;       lauth = parse_url(url);       if(!stringp(lauth->scheme) ||    ((lauth->scheme != "ldap")   #if constant(SSL.Cipher.CipherAlgorithm)    && (lauth->scheme != "ldaps")
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:801:    return 0;    }       last_rv = result(({raw}));    DWRITE_HI(sprintf("client.ADD: %s\n", last_rv->error_string()));    seterr(last_rv->error_number());    return !last_rv->error_number();       } // add    + static object make_control (string control_type, void|string value, +  void|int critical) + { +  array(object) seq = ({Standards.ASN1.Types.asn1_octet_string (control_type), +  ASN1_BOOLEAN (critical)}); +  if (value) seq += ({Standards.ASN1.Types.asn1_octet_string (value)}); +  return Standards.ASN1.Types.asn1_sequence (seq); + } +  + static multiset(string) supported_controls; +  + multiset(string) get_supported_controls() + //! Returns a multiset containing the controls supported by the + //! server. They are returned as object identifiers on string form. + //! A working connection is assumed. + //! + //! @seealso + //! @[search] + { +  if (!supported_controls) { +  // We need to find out if controls are supported. +  PROFILE("supported_controls", { +  supported_controls = (<>); +  object|int search_request = +  make_search_op("", 0, 0, 0, 0, 0, +  "(objectClass=*)", ({"supportedControl"})); +  //werror("search_request: %O\n", search_request); +  string|int raw; +  if(intp(raw = do_op(search_request))) { +  THROW(({error_string()+"\n",backtrace()})); +  return 0; +  } +  +  do { +  object res = .ldap_privates.ldap_der_decode(raw); +  if (res->elements[1]->get_tag() == 5) break; +  //werror("res: %O\n", res); +  foreach(res->elements[1]->elements[1]->elements, object attr) { +  if (attr->elements[0]->value == "supportedControl") { +  supported_controls |= (< +  @(attr->elements[1]->elements->value) +  >); +  //werror("supported_controls: %O\n", supported_controls); +  } +  } +  // NB: The msgid stuff is defunct in readmsg, so we can +  // just as well pass a zero there. :P +  if (intp(raw = readmsg(0))) { +  THROW(({error_string()+"\n",backtrace()})); +  return 0; +  } +  } while (1); +  }); +  } +  +  return supported_controls; + } +     /*private*/ object|int make_filter(string filter)    {      #define FILTER_PARSE_ERR(MSG...) do { \    /*werror (MSG);*/ \    return -1; \    } while (0)       // Afaict from the RFCs no insignificant whitespace is allowed in    // query filters, but the old parser and other query tools (at
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1042:    ({ 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    })) ;    }    - //! @ignore - IF_ELSE_PAGED_SEARCH(static multiset(string) supported_controls;,) - //! @endignore -  +     //! Search LDAP directory.    //!    //! @param filter    //! Search filter used when searching directory objects.    //!    //! @param attrs    //! The array of attribute names which will be returned by server.    //! for every entry.    //!    //! @param attrsonly    //! The flag causes server return only attribute name but not    //! the attribute values.    //! -  +  //! @param controls +  //! Extra controls to send in the search query, to modify how the +  //! server executes the search in various ways. The value is a +  //! mapping with an entry for each control. +  //! +  //! The mapping index is the object identifier in string form for +  //! the control type. The mapping value is an array where the +  //! first field is an integer flag and the second is the value of +  //! the control in string form. +  //! +  //! The integer flag specifies whether the control is critical or +  //! not. If it is nonzero, the server returns an error if it +  //! doesn't understand the control. If it is zero, the server +  //! ignores it instead. +  //! +  //! The value may also be zero if the control doesn't need any +  //! value at all. +  //! +  //! There are constants in @[Protocols.LDAP] for the object +  //! identifiers for some known controls. +  //!    //! @returns    //! Returns object @[LDAP.client.result] on success, @expr{0@}    //! otherwise.    //!    //! @note    //! For syntax of search filter see at RFC 1960    //! (http://community.roxen.com/developers/idocs/rfc/rfc1960.html).    //!    //! @note    //! The API change: the returning code was changed in Pike 7.3+    //! to follow his logic better.    //!    //! @seealso -  //! @[LDAP.client.result], @[LDAP.client.result.fetch] +  //! @[result], @[result.fetch], @[get_supported_controls]    object|int search (string|void filter, array(string)|void attrs, -  int|void attrsonly) { +  int|void attrsonly, +  void|mapping(string:array(int|string)) controls) {       int id,nv;    mixed raw;    array(string) rawarr = ({});       filter=filter||lauth->filter; // default from LDAP URI       DWRITE_HI("client.SEARCH: " + (string)filter + "\n");    if (chk_ver())    return 0;    if (chk_binded())    return 0;    if(ldap_version == 3) {    filter = string_to_utf8(filter);    }    -  object|int search_request; -  -  IF_ELSE_PAGED_SEARCH({ -  if (!supported_controls) { -  // We need to find out if controls are supported. -  PROFILE("supported_controls", { -  supported_controls = (<>); -  search_request = -  make_search_op("", 0, 0, 0, 0, 0, -  "(objectClass=*)", ({"supportedControl"})); -  //werror("search_request: %O\n", search_request); -  if(intp(raw = do_op(search_request))) { -  THROW(({error_string()+"\n",backtrace()})); -  return 0; -  } -  do { -  object res = .ldap_privates.ldap_der_decode(raw); -  if (res->elements[1]->get_tag() == 5) break; -  //werror("res: %O\n", res); -  foreach(res->elements[1]->elements[1]->elements, object attr) { -  if (attr->elements[0]->value == "supportedControl") { -  supported_controls |= (< -  @(attr->elements[1]->elements->value) -  >); -  //werror("supported_controls: %O\n", supported_controls); -  } -  } -  if (intp(raw = readmsg(id))) { -  THROW(({error_string()+"\n",backtrace()})); -  return 0; -  } -  } while (0); -  }); -  } -  },); -  -  search_request = +  object|int search_request =    make_search_op(ldap_basedn, ldap_scope, ldap_deref,    ldap_sizelimit, ldap_timelimit, attrsonly, filter,    attrs||lauth->attributes);       if(intp(search_request)) {    THROW(({error_string()+"\n",backtrace()}));    return 0;    }    -  +  array(object) common_controls; +  if (controls) { +  common_controls = allocate (sizeof (controls)); +  int i; +  foreach (controls; string type; array(int|string) data) +  common_controls[i++] = +  make_control (type, [string] data[1], [int] data[0]); +  } +  else common_controls = ({}); +  + #if 0 +  // Microsoft AD stuff that previously was added by default. There +  // doesn't appear to be a good reason for it. It's now possible +  // for the caller to do it, anyway. /mast +  if (get_supported_controls()[Protocols.LDAP.LDAP_SERVER_DOMAIN_SCOPE_OID]) { +  // LDAP_SERVER_DOMAIN_SCOPE_OID +  // "Tells server not to generate referrals" (NtLdap.h) +  common_controls += ({make_control (Protocols.LDAP.LDAP_SERVER_DOMAIN_SCOPE_OID)}); +  } + #endif +  + #ifdef ENABLE_PAGED_SEARCH +  get_supported_controls(); + #endif +     object cookie = Standards.ASN1.Types.asn1_octet_string("");    rawarr = ({});    do {    PROFILE("send_search_op", { -  IF_ELSE_PAGED_SEARCH( -  array ctrls = ({}); -  if (supported_controls["1.2.840.113556.1.4.1339"]) { -  // LDAP_SERVER_DOMAIN_SCOPE_OID -  // "Tells server not to generate referrals" (NtLdap.h) -  ctrls += ({ -  Standards.ASN1.Types.asn1_sequence(({ -  // controlType -  Standards.ASN1.Types.asn1_octet_string("1.2.840.113556.1.4.1339"), -  ASN1_BOOLEAN(0), // criticality (FALSE) -  // controlValue -  Standards.ASN1.Types.asn1_octet_string(""), -  })), -  }); -  } -  if (supported_controls["1.2.840.113556.1.4.319"]) { +  array ctrls = common_controls; +  IF_ELSE_PAGED_SEARCH ( +  if (supported_controls[Protocols.LDAP.LDAP_PAGED_RESULT_OID_STRING]) {    // LDAP Control Extension for Simple Paged Results Manipulation    // RFC 2696. -  ctrls += ({ -  Standards.ASN1.Types.asn1_sequence(({ -  // controlType -  Standards.ASN1.Types.asn1_octet_string("1.2.840.113556.1.4.319"), -  ASN1_BOOLEAN(sizeof(cookie->value)?0:0xff), // criticality -  // controlValue -  Standards.ASN1.Types.asn1_octet_string( -  Standards.ASN1.Types.asn1_sequence(({ +  ctrls += ({make_control ( +  Protocols.LDAP.LDAP_PAGED_RESULT_OID_STRING, +  Standards.ASN1.Types.asn1_sequence( +  ({    // size    Standards.ASN1.Types.asn1_integer(0x7fffffff),    cookie, // cookie -  }))->get_der()), -  })), -  }); -  } +  }))->get_der(), +  sizeof(cookie->value)?0:0xff)}); +  },);    object controls;    if (sizeof(ctrls)) {    controls = .ldap_privates.asn1_sequence(0, ctrls);    } -  ,); +     -  if(intp(raw = do_op(search_request, -  IF_ELSE_PAGED_SEARCH(controls, 0)))) { +  if(intp(raw = do_op(search_request, controls))) {    THROW(({error_string()+"\n",backtrace()}));    return 0;    }    });       PROFILE("rawarr++", {    rawarr += ({raw});    while (ASN1_DECODE_RESULTAPP(raw) != 5) {    PROFILE("readmsg", raw = readmsg(id));    if (intp(raw)) {
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/client.pike:1213:    (sizeof(.ldap_privates.ldap_der_decode(raw)->elements) > 2)) {    object controls = .ldap_privates.ldap_der_decode(raw)->elements[2];    foreach(controls->elements, object control) {    if (!control->constructed ||    !sizeof(control) ||    control->elements[0]->type_name != "OCTET STRING") {    //werror("Protocol error in control %O\n", control);    // FIXME: Fail?    continue;    } -  if (control->elements[0]->value != "1.2.840.113556.1.4.319") { +  if (control->elements[0]->value != +  Protocols.LDAP.LDAP_PAGED_RESULT_OID_STRING) {    //werror("Unknown control %O\n", control->elements[0]->value);    // FIXME: Should look at criticallity flag.    continue;    }    if (sizeof(control) == 1) continue;    int pos = 1;    if (control->elements[1]->type_name == "BOOLEAN") {    if (sizeof(control) == 2) continue;    pos = 2;    }