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. //
1cbb892011-04-25Martin Stjernholm // $Id$
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)
239da62004-06-18Anders Johansson // RFC 2279 (UTF-8) // RFC 2696 (paged requests)
aa370a1999-04-24Johan Schön // // 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"
6c23602002-07-31Martin Nilsson #if constant(SSL.sslfile)
0668612001-09-14Honza Petrous import SSL.constants;
6c23602002-07-31Martin Nilsson #endif
0668612001-09-14Honza Petrous 
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())
bf334d2001-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)
b080de2001-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())
b080de2001-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)
0668612001-09-14Honza 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 = ([]);
bcc2352002-07-12Honza Petrous  private object last_rv = 0; // last returned value
aa370a1999-04-24Johan Schön 
0668612001-09-14Honza Petrous  //! Contains the result of a LDAP search. //! //! @seealso
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.search], @[LDAP.client.result.fetch]
0668612001-09-14Honza Petrous  //!
aa370a1999-04-24Johan Schön  class result // ------------------ { private int resultcode = LDAP_SUCCESS;
bf334d2001-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 = ({});
bf334d2001-08-15Honza Petrous  array(string) referrals;
aa370a1999-04-24Johan Schön 
c593c12002-01-23Honza Petrous  private string utf2s(string in) {
2e8e012002-01-23Honza Petrous  // catched variant of utf8_to_string needed for tagged octet string data
c593c12002-01-23Honza Petrous  string out = ""; catch( out = utf8_to_string(in) ); return out; }
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
c593c12002-01-23Honza Petrous  res = Array.map(res, utf2s);
fc02d12000-07-20Honza Petrous  }
aa370a1999-04-24Johan Schön  return(res); } private array _New_decode(array ar) { array res = ({}); array entry1; mapping attrs;
b080de2001-06-21Honza Petrous  object oder;
aa370a1999-04-24Johan Schön  foreach(ar, string raw1) {
b080de2001-06-21Honza Petrous  oder = (.ldap_privates.ldap_der_decode(raw1)->elements[1]);
59b0af2005-12-16Jonas Wallden  string dn = ASN1_DECODE_DN(oder); if (ldap_version == 3) dn = utf2s(dn); attrs = (["dn" : ({ dn }) ]);
161ed02002-09-05H. William Welliver III  if(catch(entry1 = ASN1_GET_ATTR_ARRAY(oder))) continue;
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
0668612001-09-14Honza 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) {
72c52b2002-11-27Marcus Comstedt  global::seterr (LDAP_LOCAL_ERROR);
aa370a1999-04-24Johan Schön  THROW(({"LDAP: Internal error.\n",backtrace()}));
72c52b2002-11-27Marcus Comstedt  return(-global::ldap_errno);
aa370a1999-04-24Johan Schön  } 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]);
bf334d2001-08-15Honza Petrous  DWRITE(sprintf("result.create: str=%s\n",resultstring)); #ifdef V3_REFERRALS
aa370a1999-04-24Johan Schön  // referral (v3 mode)
bf334d2001-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());
0668612001-09-14Honza Petrous  } //! //! Returns error number of search result. //! //! @seealso
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.result.error_string]
0668612001-09-14Honza Petrous  int error_number() { return(resultcode); }
aa370a1999-04-24Johan Schön 
0668612001-09-14Honza Petrous  //! //! Returns error description of search result. //! //! @seealso
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.result.error_number]
0668612001-09-14Honza Petrous  string error_string() { return((stringp(resultstring) && sizeof(resultstring)) ? resultstring : ldap_errlist[resultcode]); }
aa370a1999-04-24Johan Schön 
0668612001-09-14Honza Petrous  //! //! Returns the number of entries. //! //! @seealso
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.result.count_entries]
0668612001-09-14Honza Petrous  int num_entries() { return(entrycnt); } //! //! Returns the number of entries from current cursor //! possition till end of the list. //! //! @seealso
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.result.first], @[LDAP.client.result.next]
0668612001-09-14Honza Petrous  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. //!
9acfda2001-10-04Martin Nilsson  //! @param index
0668612001-09-14Honza 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); }
0668612001-09-14Honza 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]); }
0668612001-09-14Honza Petrous  //! //! Initialized the result cursor to the first entry //! in the result list. //! //! @seealso
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.result.next]
aa370a1999-04-24Johan Schön  void first() { actnum = 0; }
0668612001-09-14Honza 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
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.result.next]
aa370a1999-04-24Johan Schön  int next() { if (actnum < (num_entries()-1)) { actnum++; return(count_entries()); } return(0); }
0668612001-09-14Honza Petrous 
aa370a1999-04-24Johan Schön  } // end of class 'result' ---------------
239da62004-06-18Anders Johansson  // helper functions and macros #ifdef ENABLE_PAGED_SEARCH #define IF_ELSE_PAGED_SEARCH(X,Y) X #else /* !ENABLE_PAGED_SEARCH */ #define IF_ELSE_PAGED_SEARCH(X,Y) Y #endif #ifdef LDAP_PROTOCOL_PROFILE #define PROFILE(STR, CODE) DWRITE_PROF(STR + ": %O\n", gauge {CODE;}) #else #define PROFILE(STR, CODE) do { CODE; } while(0) #endif
aa370a1999-04-24Johan Schön  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); }
968b542001-09-14Honza Petrous  //! Several information about code itself and about active connection too
0668612001-09-14Honza Petrous  mapping info; //! @decl void create()
9acfda2001-10-04Martin Nilsson  //! @decl void create(string url) //! @decl void create(string url, object context)
0668612001-09-14Honza Petrous  //! //! Create object. The first optional argument can be used later //! for subsequence operations. The second one can specify
3c96592001-11-02H. William Welliver III  //! 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.
0668612001-09-14Honza Petrous  //! //! @param url //! LDAP server URL in form //! @tt{"ldap://hostname/basedn?attrlist?scope?ext"@} //! //! @param context //! TLS context of connection //! //! @seealso
dbd5912001-09-14Honza Petrous  //! @[LDAP.client.bind], @[LDAP.client.search]
0668612001-09-14Honza Petrous  void create(string|void url, object|void context)
aa370a1999-04-24Johan Schön  {
bcb1032008-06-20Stephen R. van den Berg  info = ([ "code_revision" : sprintf("%d.%d.%d",(int)__REAL_VERSION__,__REAL_MINOR__,__REAL_BUILD__) ]);
0668612001-09-14Honza Petrous  if(!url || !sizeof(url)) url = LDAP_DEFAULT_URL;
fc02d12000-07-20Honza Petrous 
0668612001-09-14Honza Petrous  lauth = parse_url(url);
fc02d12000-07-20Honza Petrous 
0668612001-09-14Honza Petrous  if(!stringp(lauth->scheme) ||
6c23602002-07-31Martin Nilsson  ((lauth->scheme != "ldap") #if constant(SSL.sslfile) && (lauth->scheme != "ldaps") #endif )) {
fc02d12000-07-20Honza Petrous  THROW(({"Unknown scheme in server URL.\n",backtrace()})); }
59a35e2000-07-25Honza Petrous  if(!lauth->host)
0668612001-09-14Honza Petrous  lauth += ([ "host" : LDAP_DEFAULT_HOST ]);
59a35e2000-07-25Honza Petrous  if(!lauth->port)
0668612001-09-14Honza Petrous  lauth += ([ "port" : lauth->scheme == "ldap" ? LDAP_DEFAULT_PORT : LDAPS_DEFAULT_PORT ]);
6c23602002-07-31Martin Nilsson #if constant(SSL.sslfile)
0668612001-09-14Honza Petrous  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, }); }
6c23602002-07-31Martin Nilsson #endif
0668612001-09-14Honza Petrous  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()})); }
0668612001-09-14Honza Petrous  if(lauth->scheme == "ldaps") {
058bd82004-06-14Henrik Grubbström (Grubba) #if constant(SSL.sslfile)
0668612001-09-14Honza Petrous  context->random = Crypto.randomness.reasonably_random()->read;
058bd82004-06-14Henrik Grubbström (Grubba)  ::create(SSL.sslfile(this, context, 1,1));
0668612001-09-14Honza Petrous  info->tls_version = ldapfd->version;
828dd02002-09-05H. William Welliver III #else
058bd82004-06-14Henrik Grubbström (Grubba)  error("LDAP: LDAPS is not available without SSL support.\n");
6c23602002-07-31Martin Nilsson #endif
058bd82004-06-14Henrik Grubbström (Grubba)  } else ::create(::_fd);
6c23602002-07-31Martin Nilsson 
0668612001-09-14Honza Petrous  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;
ddf0682003-07-01Anders Johansson  string pass = password; password = "censored";
aa370a1999-04-24Johan Schön  vers = Standards.ASN1.Types.asn1_integer(ldap_version); namedn = Standards.ASN1.Types.asn1_octet_string(name);
ddf0682003-07-01Anders Johansson  auth = ASN1_CONTEXT_OCTET_STRING(0, pass);
aa370a1999-04-24Johan Schön  // SASL credentials ommited msgval = ASN1_APPLICATION_SEQUENCE(0, ({vers, namedn, auth})); return (do_op(msgval)); }
0668612001-09-14Honza Petrous  //! @decl int bind()
9acfda2001-10-04Martin Nilsson  //! @decl int bind(string dn, string password) //! @decl int bind(string dn, string password, int version)
0668612001-09-14Honza 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. //!
bcc2352002-07-12Honza Petrous  //! @returns
acd2582002-07-13Honza Petrous  //! Returns @tt{1@} on success, @tt{0@} otherwise.
bcc2352002-07-12Honza Petrous  //!
0668612001-09-14Honza Petrous  //! @note //! Only simple authentication type is implemented. So be warned //! clear text passwords are sent to the directory server.
bcc2352002-07-12Honza Petrous  //! //! @note //! The API change: the returning code was changed in Pike 7.3+ //! to follow his logic better.
0668612001-09-14Honza Petrous  int bind (string|void dn, string|void password, int|void version) {
aa370a1999-04-24Johan Schön  int id; mixed raw;
ddf0682003-07-01Anders Johansson  string pass = password; password = "censored";
aa370a1999-04-24Johan Schön 
0668612001-09-14Honza Petrous  if (!version) version = LDAP_DEFAULT_VERSION;
aa370a1999-04-24Johan Schön  if (chk_ver())
bcc2352002-07-12Honza Petrous  return(0);
0668612001-09-14Honza Petrous  if (!stringp(dn)) dn = mappingp(lauth->ext) ? lauth->ext->bindname||"" : "";
ddf0682003-07-01Anders Johansson  if (!stringp(pass)) pass = "";
0668612001-09-14Honza Petrous  ldap_version = version;
aa370a1999-04-24Johan Schön  if(ldap_version == 3) {
0668612001-09-14Honza Petrous  dn = string_to_utf8(dn);
ddf0682003-07-01Anders Johansson  pass = string_to_utf8(pass);
aa370a1999-04-24Johan Schön  }
ddf0682003-07-01Anders Johansson  if(intp(raw = send_bind_op(dn, pass))) {
aa370a1999-04-24Johan Schön  THROW(({error_string()+"\n",backtrace()})); return(-ldap_errno); }
bcc2352002-07-12Honza Petrous  binded = 0; last_rv = result(({raw}),1); if (!last_rv->error_number())
aa370a1999-04-24Johan Schön  binded = 1;
bcc2352002-07-12Honza Petrous  DWRITE_HI(sprintf("client.BIND: %s\n", last_rv->error_string())); seterr (last_rv->error_number()); return binded;
aa370a1999-04-24Johan Schön  } // bind private int send_unbind_op() { // UNBIND operation writemsg(ASN1_APPLICATION_OCTET_STRING(2, "")); //ldap::close(); return (1); }
30ae7e2004-09-14Martin Stjernholm #if 0
aa370a1999-04-24Johan Schön  void destroy() { //send_unbind_op();
30ae7e2004-09-14Martin Stjernholm  // Hazard area: General confusion error. /mast //destruct(this_object());
aa370a1999-04-24Johan Schön  }
30ae7e2004-09-14Martin Stjernholm #endif
aa370a1999-04-24Johan Schön 
0668612001-09-14Honza 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))); }
0668612001-09-14Honza Petrous  //! //! Deletes entry from the LDAP server. //! //! @param dn //! The distinguished name of deleted entry.
bcc2352002-07-12Honza Petrous  //!
acd2582002-07-13Honza Petrous  //! @returns //! Returns @tt{1@} on success, @tt{0@} otherwise. //!
bcc2352002-07-12Honza Petrous  //! @note //! The API change: the returning code was changed in Pike 7.3+ //! to follow his logic better.
aa370a1999-04-24Johan Schön  int delete (string dn) { int id; mixed raw; if (chk_ver())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_binded())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_dn(dn))
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  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()}));
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  }
bcc2352002-07-12Honza Petrous  last_rv = result(({raw})); DWRITE_HI(sprintf("client.DELETE: %s\n", last_rv->error_string())); seterr (last_rv->error_number()); return (!last_rv->error_number());
aa370a1999-04-24Johan Schön  } // 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)); }
0668612001-09-14Honza Petrous  //! //! Compares given attribute value with one in the directory.
dbd5912001-09-14Honza Petrous  //! //! @param dn //! The distinguished name of compared entry. //! //! @param aval //! The mapping of compared attributes and theirs values.
bcc2352002-07-12Honza Petrous  //!
acd2582002-07-13Honza Petrous  //! @returns //! Returns @tt{1@} on success, @tt{0@} otherwise. //!
bcc2352002-07-12Honza Petrous  //! @note //! The API change: the returning code was changed in Pike 7.3+ //! to follow his logic better.
aa370a1999-04-24Johan Schön  int compare (string dn, array(string) aval) { int id; mixed raw; // if (!aval || sizeof(aval)<2) // error if (chk_ver())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_binded())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_dn(dn))
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  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()}));
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  }
bcc2352002-07-12Honza Petrous  last_rv = result(({raw})); DWRITE_HI(sprintf("client.COMPARE: %s\n", last_rv->error_string())); seterr (last_rv->error_number()); return (!last_rv->error_number());
aa370a1999-04-24Johan Schön  } // 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)); }
3c96592001-11-02H. William Welliver III  //! 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.
acd2582002-07-13Honza Petrous  //! //! @returns //! Returns @tt{1@} on success, @tt{0@} otherwise.
3c96592001-11-02H. William Welliver III  //!
bcc2352002-07-12Honza Petrous  //! @note //! The API change: the returning code was changed in Pike 7.3+ //! to follow his logic better.
aa370a1999-04-24Johan Schön  int add (string dn, mapping(string:array(string)) attrs) { int id; mixed raw; if (chk_ver())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_binded())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_dn(dn))
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  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()}));
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  }
bcc2352002-07-12Honza Petrous  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());
aa370a1999-04-24Johan Schön  } // add
fa18e32005-01-21Martin Stjernholm  static string unescape_filter_value (string val) // Decodes escapes in val according to section 4 in RFC 2254 and // section 3 in the older RFC 1960. { string res = ""; while (sscanf (val, "%s\\%s", string pre, val) == 2) { res += pre; if (sscanf (val, "%2x%s", int chr, val) == 2) res += sprintf ("%c", chr);
a6a5442005-01-26Martin Stjernholm  else if (val != "" && (<'*', '(', ')'>)[val[0]]) {
fa18e32005-01-21Martin Stjernholm  res += val[..0]; val = val[1..]; }
a6a5442005-01-26Martin Stjernholm  else { res += "\\" + val[..0]; val = val[1..]; }
fa18e32005-01-21Martin Stjernholm  } return res + val; }
aa370a1999-04-24Johan Schön  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);
3c96592001-11-02H. William Welliver III  }
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] == '\\')
1ead742004-08-23Martin Stjernholm  ahlp[-1] = ahlp[-1][..sizeof (ahlp[-1]) - 2] + "*" + filtval[cnt];
aa370a1999-04-24Johan Schön  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]))
fa18e32005-01-21Martin Stjernholm  oarr = ({ASN1_CONTEXT_OCTET_STRING( 0, unescape_filter_value (ahlp[0]))});
aa370a1999-04-24Johan Schön  } else if(cnt == ix-1) { // rightmost element if(sizeof(ahlp[ix-1]))
fa18e32005-01-21Martin Stjernholm  oarr += ({ASN1_CONTEXT_OCTET_STRING( 2, unescape_filter_value (ahlp[ix-1]))});
aa370a1999-04-24Johan Schön  } else { // inside element if(sizeof(ahlp[cnt]))
fa18e32005-01-21Martin Stjernholm  oarr += ({ASN1_CONTEXT_OCTET_STRING( 1, unescape_filter_value (ahlp[cnt]))});
aa370a1999-04-24Johan Schön  } // 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)]),
fa18e32005-01-21Martin Stjernholm  Standards.ASN1.Types.asn1_octet_string( unescape_filter_value (filter[(op+1)..]))
aa370a1999-04-24Johan Schön  }))); } } // 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");
09285c2001-11-21Anders Johansson  if (!sizeof(filter)) return make_simple_filter(filter);
aa370a1999-04-24Johan Schön  // strip leading and trailing spaces
09285c2001-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)); } }
239da62004-06-18Anders Johansson  private object|int make_search_op(string basedn, int scope, int deref, int sizelimit, int timelimit, int attrsonly, string filter, void|array(string) attrs) { // SEARCH
aa370a1999-04-24Johan Schön  // 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(({}))});
239da62004-06-18Anders Johansson  return ASN1_APPLICATION_SEQUENCE(3,
aa370a1999-04-24Johan Schön  ({ 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 })) ; }
3073c62004-07-08Marek Habersack //! @ignore
239da62004-06-18Anders Johansson IF_ELSE_PAGED_SEARCH(static multiset(string) supported_controls;,)
3073c62004-07-08Marek Habersack //! @endignore
aa370a1999-04-24Johan Schön 
bcc2352002-07-12Honza Petrous  //! Search LDAP directory. //!
66f19d2002-02-14Martin Nilsson  //! @param filter
bcc2352002-07-12Honza Petrous  //! Search filter used when searching directory objects. //!
66f19d2002-02-14Martin Nilsson  //! @param attrs
bcc2352002-07-12Honza Petrous  //! 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. //! //! @returns //! Returns object @[LDAP.client.result] on success, @tt{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] object|int search (string|void filter, array(string)|void attrs, int|void attrsonly) {
aa370a1999-04-24Johan Schön 
b080de2001-06-21Honza Petrous  int id,nv;
aa370a1999-04-24Johan Schön  mixed raw; array(string) rawarr = ({});
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())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_binded())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if(ldap_version == 3) {
6f3cc51999-08-12Marcus Comstedt  filter = string_to_utf8(filter);
aa370a1999-04-24Johan Schön  }
239da62004-06-18Anders Johansson  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 = make_search_op(ldap_basedn, ldap_scope, ldap_deref, ldap_sizelimit, ldap_timelimit, attrsonly, filter, attrs||lauth->attributes); if(intp(search_request)) {
aa370a1999-04-24Johan Schön  THROW(({error_string()+"\n",backtrace()}));
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  }
239da62004-06-18Anders Johansson  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"]) { // 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"),
50b2742005-04-20Martin Stjernholm  ASN1_BOOLEAN(sizeof(cookie->value)), // criticality
239da62004-06-18Anders Johansson  // controlValue Standards.ASN1.Types.asn1_octet_string( Standards.ASN1.Types.asn1_sequence(({ // size Standards.ASN1.Types.asn1_integer(0x7fffffff), cookie, // cookie }))->get_der()), })), }); } object controls; if (sizeof(ctrls)) { controls = .ldap_privates.asn1_sequence(0, ctrls); } ,);
aa370a1999-04-24Johan Schön 
239da62004-06-18Anders Johansson  if(intp(raw = do_op(search_request, IF_ELSE_PAGED_SEARCH(controls, 0)))) { THROW(({error_string()+"\n",backtrace()})); return 0; } }); PROFILE("rawarr++", { rawarr += ({raw});
aa370a1999-04-24Johan Schön  while (ASN1_DECODE_RESULTAPP(raw) != 5) {
239da62004-06-18Anders Johansson  PROFILE("readmsg", raw = readmsg(id));
aa370a1999-04-24Johan Schön  if (intp(raw)) { THROW(({error_string()+"\n",backtrace()}));
239da62004-06-18Anders Johansson  return 0;
aa370a1999-04-24Johan Schön  }
bf334d2001-08-15Honza Petrous  rawarr += ({raw});
aa370a1999-04-24Johan Schön  } // while
239da62004-06-18Anders Johansson  });
aa370a1999-04-24Johan Schön 
239da62004-06-18Anders Johansson  // At this point @[raw] contains a SearchResultDone. cookie = 0; IF_ELSE_PAGED_SEARCH({ if ((ASN1_DECODE_RESULTCODE(raw) != 10) && (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") { //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; } if (control->elements[pos]->type_name != "OCTET STRING") { // FIXME: Error? continue; } object control_info = .ldap_privates.ldap_der_decode(control->elements[pos]->value); if (!control_info->constructed || sizeof(control_info) < 2 || control_info->elements[1]->type_name != "OCTET STRING") { // Unexpected control information. continue; } if (sizeof(control_info->elements[1]->value)) { cookie = control_info->elements[1]; } } if (cookie) { // Remove the extra end marker. rawarr = rawarr[..sizeof(rawarr)-2]; } } },); } while (cookie); PROFILE("result", last_rv = result(rawarr));
bcc2352002-07-12Honza Petrous  if(objectp(last_rv)) seterr (last_rv->error_number());
aa370a1999-04-24Johan Schön  //if (rv->error_number() || !rv->num_entries()) // if error or entries=0 // rv = rv->error_number();
bcc2352002-07-12Honza Petrous  DWRITE_HI(sprintf("client.SEARCH: %s (entries: %d)\n", last_rv->error_string(), last_rv->num_entries())); return(last_rv);
aa370a1999-04-24Johan Schön  } // search
66f19d2002-02-14Martin Nilsson  //! @param base_dn //! base DN for search
aa370a1999-04-24Johan Schön  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); }
227d482001-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;
227d482001-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); }
66f19d2002-02-14Martin Nilsson  //! @param option_type //! LDAP_OPT_xxx //! @param value //! new value for option
aa370a1999-04-24Johan Schön  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); }
66f19d2002-02-14Martin Nilsson  //! @param option_type //! LDAP_OPT_xxx
aa370a1999-04-24Johan Schön  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)); }
3c96592001-11-02H. William Welliver III  private int|string send_modifydn_op(string dn, string newrdn, int deleteoldrdn, string newsuperior) {
aa370a1999-04-24Johan Schön 
3c96592001-11-02H. William Welliver III  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. //!
acd2582002-07-13Honza Petrous  //! @returns //! Returns @tt{1@} on success, @tt{0@} otherwise. //!
bcc2352002-07-12Honza Petrous  //! @note //! The API change: the returning code was changed in Pike 7.3+ //! to follow his logic better.
3c96592001-11-02H. William Welliver III  int modifydn (string dn, string newrdn, int deleteoldrdn, string|void newsuperior) { mixed raw; if (chk_ver())
bcc2352002-07-12Honza Petrous  return(0);
3c96592001-11-02H. William Welliver III  if (chk_binded())
bcc2352002-07-12Honza Petrous  return(0);
3c96592001-11-02H. William Welliver III  if (chk_dn(dn))
bcc2352002-07-12Honza Petrous  return(0);
3c96592001-11-02H. William Welliver III  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()}));
bcc2352002-07-12Honza Petrous  return(0);
3c96592001-11-02H. William Welliver III  }
bcc2352002-07-12Honza Petrous  last_rv = result(({raw})); DWRITE_HI(sprintf("client.MODIFYDN: %s\n", last_rv->error_string())); seterr (last_rv->error_number()); return (!last_rv->error_number());
3c96592001-11-02H. William Welliver III  } //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 //!
bcc2352002-07-12Honza Petrous  //! @returns //! Returns @tt{1@} on uccess, @tt{0@} otherwise. //! //! @note //! The API change: the returning code was changed in Pike 7.3+ //! to follow his logic better.
aa370a1999-04-24Johan Schön  int modify (string dn, mapping(string:array(mixed)) attropval) { int id; mixed raw; if (chk_ver())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_binded())
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  if (chk_dn(dn))
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  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()}));
bcc2352002-07-12Honza Petrous  return(0);
aa370a1999-04-24Johan Schön  }
bcc2352002-07-12Honza Petrous  last_rv = result(({raw})); DWRITE_HI(sprintf("client.MODIFY: %s\n", last_rv->error_string())); seterr (last_rv->error_number()); return (!last_rv->error_number());
aa370a1999-04-24Johan Schön  } // modify
bcc2352002-07-12Honza Petrous  //! Gets referrals. //! //! @returns
0b5e392002-07-22H. William Welliver III  //! Returns array of referrals or @tt{0@}. array|int get_referrals() {
bcc2352002-07-12Honza Petrous  if(last_rv->referrals) return last_rv->referrals; return 0; }
aa370a1999-04-24Johan Schön 
66f19d2002-02-14Martin Nilsson  //! @param ldapuri //! LDAP URL
6de1dc2000-07-14Honza Petrous  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