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

version» Context lines:

pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:212:   constant MODIFY_ADD = 0;   constant MODIFY_DELETE = 1;   constant MODIFY_REPLACE = 2;   //! Constants used in the @expr{attropval@} argument to   //! @[Protocols.LDAP.client.modify].      string ldap_encode_string (string str)   //! Quote characters in the given string as necessary for use as a   //! string literal in filters and various composite LDAP attributes.   //! - //! The quoting is compliant with RFCs 2252 (section 4.3) and 2254 - //! (section 4). All characters that can be special in those RFCs are - //! quoted using the @expr{\xx@} syntax, but the set might be + //! The quoting is compliant with @rfc{2252:4.3@} and + //! @rfc{2254:4@}. All characters that can be special in those RFCs + //! are quoted using the @expr{\xx@} syntax, but the set might be   //! extended.   //!   //! @seealso   //! @[ldap_decode_string], @[Protocols.LDAP.client.search]   {    return replace (str,    ({"\0", "#", "$", "'", "(", ")", "*", "\\"}),    ({"\\00", "\\23", "\\24", "\\27", "\\28", "\\29", "\\2a", "\\5c"}));   }   
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:256:    rest = rest[1..];    }    }    return res;   }      string encode_dn_value (string str)   //! Encode the given string for use as an attribute value in a   //! distinguished name (on string form).   //! - //! The encoding is according to RFC 2253 section 2.4 with the - //! exception that characters above @expr{0x7F@} aren't UTF-8 encoded. - //! UTF-8 encoding can always be done afterwards on the complete DN, - //! which also is done internally by the @[Protocols.LDAP] functions - //! when LDAPv3 is used. + //! The encoding is according to @rfc{2253:2.4@} with the exception + //! that characters above @expr{0x7F@} aren't UTF-8 encoded. UTF-8 + //! encoding can always be done afterwards on the complete DN, which + //! also is done internally by the @[Protocols.LDAP] functions when + //! LDAPv3 is used.   {    str = replace (str,    ({",", "+", "\"", "\\", "<", ">", ";"}),    ({"\\,", "\\+", "\\\"", "\\\\", "\\<", "\\>", "\\;"}));    if (has_suffix (str, " "))    str = str[..<1] + "\\ ";    if (has_prefix (str, " ") || has_prefix (str, "#"))    str = "\\" + str;    return str;   }      string canonicalize_dn (string dn, void|int strict)   //! Returns the given distinguished name on a canonical form, so it   //! reliably can be used in comparisons for equality. This means   //! removing surplus whitespace, lowercasing attributes, normalizing   //! quoting in string attribute values, lowercasing the hex digits in   //! binary attribute values, and sorting the RDN parts separated by   //! "+".   //! - //! The returned string follows RFC 2253. The input string may - //! use legacy LDAPv2 syntax and is treated according to section 4 in - //! RFC 2253. + //! The returned string follows @rfc{2253@}. The input string may use + //! legacy LDAPv2 syntax and is treated according to @rfc{2253:4@}.   //!   //! If @[strict] is set then errors will be thrown if the given DN is   //! syntactically invalid. Otherwise the invalid parts remain   //! untouched in the result.   //!   //! @note   //! The result is not entirely canonical since no conversion is done   //! from or to hexadecimal BER encodings of the attribute values. It's   //! assumed that the input already has the suitable value encoding   //! depending on the attribute type.
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:424:    //werror ("canonicalize \"%s\" -> \"%s\"\n", dn, res);    return res;   }      // Constants to make more human readable names for some known   // LDAP related object identifiers.      // LDAP controls      constant LDAP_CONTROL_MANAGE_DSA_IT = "2.16.840.1.113730.3.4.2"; - //! LDAP control: Manage DSA IT LDAPv3 control (RFC 3296): Control to - //! indicate that the operation is intended to manage objects within - //! the DSA (server) Information Tree. + //! LDAP control: Manage DSA IT LDAPv3 control (@rfc{3296@}): Control + //! to indicate that the operation is intended to manage objects + //! within the DSA (server) Information Tree.      constant LDAP_CONTROL_VLVREQUEST = "2.16.840.1.113730.3.4.9";   //! LDAP control: LDAP Extensions for Scrolling View Browsing of   //! Search Results (internet draft): Control used to request virtual   //! list view support from the server.      constant LDAP_CONTROL_VLVRESPONSE = "2.16.840.1.113730.3.4.10";   //! LDAP control: LDAP Extensions for Scrolling View Browsing of   //! Search Results (internet draft): Control used to pass virtual list   //! view (VLV) data from the server to the client.
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:555:   constant SYNTAX_OBJECT_CLASS_DESCR = "1.3.6.1.4.1.1466.115.121.1.37"; // RFC 2252, 6.24   constant SYNTAX_OID = "1.3.6.1.4.1.1466.115.121.1.38"; // RFC 2252, 6.25   constant SYNTAX_OTHER_MAILBOX = "1.3.6.1.4.1.1466.115.121.1.39"; // RFC 2252, 6.26   constant SYNTAX_POSTAL_ADDR = "1.3.6.1.4.1.1466.115.121.1.41"; // RFC 2252, 6.27   constant SYNTAX_PRESENTATION_ADDR = "1.3.6.1.4.1.1466.115.121.1.43"; // RFC 2252, 6.28   constant SYNTAX_PRINTABLE_STR = "1.3.6.1.4.1.1466.115.121.1.44"; // RFC 2252, 6.29   constant SYNTAX_PHONE_NUM = "1.3.6.1.4.1.1466.115.121.1.50"; // RFC 2252, 6.30   constant SYNTAX_UTC_TIME = "1.3.6.1.4.1.1466.115.121.1.53"; // RFC 2252, 6.31   constant SYNTAX_LDAP_SYNTAX_DESCR = "1.3.6.1.4.1.1466.115.121.1.54"; // RFC 2252, 6.32   constant SYNTAX_DIT_STRUCTURE_RULE_DESCR = "1.3.6.1.4.1.1466.115.121.1.17"; // RFC 2252, 6.33 - //! LDAP syntax: Standard syntaxes from RFC 2252. + //! LDAP syntax: Standard syntaxes from @rfc{2252@}.      constant SYNTAX_CASE_EXACT_STR = SYNTAX_DIRECTORY_STR; - //! @expr{"caseExactString"@} is an alias used in e.g. RFC 2079. + //! @expr{"caseExactString"@} is an alias used in e.g. @rfc{2079@}.      constant SYNTAX_DELIVERY_METHOD = "1.3.6.1.4.1.1466.115.121.1.14"; // RFC 2256, 6.1   constant SYNTAX_ENHANCED_GUIDE = "1.3.6.1.4.1.1466.115.121.1.21"; // RFC 2256, 6.2   constant SYNTAX_GUIDE = "1.3.6.1.4.1.1466.115.121.1.25"; // RFC 2256, 6.3   constant SYNTAX_OCTET_STR = "1.3.6.1.4.1.1466.115.121.1.40"; // RFC 2256, 6.4   constant SYNTAX_TELETEX_TERMINAL_ID = "1.3.6.1.4.1.1466.115.121.1.51"; // RFC 2256, 6.5   constant SYNTAX_TELETEX_NUM = "1.3.6.1.4.1.1466.115.121.1.52"; // RFC 2256, 6.6   constant SYNTAX_SUPPORTED_ALGORITHM = "1.3.6.1.4.1.1466.115.121.1.49"; // RFC 2256, 6.7 - //! LDAP syntax: Standard syntaxes from RFC 2256. + //! LDAP syntax: Standard syntaxes from @rfc{2256@}.      //constant SYNTAX_ACI_ITEM = "1.3.6.1.4.1.1466.115.121.1.1";   //constant SYNTAX_ACCESS_POINT = "1.3.6.1.4.1.1466.115.121.1.2";   //constant SYNTAX_AUDIO = "1.3.6.1.4.1.1466.115.121.1.4";   //constant SYNTAX_DATA_QUALITY_SYNTAX = "1.3.6.1.4.1.1466.115.121.1.13";   //constant SYNTAX_DL_SUBMIT_PERMISSION = "1.3.6.1.4.1.1466.115.121.1.18";   //constant SYNTAX_DSA_QUALITY_SYNTAX = "1.3.6.1.4.1.1466.115.121.1.19";   //constant SYNTAX_DSE_TYPE = "1.3.6.1.4.1.1466.115.121.1.20";   //constant SYNTAX_MASTER_AND_SHADOW_ACCESS_POINTS = "1.3.6.1.4.1.1466.115.121.1.29";   //constant SYNTAX_MAIL_PREFERENCE = "1.3.6.1.4.1.1466.115.121.1.32";
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:1222:    error ("Don't know the encoder corresponding to %O.\n",    syntax_decode_fns[syntax]);    syntax_encode_fns[syntax] = encoder;    }       // _standard_attr_type_descrs       // Note partial code dup with client.get_attr_type_descr.    array(mapping(string:mixed)) incomplete = ({});    -  foreach (indices (this_program), string const) { -  if (has_prefix (const, "ATD_")) { +  foreach (indices (this_program), string name) { +  if (has_prefix (name, "ATD_")) {    mapping(string:mixed) descr =    // Pike doesn't allow a type as first arg in the native []    // operator syntax. -  predef::`[] (this_program, const); +  predef::`[] (this_program, name);    if (_standard_attr_type_descrs[descr->oid])    error ("OID conflict between %O and %O.\n",    _standard_attr_type_descrs[descr->oid], descr);    if (descr->SUP) incomplete += ({descr});    _standard_attr_type_descrs[descr->oid] = descr;    foreach (descr->NAME, string name)    _standard_attr_type_descrs[lower_case (name)] = descr;    }    }       void complete (mapping(string:mixed) descr) {    mapping(string:mixed) sup_descr =    _standard_attr_type_descrs[lower_case (descr->SUP)];    if (!sup_descr)    error ("Got SUP reference to unknown attribute %O: %O\n",    descr->SUP, descr);    if (sup_descr->SUP)    complete (sup_descr);    foreach (indices (sup_descr), string term) -  if (zero_type (descr[term])) +  if (!has_index (descr, term))    descr[term] = sup_descr[term];    };    foreach (incomplete, mapping(string:mixed) descr)    complete (descr);    - #ifdef DEBUG + #ifdef LDAP_DEBUG    // To discover duplicate constant names.    get_constant_name (0);   #endif   }      protected constant supported_extensions = (<"bindname">);      //! Parses an LDAP URL and returns its fields in a mapping.   //!   //! @returns
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:1356:    constant is_generic_error = 1;    string error_message;    array error_backtrace;    string|array `[] (int i) {return i ? error_backtrace : error_message;}    protected void create (string msg, mixed... args)    {    if (sizeof (args)) msg = sprintf (msg, @args);    error_message = msg;    error_backtrace = backtrace();    error_backtrace = error_backtrace[..<1]; -  throw (this_object()); +  throw (this);    }   }      object make_filter (string filter, void|int ldap_version)   //! Parses an LDAP filter string into an ASN1 object tree that can be   //! given to @[Protocols.LDAP.search].   //!   //! Using this function instead of giving the filter string directly   //! to the search function has two advantages: This function provides   //! better error reports for syntax errors, and the same object tree   //! can be used repeatedly to avoid reparsing the filter string.   //!   //! @param filter - //! The filter to parse, according to the syntax specified in RFC - //! 2254. The syntax is extended a bit to allow and ignore + //! The filter to parse, according to the syntax specified in + //! @rfc{2254@}. The syntax is extended a bit to allow and ignore   //! whitespace everywhere except inside and next to the filter   //! values.   //!   //! @param ldap_version   //! LDAP protocol version to make the filter for. This controls what   //! syntaxes are allowed depending on the protocol version. Also, if   //! the protocol is @expr{3@} or later then full Unicode string   //! literals are supported. The default is the latest supported   //! version.   //!
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:1558:    ({ASN1_CONTEXT_OCTET_STRING (0, parts[0])}) : ({});    foreach (parts[1..<1], string middle)    subs += ({ASN1_CONTEXT_OCTET_STRING (1, middle)});    if (sizeof (parts) > 1 && sizeof (parts[-1]))    subs += ({ASN1_CONTEXT_OCTET_STRING (2, parts[-1])});       if (!sizeof (subs))    res = ASN1_CONTEXT_OCTET_STRING (7, attr); // 'present'    else    res = ASN1_CONTEXT_SEQUENCE ( // 'substrings' -  4, ({Standards.ASN1.Types.asn1_octet_string (attr), -  Standards.ASN1.Types.asn1_sequence (subs)})); +  4, ({Standards.ASN1.Types.OctetString (attr), +  Standards.ASN1.Types.Sequence (subs)}));    }       else {    int op_number;    switch (op) {    case "=": op_number = 3; break; // 'equalityMatch'    case ">=": op_number = 5; break; // 'greaterOrEqual'    case "<=": op_number = 6; break; // 'lessOrEqual'    case "~=": op_number = 8; break; // 'approxMatch'    default: // Shouldn't be reached.    FilterError ("Invalid filter type in the expression "    "starting at %O.\n", EXCERPT (subfilter));    }    res = ASN1_CONTEXT_SEQUENCE ( -  op_number, ({Standards.ASN1.Types.asn1_octet_string (attr), -  Standards.ASN1.Types.asn1_octet_string (val)})); +  op_number, ({Standards.ASN1.Types.OctetString (attr), +  Standards.ASN1.Types.OctetString (val)}));    }    }       break;    }    }       //werror ("make_filter_recur => %O (rest: %O)\n", res, filter);       if (sscanf (filter, WS")"WS"%s", filter) != 3)
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:1671:       if (sizeof (idle_conns))    periodic_idle_conns_garb_call_out =    call_out (periodic_idle_conns_garb, connection_idle_garb_interval);    else {    DWRITE ("No connections left - disabling periodic garb.\n");    periodic_idle_conns_garb_call_out = 0;    }   }    + // This mapping is used to attempt to pin connections to a single + // LDAP server in the case of clustering with DNS round-robin. + // + // Mapping from hostname to an array with the index of the most + // recently working connection in index 0, followed by ip-numbers. + protected mapping(string:array(int|string)) host_lookup = ([]); +    object/*(client)*/ get_connection (string ldap_url, void|string binddn, -  void|string password, void|int version) +  void|string password, void|int version, +  void|SSL.Context ctx)   //! Returns a client connection to the specified LDAP URL. If a bind   //! DN is specified (either through a @expr{"bindname"@} extension in   //! @[ldap_url] or, if there isn't one, through @[binddn]) then the   //! connection will be bound using that DN and the optional password.   //! If no bind DN is given then any connection is returned, regardless   //! of the bind DN it is using.   //!   //! @[version] may be used to specify the required protocol version in   //! the bind operation. If zero or left out, a bind attempt with the   //! default version (currently @expr{3@}) is done with a fallback to   //! @expr{2@} if that fails. Also, a cached connection for any version   //! might be returned if @[version] isn't specified.   //! -  + //! @[ctx] may be specified to control SSL/TLS parameters to use + //! with the @expr{"ldaps"@}-protocol. Note that changing this only + //! affects new connections. + //!   //! As opposed to creating an @[Protocols.LDAP.client] instance   //! directly, this function can return an already established   //! connection for the same URL, provided connections are given back   //! using @[return_connection] when they aren't used anymore.   //!   //! A client object with an error condition is returned if there's a   //! bind error, e.g. invalid password.   {    string pass = password;    password = "CENSORED";
pike.git/lib/modules/Protocols.pmod/LDAP.pmod/module.pmod:1769:    }       if (!sizeof (idle_conns) && periodic_idle_conns_garb_call_out) {    DWRITE ("No connections left in pool - disabling periodic garb.\n");    remove_call_out (periodic_idle_conns_garb_call_out);    periodic_idle_conns_garb_call_out = 0;    }       if (!conn) {    DWRITE("Connecting to %O.\n", ldap_url); -  conn = Protocols.LDAP["client"] (parsed_url); +  string host = parsed_url->host; +  if (host) { +  if (!host_lookup[host]) { +  array(string|array(string)) entry = +  Protocols.DNS.gethostbyname(host); +  if ((sizeof(entry) >= 2) && sizeof(entry[1])) { +  DWRITE("DNS lookup: %O.\n", entry[1]); +  host_lookup[host] = ({ random(sizeof(entry[1])) + 1 }) + entry[1]; +  } else { +  entry = gethostbyname(host); +  if ((sizeof(entry) >= 2) && sizeof(entry[1])) { +  DWRITE("Hosts lookup: %O.\n", entry[1]); +  host_lookup[host] = ({ random(sizeof(entry[1])) + 1 }) + entry[1];    } -  +  } +  } +  array(int|string) ips = host_lookup[host]; +  if (ips) { +  int i = ips[0]; +  for (int j = 1; j < sizeof(ips); j++) { +  mixed err = catch { +  DWRITE("Attempt #%d: %O.\n", j, ips[i]); +  parsed_url->host = ips[i]; +  conn = Protocols.LDAP["client"](parsed_url, ctx); +  ips[0] = i; +  break; +  }; +  if (!err) { +  break; +  } +  DWRITE("Failure: %s\n", describe_error(err)); +  if (++i >= sizeof(ips)) i = 1; +  } +  parsed_url->host = host; +  if (!conn) { +  m_delete(host_lookup, host); +  } +  } +  } +  }    -  +  if (!conn) { +  conn = Protocols.LDAP["client"] (parsed_url, ctx); +  } +     if (!binddn)    DWRITE ("Bound connection not requested.\n");    else {    if (binddn == "")    DWRITE ("Not binding - no bind DN set.\n");    else if (conn->get_bound_dn() == binddn &&    (!version || conn->get_protocol_version() == version))    DWRITE ("Not binding - already bound to %O using version %d.\n",    binddn, conn->get_protocol_version());    else {