Re: olcLimits and groupOfURLs dynlist
Greetings. A summary, for the archive and for google The missing piece, from my point of view, is that it looks like the group selector, for the olcLimits option (which is what I started off looking at; and see slapd-config(5)) has similar semantics to that for the corresponding olcAccess option, more fully documented in slapd.access(5). In the documentation of the field there, we learn that 'The statement group= means that access is granted to requests whose DN is listed in the group entry whose DN is given by .' But despite slapo-dynlist saying 'Any time an entry with a specific objectClass is being returned...', this does _not_ apply here, since the next paragraph of slapd.access says 'For dynamic groups the attributeType must be a subtype of the labeledURI attributeType. Only LDAP URIs of the form ldap:///??? will be evaluated in a dynamic group, by searching the local server only.' That is, the olcAccess group processing is, in effect, restricted to the three-argument version of the attrset option of slapo-dynlist -- that's what I had missed. Presuming the olcLimits option has the same restriction, then the effect I was initially aiming to achieve -- setting a limit for members of a particular group which is dynamically populated -- is not possible for me by this route. The groups I'm aiming to set limits and access for are most naturally defined from the union of other groups. Such groups are easy to define via the two-argument dynlist-attrset value (which uses ldap:///?member?sub?), but not, as far as I can see, via the three-argument one. I can probably instead synthesise the groups I want, dynamically, by introducing a memberOf attribute attached to the groups' members, but I worry that has the potential to get a little messy in practice; I notice group.expand, which might help. I notice that the documentation of olcAccess doesn't actually mention the dynlist overlay, and thus may be entirely independent of it. Something for me to investigate. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: olcLimits and groupOfURLs dynlist
Howard, hello. On 8 Feb 2024, at 16:22, Howard Chu wrote: >> And slapo-dynlist says: >> >> Any time an entry with a specific objectClass is being returned, >> the LDAP URI-valued occurrences of a specific attribute are expanded >> into the corresponding entries, and the values of the attributes listed >> in the URI are added to the original entry. > > The text above is for a *dynamic list* - which is not a *dynamic group*. Sure -- no dispute about that. But we're talking about olcLimits. The documentation for olcLimits includes the words the oc group objectClass (default groupOfNames) whose DN exactly matches pattern. That doesn't say anything about restricting these to 'dynamic groups' (in slapo-dynlist terminology). Those words seem to cover any entry of the designated objectClass which has the designated DN. The olcLimits declaration I quoted works one way when the entry with the given DN is a static/normal/explicit group, and works a different way when an entry with the same DN and the _same_ set of 'member' attributes is produced on expansion by dynlist. The documentation of olcLimits doesn't suggest that's deliberate. Again, if OpenLDAP/dynlist is incapable of generating this entry, then that's fine -- I'll bodge some different way of getting what I need. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: olcLimits and groupOfURLs dynlist
Howard, hello. On 8 Feb 2024, at 15:07, Howard Chu wrote: >> Norman Gray wrote: >> >> Howard, hello. >> >> On 8 Feb 2024, at 0:34, Howard Chu wrote: >> >>> 65c3df21.21fc2a30 0x16cacf000 >>> ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs))) >>> >>> The above URL is not valid for a dynamic group. The attrs portion of the >>> URL must be empty. >>> >>> Since it's invalid, after it is parsed it gets ignored. >> >> That's true when constructing what slapo-dynlist(5) calls a dynamic >> group, but that's not what I'm constructing here, but instead a group >> entry which is dynamically expanded, to a group, by a search. > > Whatever you've constructed is not a dynamic group, as defined in > slapo-dynlist. > As such, it is not supported for the purpose you're asking. Indeed -- it's not a 'dynamic group' in the terms of slapo-dynlist, but it is an entry which has a set of 'member' attributes, which is dynamically constructed (whatever one wants to call this). But I can't see that matters, since the slapd-config(5) text covering the olcLimits configuration attribute seems to clearly indicate that olcLimits: group/groupOfURLs/member="cn=ldap-operators,ou=groups,o=example" size=2 'sets the limits for any DN listed in the values of the [member] attribute of the [groupOfURLs] group whose DN exactly matches ["cn=ldap-operators,ou=groups,o=example"]' (where [...] fills in the blanks in the text there as I understand it). I can't see a way of interpreting this manpage text which doesn't match this situation. This works as expected when cn=ldap-operators is an entry which is not dynamically expanded. It doesn't say that that group has to be a 'dynamic group in the terms of slapo-dynlist', it just says 'group'. And slapo-dynlist says: > Any time an entry with a specific objectClass is being returned, > the LDAP URI-valued occurrences of a specific attribute are expanded > into the corresponding entries, and the values of the attributes listed > in the URI are added to the original entry. This is exactly what happens when I ldapsearch the directory for this cn=ldap-operators entry, and what does not happen (because slapd logs that it can't find an attribute 'member') when the same group is returned from a search during processing of olcLimits. The slapo-dynlist text says 'Any time an entry with a specific objectClass is being returned...'. It doesn't say 'returned in response to an external query', it just says 'returned', which I of course take to include returned in response to an internal query such as this one. Or, stepping back more, how _should_ I dynamically create an entry which olcLimits will respect? I'm quite happy to be told I'm barking up the wrong tree here. Is OpenLDAP simply unable to do this, or is dynlist expansion documented somewhere as happening only in restricted circumstances? Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: olcLimits and groupOfURLs dynlist
Howard, hello. On 8 Feb 2024, at 0:34, Howard Chu wrote: >> 65c3df21.21fc2a30 0x16cacf000 >> ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs))) > > The above URL is not valid for a dynamic group. The attrs portion of the URL > must be empty. > > Since it's invalid, after it is parsed it gets ignored. That's true when constructing what slapo-dynlist(5) calls a dynamic group, but that's not what I'm constructing here, but instead a group entry which is dynamically expanded, to a group, by a search. Hmm: I realise there's a collision of terminology here, and that I used the specific phrase 'dynamic group' in the first message (but managed to avoid it in the second). slapo-dynlist(5) does indeed consistently use 'dynamic group' to refer to the case where the olcDynListAttrSet has three values, and the generated entry contains the DNs of the matching entries. Here, however, I'm using the two-argument case, and defining a group as the union of a number of groupOfNames groups. That's a group which is dynamic, but perhaps 'dynamically generated group' would be a less colliding name than 'dynamic group'. Anyway... slapd.ldif: dn: olcOverlay=dynlist,olcDatabase={3}mdb,cn=config objectClass: olcOverlayConfig objectClass: olcDynlistConfig olcOverlay: dynlist olcDynListAttrSet: groupOfURLs memberURL and group definition: dn: cn=ldap-operators,ou=groups,o=example cn: ldap-operators objectClass: groupOfURLs description: Members of all of the LDAP admin and tech groups memberURL: ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)) When I search for this, I do indeed get a group that looks as I'd hope: % ldapsearch -x -H ldap://localhost:8389 -b o=example -LLL '(cn=ldap-operators)' dn: cn=ldap-operators,ou=groups,o=example cn: ldap-operators objectClass: groupOfURLs description: Members of all of the LDAP admin and tech groups memberURL: ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)) member: uid=norman,ou=staff,o=example [...] That is, the dynlist overlay has synthesised an entry which has a number of 'member' attributes. That looks good, but this doesn't have the expected effect when I use this group in the olcLimits directive. olcLimits: group/groupOfURLs/memberURL="cn=ldap-operators,ou=groups,o=example" size=2 (or group/groupOfURLs/member). Speculation: The objectClass of the above group is groupOfURLs, and not groupOfNames, but the olcLimits documentation mentions groupOfNames only as the default for the /oc element of this spec, and not as a general requirement. The short version is that if I look at the documentation for olcLimits, it says: > The term group, with the optional objectClass oc and attributeType at > fields, followed by pattern, sets the limits for any DN listed in the > values of the at attribute (default member) of the oc group > objectClass (default groupOfNames) whose DN exactly matches pattern. Using dynlist, I have synthesised a group with what appear to be the required properties, but olcLimits isn't processing it as I expect. The only difference is that the group is dynamic rather than fixed. What is wrong with my expectation? Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: olcLimits and groupOfURLs dynlist
Howard, hello. On 7 Feb 2024, at 19:36, Howard Chu wrote: >> If I then make a query which has a few results, I do not get this limit >> imposed, and instead see in the logs >> >> 65c3ce83.0f52bea8 0x16e9d3000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" >> 65c3ce83.0f533f90 0x16e9d3000 <= mdb_entry_get: failed to find attribute >> member > > And those logs are correct, the group entry you specified has no member > attribute. > What it has is a memberURL attribute, and that's what you should have > configured > in your olcLimits statement. Aha. I had taken the description to refer to the synthesised 'member' attributes in the dynamically generated group. Thanks for this. On changing this, though, to olcLimits: group/groupOfURLs/memberURL="cn=ldap-operators,ou=groups,o=example" size=2 and making a query, I now see in the logs (with -d-1): 65c3df21.21fa70c8 0x16cacf000 ==> limits_get: conn=1000 op=1 self="uid=norman,ou=staff,o=example" this="o=example" 65c3df21.21fa97d8 0x16cacf000 => mdb_entry_get: ndn: "cn=ldap-operators,ou=groups,o=example" 65c3df21.21fab718 0x16cacf000 => mdb_entry_get: oc: "groupOfURLs", at: "memberURL" 65c3df21.21fb1ca8 0x16cacf000 mdb_dn2entry("cn=ldap-operators,ou=groups,o=example") 65c3df21.21fb4b88 0x16cacf000 => mdb_dn2id("cn=ldap-operators,ou=groups,o=example") 65c3df21.21fb8a08 0x16cacf000 <= mdb_dn2id: got id=0x2857 65c3df21.21fbb8e8 0x16cacf000 => mdb_entry_decode: 65c3df21.21fbd440 0x16cacf000 <= mdb_entry_decode 65c3df21.21fbef98 0x16cacf000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3df21.21fc0ed8 0x16cacf000 mdb_entry_get: rc=0 65c3df21.21fc2a30 0x16cacf000 ldap_url_parse_ext(ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs))) 65c3df21.21fc7c38 0x16cacf000 => mdb_search 65c3df21.21fcb6d0 0x16cacf000 mdb_dn2entry("o=example") 65c3df21.21fcd9f8 0x16cacf000 => mdb_dn2id("o=example") 65c3df21.21fcf938 0x16cacf000 <= mdb_dn2id: got id=0x1 65c3df21.21fd1490 0x16cacf000 => mdb_entry_decode: 65c3df21.21fd2fe8 0x16cacf000 <= mdb_entry_decode 65c3df21.21fd4b40 0x16cacf000 => access_allowed: search access to "o=example" "entry" requested There's no mention of 'limits' after this point in the log. Thus it's finding the right entry and attribute, and parsing the URL therein, but it's not clear what it's concluding. When a search is performed as a user who is included in the synthesised cn=ldap-operators (confirmed by a search for that group), the query results are not limited to 2 objects. That 2-object limit is what I see in the corresponding configuration when ldap-operators is a groupOfNames with explicit member attributes: 65c3e6ae.1da1a5c8 0x16e80b000 ==> limits_get: conn=1000 op=1 self="uid=norman,ou=staff,o=example" this="o=example" 65c3e6ae.1da1c8f0 0x16e80b000 => mdb_entry_get: ndn: "cn=ldap-operators,ou=groups,o=example" 65c3e6ae.1da1e060 0x16e80b000 => mdb_entry_get: oc: "groupOfNames", at: "member" 65c3e6ae.1da226b0 0x16e80b000 mdb_dn2entry("cn=ldap-operators,ou=groups,o=example") 65c3e6ae.1da24dc0 0x16e80b000 => mdb_dn2id("cn=ldap-operators,ou=groups,o=example") 65c3e6ae.1da28088 0x16e80b000 <= mdb_dn2id: got id=0x2857 65c3e6ae.1da2ab80 0x16e80b000 => mdb_entry_decode: 65c3e6ae.1da2c6d8 0x16e80b000 <= mdb_entry_decode 65c3e6ae.1da2de48 0x16e80b000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3e6ae.1da2fd88 0x16e80b000 mdb_entry_get: rc=0 65c3e6ae.1da31cc8 0x16e80b000 dnMatch 0 "uid=norman,ou=staff,o=example" "uid=norman,ou=staff,o=example" 65c3e6ae.1da33c08 0x16e80b000 <== limits_get: type=GROUP match=EXACT dn="cn=ldap-operators,ou=groups,o=example" oc="groupOfNames" ad="member" 65c3e6ae.1da36700 0x16e80b000 => mdb_search 65c3e6ae.1da3bcf0 0x16e80b000 mdb_dn2entry("o=example") 65c3e6ae.1da3e018 0x16e80b000 => mdb_dn2id("o=example") 65c3e6ae.1da3fb70 0x16e80b000 <= mdb_dn2id: got id=0x1 65c3e6ae.1da41ab0 0x16e80b000 => mdb_entry_decode: 65c3e6ae.1da43220 0x16e80b000 <= mdb_entry_decode 65c3e6ae.1da44d78 0x16e80b000 => access_allowed: search access to "o=example" "entry" requested (interestingly, the string 'limit' doesn't subsequently appear in this -d-1 log, either) So I'm afraid I'm still puzzled. Norman -- Norman Gray : https://nxg.me.uk
Re: The unique overlay: enforcing uniqueness in the union of trees
Quanah, hello. On 7 Feb 2024, at 19:26, Quanah Gibson-Mount wrote: > Since it was historically done this way, yeah, best thing is to slowly fix > the data until it can be done correctly. It's really a local case of NIS. Must. Die Norman -- Norman Gray : https://nxg.me.uk
olcLimits and groupOfURLs dynlist
Greetings. I have another puzzle with my OpenLDAP configuration, where I'm not sure if what I'm seeing is unexpected. Short version: should I expect a group in an olcLimits spec to work when the group is dynamic? I have a dynamic group set up, using the dynlist overlay, which expands to a set of DNs which should be allowed slightly privileged access to a directory. That group seems to be working OK: % ldapsearch -x -H ldap://localhost:8389 -b o=example -LLL '(cn=ldap-operators)' dn: cn=ldap-operators,ou=groups,o=example cn: ldap-operators objectClass: groupOfURLs description: Members of all of the LDAP admin and tech groups memberURL: ldap:///ou=groups,o=example?member?sub?(|(cn=ldap-admins-*)(cn=ldap-techs)) member: uid=norman,ou=staff,o=example [...] One goal here is to remove query limits for this group. I can test that by adding an artificially low limit: olcLimits: group/groupOfURLs/member="cn=ldap-operators,ou=groups,o=example" size=2 If I then make a query which has a few results, I do not get this limit imposed, and instead see in the logs 65c3ce83.0f52bea8 0x16e9d3000 => mdb_entry_get: found entry: "cn=ldap-operators,ou=groups,o=example" 65c3ce83.0f533f90 0x16e9d3000 <= mdb_entry_get: failed to find attribute member (If, instead of this, I define an ldap-operators group of class groupOfNames, with the above 'member' included explicitly, and make the corresponding change to the olcLimits line, I get what I expect -- ie, a restricted-size response to the query -- which reassures me I'm not doing something stupid elsewhere.) The slapo-dynlist(5) page says: > Any time an entry with a specific objectClass is being returned, the > LDAP URI-valued occurrences of a specific attribute are expanded into > the corresponding entries, and the values of the attributes listed in > the URI are added to the original entry. I note the ‘any time’. My configuration appears to be working for the ldapsearch lookup; I don't see any text in that manpage that suggests this won't work for the (somehow internal?) lookup being done when processing the olcLimits expression. The page slapd-config(5) says, under olcLimits: > The term group, with the optional objectClass oc and attributeType at > fields, followed by pattern, sets the limits for any DN listed in the > values of the at attribute (default member) of the oc group > objectClass (default groupOfNames) whose DN exactly matches pattern. That text doesn't seem to me to exclude this entry lookup from the ‘any time’ in the slapo-dynlist text above. This is OpenLDAP 2.6.7. I am of course open to a frame-challenge about the best way of achieving the underlying goal. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: The unique overlay: enforcing uniqueness in the union of trees
Quanah, hello. On 6 Feb 2024, at 16:03, Quanah Gibson-Mount wrote: > Questions about slapo-unique aside, this is a horrific way to organize your > data tree. I'd strongly advise creating a tree for people, like: > > cn=people,dc=example,dc=com > > uid=x,cn=people,dc=example,dc=com > uid=y,cn=people,dc=example,dc=com > > Store what department(s) they belong to as attribute in their user entry. I take the point, and I certainly wouldn't organise things this way if _I_ were king. In this case, though, dept1, dept2, and so on, are separate administrative domains, in both IT terms and real bureaucratic ones, and this is an attempt to bring some sort of coherence to a bit of historic anarchy (and yes, there is an ou=staff layer in the middle of the real trees). Everyone more-or-less agrees on the names and uidNumbers in dept1, but there might be a local 'norman' in both dept2 and dept3, or people in those trees with historically colliding UIDs. The result is that systems in dept2 will acknowledge users in ou=dept1 and ou=dept2, users in dept3 acknowledge ou=dept1 and dept3 but ignore ou=dept2, and so on. I expect that names will soon no longer be created in the deptN trees (pretty please?), in favour of the dept1 tree, and the ou=staff parts of those will atrophy, but I'll be retired by then. If there's a different way of approaching that particular problem, though, right now is the time for me to be rethinking this, so I'm open to challenge. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
The unique overlay: enforcing uniqueness in the union of trees
d 2.6.7 (Jan 1 1980 00:00:00) $ openldap Included static overlays: accesslog syncprov unique Included static backends: config ldif monitor mdb relay -- Norman Gray : https://nxg.me.uk
Re: Transitioning from slapd.conf to slapd.d, best practices for maintaining configuration comments?
Ben, hello. On 15 Nov 2023, at 18:58, Ben Poliakoff wrote: > Looking for any tips about how > best to annotate slapd configuration, in a slapd.d/olc world. Does anyone > have a practice that they find works well for them? What works for me (in a primary+replicas setup) is to maintain a slapd.ldif file with structured comments in it (ie, #@PRIMARY@ and #@REPLICA@ marking different variants), and when changes need to be made to the configuration, I stop the primary server (leaving things to replicas), slapcat the data, rebuild the slapd.d from scratch with the appropriate version of the configuration file, reload the data, and restart; then do the same with the replicas. This isn't ideal, but it works for me because the window when no-one can write, because the primary is off, is acceptably small. The advantages are * I can version-control (and of course densely comment) the configuration, with all the attendant advantages * I know exactly what the configuration of the server is, without querying the server * because they're both generated from the same source, I know for sure that the primary and replicas have compatible configurations * that means I can have a test server (including scratch regression-test servers) with a duplicate configuration I can see how I could achieve most of these things using slapd.d as intended. But this route feels more direct, and thus more intelligible to me. The first advantage seems the key one, to me. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: Proposal to strengthen slapd EXTERNAL authentication
Greetings. On 28 Jun 2023, at 3:41, Jordan Brown wrote: > On 6/27/2023 7:14 PM, Quanah Gibson-Mount wrote: > >> Using a public CA for client certs seems very odd to me. > > Depends on your use case. Think of it as a form of federated login. Indeed. I've done something similar in the past (this was with access to a web service rather than an LDAP server, but the logic is the same). Some of my users had, and knew how to use, X.509 certs issued by a large computing grid. So I got my server to trust the CA's cert, and listed the DNs allowed access. The grid CA did the legwork of setting up the PKI and checking the users, and I piggybacked on that, feeling rather smart. Unfortunately, not _all_ of the relevant users had those certs, so I still had to set up a local CA, which meant it ended up more trouble than it was in fact worth. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: Tools parsing dc=foo URLs
Uwe, helllo. On 20 Feb 2023, at 13:28, Uwe Sauter wrote: > ldapsearch -x -H 'ldap:///dc=example,dc=net' '(cn=foo)' > > instead of > > ldapsearch -x -H 'ldap:///dc=example,dc=net' '(cn=foo)' No: the documentation for the -H option says that the SRV-lookup form, with the dc= components, is only tried when the host:port element of the URI is empty. Looking at the code, the problem does appear to be that common.c:tool_args attempts to break the -H argument into multiple LDAP URIs by separating at commas, as well as spaces. That's the right thing to do to match the first sentence of the ldapsearch(1) documentation of -H, but the second sentence there (the one starting 'As an exception...') doesn't mention a list of SRV names (sensibly, since an SRV record is implicitly a list anyway), so shouldn't, I think, attempt to split the argument. As the code stands, you have to escape the ',' in order to trick the code into not splitting this argument. I noted in the OP that there's a mild code change which would make this work as expected. I'm raise this as a bugreport with suggested fix, since it doesn't sound from this discussion that there's something obvious I'm missing. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: Tools parsing dc=foo URLs
Ede, hello. On 20 Feb 2023, at 12:49, Ede Wolf wrote: > Just guessing by the error message, but your DNS Server does have a valid > service record for that Base DN? As the error message suggests otherwise, but > I may as well be completely off here. A good thought -- thanks -- but yes, the SRV records are set up correctly, and I can make the query if the ',' in the dc= list are escaped (even though RFC 2396 doesn't require that). > Otherwise, but I am sure you know that, with only two "/" you can alway use > the hostname, in case the DNS serive record is missing: > > ldapsearch -H ldap://server.example.net -b dc=example,dc=net" 'cn=foo' Indeed, and that's what I do most of the time. In fact, this is a case where a sequence of host+port LDAP URIs is useful. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Tools parsing dc=foo URLs
Greetings. This command fails in an unexpected way: % ldapsearch -x -H 'ldap:///dc=example,dc=net' '(cn=foo)' Could not parse LDAP URI(s)=ldap:///dc=example,dc=net (3) It appears that ldapsearch wants me to escape the '=' and ',' in that URI: % ldapsearch -x -H 'ldap:///dc%3dexample%2cdc%3dnet' '(cn=foo)' DNS SRV: Could not turn domain=example.net into a hostlist But why? The manpage for ldapsearch says -H ldapuri Specify URI(s) referring to the ldap server(s); a list of URI, separated by whitespace or commas is expected; only the protocol/host/port fields are allowed. As an exception, if no host/port is specified, but a DN is, the DN is used to look up the corresponding host(s) using the DNS SRV records, according to RFC 2782. The DN must be a non-empty sequence of AVAs whose attribute type is "dc" (domain component), and must be escaped according to RFC 2396. I read that as clearly saying (via the 'exception' branch of that paragraph) that the first -H argument is correct. RFC digression: According to RFC 2396, the /dc... is `"/" path_segments`, segments are composed of *pchar, and pchar = unreserved | escaped | ":" | "@" | "&" | "=" | "+" | "$" | "," ...which includes both '=' and ','. Thus those characters don't need to be escaped, by RFC 2396. Or, put another way, 'ldap:///dc=example,dc=net' _is_ escaped according to RFC2396, in the sense that nothing in it needs to be escaped. Looking instead at RFC 4516, the 'dn' in the 'ldapurl' is a 'distinguishedName' from RFC 4514 which (Sect.3) permits '=' and ',' to be included. Sect.2.1 of 4516 requires that the URI must include , or of RFC 3986, but if we look at that, then Sect.2.2 indicates that includes both '=' and ','. Thus the behaviour of ldapsearch, when parsing the -H option, doesn't appear to match the documentation. Explanation: Looking at common.c:tool_args and common.c line 1199, I see that it calls ldap_url_parselist to break the -H argument into a list of URIs, and this will separate dc=example,dc=net at the comma. And sure enough, in practice it's only the ',' that has to be escaped by %2c. I believe this behaviour doesn't match the manpage, which (clearly in my reading of it) requires either a list of protocol/host/port URIs OR (the exception) a single URI containing no host/port but only a DN. That suggests that common.c:tool_args has to detect that exception/second case. Apart from the documentation issue, having to escape commas is both repeatedly surprising and a pain in the neck on the occasions when I want to use the dc=... syntax with ldapsearch. Re detecting that exception, searching for "///" in the ldapuri string would seem to be sufficient, and calling ldap_url_parselist_int in that case (instead of ldap_url_parselist) with a sep argument of " " looks like it would do the job with a minimal change to the code. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: using SRV-records in syncrepl
Ondřej, hello. On 20 Jan 2023, at 16:19, Ondřej Kuzník wrote: > DNS retrieval and parsing of SRV records is the easy part, and libevent > has code to help us with that already. It's the management of the > backends on the tier (shutting down those that aren't listed anymore, > adding those that are new, rearranging the lists) all while the server > is still running unless we request a full server pause for it. And any > of the backends being removed might still have pending operations, what > do we do with those? > > And then the code that uses those levels and weights for each request > that comes in, in an efficient way. Ah, right -- fair enough. I'd misunderstood where the gap was. Looking at ldap_domain2hostlist, I still think my sort algorithm is more elegant, here, than use of the Fisher-Yates one (*cough*), but it's not like this is going to be a hotspot in the code! Changing the subject slightly, since there is SRV support in libldap, is there any chance that it'll become possible to specify a SRV domain in ldap.conf, in place of the list of LDAP URIs in option 'URI'. That is, it'd be useful to be able to say 'URI DNS:ldap.example.com' in there. Supporting something similar to that is why I wrote this code. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: using SRV-records in syncrepl
Quanah, hello. On 20 Jan 2023, at 15:43, Quanah Gibson-Mount wrote: > There is no license noted in this file, which would be a requirement to even > look at it. No problem -- I've reattached a version with the BSD 2-clause licence included. Is that an OK one for you? Best wishes, Norman -- Norman Gray : https://nxg.me.uk // DNS support -- return information about LDAP servers, // by looking up a SRV record. // // SRV records, as discussed in RFC 2782, point to locations of // services. In the words of that RFC, // // If a SRV-cognizant LDAP client wants to discover a LDAP server that // supports TCP protocol and provides LDAP service for the domain // example.com., it does a lookup of // // _ldap._tcp.example.com // // The get_sorted_srv_records() function below will take a domain example.com, // prepend the "_ldap._tcp", // do the lookup to obtain the SRV record, // and return a string containing the hosts and ports, // sorted as described in the RFC. // // It's unexpectedly hard to find clear documentation about DNS lookups, // but the following does seem to be portable, // though without much in the way of intelligible manpages on any platform. // // See: // Scrappy documentation: https://docstore.mik.ua/orelly/networking_2ndEd/dns/ch15_02.htm // Wikipedia: https://en.wikipedia.org/wiki/List_of_DNS_record_types // RFC 1035, Domain names -- implementation and specification // // This module exposes a function // //char* get_sorted_srv_records(const char* domain); // // which returns a space-separated list of ldap:// URIs obtained from // the SRV record corresponding to the given domain, in a random order // appropriate to the weights obtained. // // This can be compiled as a standalone (test?) program; see STANDALONE below. // // LICENCE: this is licensed under the terms of the BSD 2-clause licence. // The licence text is at the bottom of the file. // CentOS features.h requires _BSD_SOURCE in order to get types.h to define u_char, // which resolv.h refers to. #define _BSD_SOURCE 1 // Debian prefers _DEFAULT_SOURCE, and issues a deprecation warning // if _BSD_SOURCE is defined and _DEFAULT_SOURCE isn't. #define _DEFAULT_SOURCE 1 // ... and _XOPEN_SOURCE to get getopt // (on eg CentOS, this also happens with _POSIX_C_SOURCE >= 2, but // that _prevents_ macOS types.h from defining u_char). #define _XOPEN_SOURCE 1 #include #include #include #include #include #include "config.h" // See the docs for autoconf AC_HEADER_RESOLV #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_NETINET_IN_H # include/* inet_ functions / structs */ #endif #ifdef HAVE_ARPA_NAMESER_H # include /* DNS HEADER struct */ #endif #ifdef HAVE_NETDB_H # include #endif #include // There is a test main program at the bottom of this file. // To compile: // cc -Wall -std=c99 -o dns-support -DSTANDALONE=1 -lresolv dns-support.c // #ifndef STANDALONE #define STANDALONE 0 #endif #if STANDALONE #include #include // dummy out functions provided by the other modules in this kit static int _chatter = 2; int get_verbosity(void) { return _chatter; } void change_verbosity(int inc) { _chatter += inc; } void log_err_message(void* dummy, const char* fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } #else // we call get_verbosity and log_err_message #include "ldaputil.h" #include "ldap-config.h" #endif typedef unsigned char byte; // Return a uniform random deviate in [0,1) // // random(3) returns a number in [0, 2^31-1]. // We convert this to a double by dividing it by 2^31 exactly. // Thus the return value is always strictly less than one. static double random_f(void) { static byte initialised_p = 0; static double denom; if (! initialised_p) { srandom(time(NULL)); denom = ldexp(1, 31); initialised_p = 1; // Explore a few properties of 'denom' // int exp; // double mantissa = frexp(denom, ); // printf("%g -> %f.2^%d; ", denom, mantissa, exp); // double two31 = (double)2147483647; // 2^31-1 as an integer // mantissa = frexp(two31, ); // printf("%g -> %f.2^%d; ", two31, mantissa, exp); // printf("ratio = %g, less than one? %s\n", //two31/denom, two31/denom < 1.0 ? "yes" : "no"); } double univariate = (double)random(); return univariate / denom; } // Uncompress a domain-name. // The returned pointer points to storage which is reused on each invocation. static const char* uncompress(ns_msg msg, const byte* p, int* skiplen_p) { static char ans[NS_MAXDNAME]; int skiplen = ns_name_uncompress(ns_msg_base(msg), ns_msg_end(msg), p, ans, sizeof(ans)); if (skiplen_p != NULL) *skiplen_p = skiplen; return (skip
Re: using SRV-records in syncrepl
And... On 20 Jan 2023, at 15:33, Norman Gray wrote: > This exposes a function > > char* get_sorted_srv_records(const char* domain); > > which does a SRV lookup, and orders the records that come back according to > the specification of RFC 2782 (though in a single pass, rather than the > clumsy multiple pass algorithm that the RFC suggests). I should mention that the way I use this is ldap_server = "ldap://foo;; // or ldap_server = "DNS:ldap.example.com"; char* server_list; if (strncmp(ldap_server, "DNS:", 4) == 0) { server_list = get_sorted_srv_records(_server[4]); } else { server_list = ldap_server; } ldap_initialise(, server_list); Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: using SRV-records in syncrepl
Ondřej, hello. On 20 Jan 2023, at 10:47, Ondřej Kuzník wrote: > That said, patches implementing some kind of SRV are welcome. The easiest > way might be to introduce an lloadd tier implementation that manages its > backend collection accordingly. It's not an OpenLDAP patch, but I've attached a module which might be of interest here. This exposes a function char* get_sorted_srv_records(const char* domain); which does a SRV lookup, and orders the records that come back according to the specification of RFC 2782 (though in a single pass, rather than the clumsy multiple pass algorithm that the RFC suggests). Best wishes, Norman -- Norman Gray : https://nxg.me.uk // DNS support -- return information about LDAP servers, // by looking up a SRV record. // // SRV records, as discussed in RFC 2782, point to locations of // services. In the words of that RFC, // // If a SRV-cognizant LDAP client wants to discover a LDAP server that // supports TCP protocol and provides LDAP service for the domain // example.com., it does a lookup of // // _ldap._tcp.example.com // // The get_sorted_srv_records() function below will take a domain example.com, // prepend the "_ldap._tcp", // do the lookup to obtain the SRV record, // and return a string containing the hosts and ports, // sorted as described in the RFC. // // It's unexpectedly hard to find clear documentation about DNS lookups, // but the following does seem to be portable, // though without much in the way of intelligible manpages on any platform. // // See: // Scrappy documentation: https://docstore.mik.ua/orelly/networking_2ndEd/dns/ch15_02.htm // Wikipedia: https://en.wikipedia.org/wiki/List_of_DNS_record_types // RFC 1035, Domain names -- implementation and specification // // This module exposes a function // //char* get_sorted_srv_records(const char* domain); // // which returns a space-separated list of ldap:// URIs obtained from // the SRV record corresponding to the given domain, in a random order // appropriate to the weights obtained. // // This can be compiled as a standalone (test?) program; see STANDALONE below. // CentOS features.h requires _BSD_SOURCE in order to get types.h to define u_char, // which resolv.h refers to. #define _BSD_SOURCE 1 // Debian prefers _DEFAULT_SOURCE, and issues a deprecation warning // if _BSD_SOURCE is defined and _DEFAULT_SOURCE isn't. #define _DEFAULT_SOURCE 1 // ... and _XOPEN_SOURCE to get getopt // (on eg CentOS, this also happens with _POSIX_C_SOURCE >= 2, but // that _prevents_ macOS types.h from defining u_char). #define _XOPEN_SOURCE 1 #include #include #include #include #include #include "config.h" // See the docs for autoconf AC_HEADER_RESOLV #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_NETINET_IN_H # include/* inet_ functions / structs */ #endif #ifdef HAVE_ARPA_NAMESER_H # include /* DNS HEADER struct */ #endif #ifdef HAVE_NETDB_H # include #endif #include // There is a test main program at the bottom of this file. // To compile: // cc -Wall -std=c99 -o dns-support -DSTANDALONE=1 -lresolv dns-support.c // #ifndef STANDALONE #define STANDALONE 0 #endif #if STANDALONE #include #include // dummy out functions provided by the other modules in this kit static int _chatter = 2; int get_verbosity(void) { return _chatter; } void change_verbosity(int inc) { _chatter += inc; } void log_err_message(void* dummy, const char* fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } #else // we call get_verbosity and log_err_message #include "ldaputil.h" #include "ldap-config.h" #endif typedef unsigned char byte; // Return a uniform random deviate in [0,1) // // random(3) returns a number in [0, 2^31-1]. // We convert this to a double by dividing it by 2^31 exactly. // Thus the return value is always strictly less than one. static double random_f(void) { static byte initialised_p = 0; static double denom; if (! initialised_p) { srandom(time(NULL)); denom = ldexp(1, 31); initialised_p = 1; // Explore a few properties of 'denom' // int exp; // double mantissa = frexp(denom, ); // printf("%g -> %f.2^%d; ", denom, mantissa, exp); // double two31 = (double)2147483647; // 2^31-1 as an integer // mantissa = frexp(two31, ); // printf("%g -> %f.2^%d; ", two31, mantissa, exp); // printf("ratio = %g, less than one? %s\n", //two31/denom, two31/denom < 1.0 ? "yes" : "no"); } double univariate = (double)random(); return univariate / denom; } // Uncompress a domain-name. // The returned pointer points to storage which is reused on each invocation. static const char* uncompress(ns_msg msg, const byte* p, int* skiplen_p) { static char ans[NS_MAXDNAME];
Discovering certificate locations from libldap
Greetings. Should I be able to discover the (default) locations of SSL certificates, via the libldap library? This can be useful when debugging why cert checks are failing -- where is the library checking? (am I using the library I think I am...?!) Of course dtruss and co can help here. ldap_get_option with LDAP_OPT_X_TLS_CACERTDIR (and friends) looks like it should say this, but when I explore that, it appears to show only settings added with ldap_set_option, thus only settings overriding a default. And this appears to be confirmed in libraries/libldap/tls_o.c:tlso_ctx_init. That function hands over to SSL functions, and while in principle I could retrieve a TLS session context with LDAP_OPT_X_TLS_{,SSL_}CTX, there are clear warnings that I shouldn't be tinkering with this. The fact that I'm this deep in the code suggests that either (a) this is not supported, or (b) I'm looking in the wrong place. I could in principle use functions from the OpenSSL library, like X509_get_default_cert_dir_env(), but that requires me to know which SSL library the libldap library was linked against (and that it was indeed OpenSSL), which has its own complications. Also, if I'm confident I know that, I have other ways to confirm the cert directory. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: [EXT] [OldapWS] ‑> Proposal of a REST Web Service for CRUD Operations
Ulrich, hello. On 20 Sep 2022, at 8:36, Ulrich Windl wrote: >> Thus the 'JSON query string' sent to the server via HTTP POST wouldn't >> qualify as 'REST'. > > OTOH it will help to keep URIs short and allow easy encoding of structured > data. True -- I wasn't primarily criticising the API design as such, merely noting that the term 'REST' has a fairly narrow definition. That said, as a separate point, I think the REST design paradigm is a good one, in the sense that it decomposes the problem in a way that I, for one, find useful. It does lead to longer URIs, yes, but that's surely a very minor inconvenience, if it's an inconvenience at all. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: [OldapWS] -> Proposal of a REST Web Service for CRUD Operations
Greetings. On 19 Sep 2022, at 17:54, Howard Chu wrote: > Then, I would like to propose a full Open Source first realease of a CRUD > REST Web Service to manipulate OpenLDAP's Directory Objects. This is a nice idea! However, as something of a terminology quibble, I'd say this was a 'web service', rather than a 'REST web service'. As I understand the term, I take 'REST' to refer to a web service which, at a minimum, * describes a URL-based scheme for _naming_ the things being manipulated, and * retrieves and manipulates those things via the HTTP verbs, GET, POST, PUT, DELETE, etc. Thus the 'JSON query string' sent to the server via HTTP POST wouldn't qualify as 'REST'. For a REST scheme, I can imagine something like supporting service objects named, eg https://ldapcrud.service/dc=fr/dc=oldapws/cn=Olivier%20Chator Calling GET on that obtains the attributes of the corresponding object; calling PUT on it replaces or creates it; calling DELETE deletes it, and so on. One might also have .../cn=Olivier%20Chator/cn to name a single attribute of the object (retrieved or edited with GET/POST/PUT/DELETE). Parameters such as 'nbbypage' might naturally fit in using URL path parameters. RFC 3986, Sect.3.3 'Path', gestures towards these without committing itself to a particular standard, but .../dc=fr/dc=oldapws;nbbypage=10;numpage=2 might work as a way of naming, and thus retrieving, the second 10 results which <.../dc=fr/dc=oldaps> would produce (ie, paging through the results of <.../dc=fr/dc=oldapws>). The Accept header in the HTTP query, containing a MIME type, can be used to request a result format. I'm not 100% sure that this scheme is optimal (is it unambiguous when distinguishing 'I want an entry' from 'I want attribute "x" from an entry'?), but this sort of scheme is very easy to use: no creating query strings, just retrieve a URL. The obligation to think about the naming of objects, and to separate that from the understanding of the HTTP verbs, produces in my experience a great deal of design clarity. It also gets a fair amount of specification for free, in that the semantics of the HTTP verbs are well-defined, in terms of idempotency and the like. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: Official way to import schema with cn=config
Michael, hello. On 5 Apr 2022, at 16:01, Michael Ströder wrote: > On 4/5/22 08:10, David Timber wrote: > >> I know how to import schemas with cn=config. That was never a question. I >> was just complaining because it's a tedious process and I believe that it >> shouldn't be like this. > > I also think that cn=config should not be so complicated. And I've looked > into supporting this schema in web2ldap. Can you say a little more about how slapd.d is complicated? I ask because I've never used slapd.conf, and I'm worried I'm missing something, or that there's an interestingly different perspective on how to configure openldap, which I could usefully learn about. If I want to set up a new (testing?) instance, or test a tweaked configuration, then I blow away any pre-existing slapd.d, slapadd slapd.ldif, upload a dump of the live database (which takes a few seconds with -q), start slapd, and off we go. All of the configuration is in that single slapd.ldif file. I might occasionally make live tweaks to the configuration with ldapmodify, but after testing I would freeze them in the version-controlled slapd.ldif. I can see that there's a way of working where the 'live' cn=config tree is the source of truth, and one backs that up carefully, but that doesn't seem an entirely comfortable way of working, to me. And I can see that if there were a very high volume of writes, then the few seconds of primary-server downtime here could become intricate. But if one had a setup like that, then presumably one has a multi-master configuration, so that the primaries could have their configurations updated from a single slapd.ldif in rotation. > For now I'm just happy that static slapd.conf is still supported. It's still > the most DevOps-friendly way to configure OpenLDAP. I'm not really sure what devops-friendly means here. I think my problem -- the source of my puzzlement -- is that I can't see much significant difference between slapd.conf and slapd.ldif other than details of the syntax (which to my eyes is less weird in the latter case than the former). Or: what would I be losing if support for slapd.conf disappeared tomorrow? Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: STARTTLS vs LDAPS
Quanah and all, hello. On 30 Mar 2022, at 18:54, Quanah Gibson-Mount wrote: > --On Wednesday, March 30, 2022 8:28 PM +0200 Stefan Kania > wrote: > >> That's what can be found in the FAQ on openldap.org: >> >> https://www.openldap.org/faq/data/cache/605.html >> >> I would trust this more then any rumors on any stack page ;) > > Unfortunately, the FAQ is dead weight we want to kill and not maintained in > any way, shape, or form. It's currently provided for historical purposes. Since the copyright dates at the bottom of that page are '1998-2013', so that the content of the site is now nearly a decade out of date, I feel the FAQ-o-matic now has negative utility, and that you should give in to the urge to kill it. I respect and applaud the desire to preserve the content for historical reasons, but surely that goal can be served by making a tarball of the content available at whatever page https://www.openldap.org/faq/.../* were to redirect to (ie, the pages shouldn't be 404-ed, but neither should they be 200; 301 is good). I have previously (indeed recently) looked at that page and, without thinking much about the question, taken its deprecation of LDAPS as current doctrine. And ah, FAQ-o-matic I have fond memories of FAQ-o-matics, back when wikis were new... Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: STARTTLS vs LDAPS
Thomas, hello.. On 31 Mar 2022, at 5:29, thomaswilliampritch...@gmail.com wrote: >> As to this overall discussion, one of the primary issues with connections >> over ldap:/// is that there's zero way with simple binds to prevent the >> bind dn + password being sent in the clear by a client to the server. With >> ldaps:/// the encryption is set up before the BIND occurs so you don't run >> this risk. > > Is that true? Surely I can > 1. connect to the server > 2. Send starttls > 3. Then bind bind can't I? > I'm fairly certain I've used LDAP Client operating in that order. Yes, you _can_ and should do that, and most folk do. The problem here is that, with LDAP+StartTLS, the server can't prevent a client doing it the other way around, and sending the bind credentials before StartTLS. That's bad, from the server's point of view. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: Documentation: mapping from slapd.conf to slapd database
Quanah and Howard, hello. On 16 Jul 2020, at 16:29, Quanah Gibson-Mount wrote: Documentation such as [1] notes that there is a one-to-one correspondence between slapd.conf directives and slapd-config database attributes. However it's not always completely clear just what the mapping is. See bugs: 5915, 6277, 7335, 8742 Right -- I see. Thanks. I appreciate the point in <https://bugs.openldap.org/show_bug.cgi?id=5915> that documenting both sets of directives would be quite a lot of work, which might have to be more-or-less redone when slapd.conf support is removed in 2.6/3.0 (hooray). As an interim alternative, it would probably be lowish-effort, but useful, if eg slapo-syncprov(5) and friends said something like the following, at the end of the '.SH CONFIGURATION' section: Note that the above are .B slapd.conf configuration options. These will be replaced by .B slapd.d options (see .B slapd-config(5) for details) in a forthcoming release. Until then, you can deduce the names of the .B slapd.d attributes by examining the source files in [suitable URL, such as at git.openldap.org, or simply a reference to servers/slapd/overlays in the distribution]. The same text could be dumped into all or most of the slapo-* manpages. It's incomplete, but it would be better than nothing, in that it would reassure the reader that there isn't some collection of documentation they've completely missed, and that the documentation isn't somehow out of date, by still referring to slapd.conf-style attributes. I have an alternative, possibly better, suggestion below. Patches welcome where appropriate. If it were simply a case of the above, then yes, I could do the labour of the edit and send you a patch. But I don't think I've the standing to be creative here (!). Overlay objectClasses are defined in the code for the overlay. <https://git.openldap.org/openldap/openldap/-/blob/master/servers/slapd/overlays/syncprov.c#L3160> Thanks -- at least I'm not missing something obvious! Separately, Howard Chu said: All of the schema is always retrievable from slapd itself, either by querying cn=Subschema or cn=schema,cn=config. That looks perfect in principle, and I see you made a similar remark in <https://bugs.openldap.org/show_bug.cgi?id=7335>. However the first way that occurred to me to search for this: # slapcat -b cn=config | grep cn=schema dn: cn=schema,cn=config dn: cn={0}core,cn=schema,cn=config dn: cn={1}cosine,cn=schema,cn=config dn: cn={2}nis,cn=schema,cn=config dn: cn={3}inetorgperson,cn=schema,cn=config ...didn't produce the results I expected. I find I can do % ldapsearch -x -b cn=schema,cn=config -D cn=config -w PASSWORD '(objectclass=*)' and grub through the output to find olcObjectClasses: ( OLcfgOvOc:1.1 NAME 'olcSyncProvConfig' DESC 'SyncRepl Prov ider configuration' SUP olcOverlayConfig STRUCTURAL MAY ( olcSpCheckpoint $ o lcSpSessionlog $ olcSpNoPresent $ olcSpReloadHint ) ) Is that what you meant, Howard? If so, then yes, a schema; but looking there really, really, isn't obvious from slapd-config(5). It is described there under 'schema options', and there's nothing wrong with the text, but one has to know it's there beforehand, in order to find it. I think none of your users would feel at all insulted by being given slightly more of a hint there or in, say, slapd.overlays(5). (I've spent enough time with OpenLDAP to be able to parse that attribute value by eye, and recognise it as not line-noise). To be concrete, how about, in slapd.overlays, .SH SCHEMAS The schemas for these overlays are documented in the corresponding .B slapo- (5) manpages, in the form appropriate for .B slapd.conf configuration. The schemas are loaded dynamically, and can be found either in the overlay source code, or else searching for the appropriate attributes under .B cn=schema,cn=config For example .B ldapsearch -x -b cn=schema,cn=config -s base -D cn=config -w PASSWORD '(objectclass=*)' olcobjectclasses See the .B slapd-config manpage for discussion of the olcObjectClasses attribute. I found and read the slapd.overlays manpage early in my search for this information. Something like the above would have led me to the information I needed very quickly. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Documentation: mapping from slapd.conf to slapd database
Greetings. Documentation such as [1] notes that there is a one-to-one correspondence between slapd.conf directives and slapd-config database attributes. However it's not always completely clear just what the mapping is. For example, both [2] and slapo-syncprov(5) describe syncprov-checkpoint. This corresponds to the attribute olcSpCheckpoint, but I can't find where it is I learned that mapping (I probably stumbled across it via [3]), so can't point to chapter and verse in my own documentation and notes. I would have thought the documentation for the olcSyncProvConfig objectClass would explain this, but I can't find any such documentation, nor even a schema for that objectClass [4]. The problem (and my puzzlement) is not unique to the syncprov overlay. Best wishes, Norman [1] https://www.openldap.org/doc/admin24/slapdconf2.html [2] https://openldap.org/doc/admin24/overlays.html [3] https://mishikal.wordpress.com/2019/04/23/configuring-mmr-using-delta-syncrepl-in-openldap-updating-an-existing-standalone-configuration/ [4] Ie, grep -i syncprov /usr/local/etc/openldap/schema/* (and similar) produces nothing. -- Norman Gray : https://nxg.me.uk
Re: slapo-unique spins its wheels on a non-trivial olcUniqueURI spec
Greetings. On 11 Sep 2019, at 11:09, Norman Gray wrote: So there is at least a documentation gap here. Of course slapd should not run crazy because of this. Is there enough information in my previous message for me to add a reasonable ITS report, do you think? I've added ITS#9486 and ITS#9488, referring to the wheel-spinning and the documentation, respectively. I hope these are useful. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: slapo-unique spins its wheels on a non-trivial olcUniqueURI spec
Michael, hello. Thanks for your response. On 10 Sep 2019, at 19:33, Michael Ströder wrote: The above is invalid. Your LDIF should contain separate attribute values for each unique URI: olcUniqueURI: ldap:///ou=dept-A,o=example?uidnumber?sub olcUniqueURI: ldap:///ou=dept-B,o=example?uidnumber?sub The problem is that both the manpage and the source-code comments seem to state that the attribute can take multiple values. Quoting from the manpage: Multiple URIs may be specified within a domain, allowing complex selections of objects. Multiple unique_uri statements or olcUniqueURI attributes will create independent domains I interpret that as saying that each olcUniqueURI attribute corresponds to, or implies, an 'independent domain', and that 'Multiple URIs may be specified within a domain' indicates that a domain can be specified by multiple ldap:/// URIs (though it doesn't say, for example, whether these are composed using UNION or something else). That is, if this text _isn't_ intended to say that there may be multiple olcUniqueURI attributes, each of which can have multiple URIs, then it should be rewritten. I would interpret your rewritten version as saying that uidnumber attributes should be unique in ou=dept-A, and that they should be unique in ou=dept-B (ie, they are independent), but not that they should be unique in (ou=dept-A UNION ou=dept-B), which is what I want. So there is at least a documentation gap here. Of course slapd should not run crazy because of this. Is there enough information in my previous message for me to add a reasonable ITS report, do you think? You can look at a running example config (cn=config read-only): Thanks -- this is very useful (and also nudges investigating Ædir further up my list). I'll study those. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
slapo-unique spins its wheels on a non-trivial olcUniqueURI spec
Greetings. I'm having difficulty creating a non-trivial olcUniqueURI spec. Can anyone advise me? The problems are: * A plausible-looking spec in olcUniqueURI causes slapadd to spin its wheels indefinitely. * The manpage doesn't make it terribly clear what I should expect from a plausible-looking spec. Details follow: I want to specify something like dn: olcOverlay=unique,olcDatabase={1}mdb,cn=config objectClass: olcOverlayConfig objectClass: olcUniqueConfig olcOverlay: unique olcUniqueURI: ldap:///ou=dept-A,o=example?uidnumber?sub ldap:///ou=dept-B,o=example?uidnumber?sub The idea is that the uidnumber attribute should be unique across the two OUs, ou=dept-A and ou=dept-B. If I do this, however, then slapadd spins its wheels: $ rm -R /usr/local/etc/openldap/slapd.d/* $ su -m ldap -c "slapadd -d255 -n 0 -F /usr/local/etc/openldap/slapd.d -l /usr/local/etc/openldap/slapd.ldif" ... ... 5d77d377 >>> dnPrettyNormal: 5d77d377 <<< dnPrettyNormal: , 5d77d377 <= str2entry(olcOverlay=unique,olcDatabase={1}mdb,cn=config) -> 0x800d4eca8 5d77d377 oc_check_required entry (olcOverlay=unique,olcDatabase={1}mdb,cn=config), objectClass "olcUniqueConfig" 5d77d377 oc_check_allowed type "objectClass" 5d77d377 oc_check_allowed type "olcOverlay" 5d77d377 oc_check_allowed type "olcUniqueURI" 5d77d377 oc_check_allowed type "structuralObjectClass" 5d77d377 >>> dnNormalize: 5d77d377 <<< dnNormalize: 5d77d377 ==> unique_db_init 5d77d377 ==> unique_new_domain <ldap:///ou=dept-A,o=example?uidnumber?sub ldap:///ou=dept-B,o=example?uidnumber?sub> 5d77d377 >>> dnPrettyNormal: 5d77d377 <<< dnPrettyNormal: , 5d77d377 >>> dnPrettyNormal: 5d77d377 <<< dnPrettyNormal: , 5d77d377 >>> dnPrettyNormal: 5d77d377 <<< dnPrettyNormal: , 5d77d377 >>> dnPrettyNormal: 5d77d377 <<< dnPrettyNormal: , 5d77d377 >>> dnPrettyNormal: 5d77d377 <<< dnPrettyNormal: , 5d77d377 >>> dnPrettyNormal: 5d77d377 <<< dnPrettyNormal: , ... ^C and so on and on and on, apparently indefinitely. slapo-unique(5) says that the syntax here is: unique_uri <[strict ][ignore ]URI[URI...]...> Configure the base, attributes, scope, and filter for uniqueness checking. Multiple URIs may be specified within a domain, allowing complex selections of objects. Multiple unique_uri statements or olcUniqueURI attributes will create independent domains, each with their own independent lists of URIs and ignore/strict settings. Keywords strict and ignore have to be enclosed in quotes (") together with the URI. The LDAP URI syntax is a subset of RFC-4516, and takes the form: ldap:///[base dn]?[attributes...]?scope[?filter] (I'm taking it that there should be a space between those `URI[URI...]`) in servers/slapd/overlays/unique.c, the comment above `unique_new_domain` says instead * domain_specs look like * * [strict ][ignore ]uri[[ uri]...] * e.g. "ldap:///ou=foo,o=bar?uid?sub ldap:///ou=baz,o=bar?uid?sub; * "strict ldap:///ou=accounts,o=bar?uid,uidNumber?one; * etc * So that's clearly permitting multiple URIs -- perhaps the quotes are required (as the manpage hints). But if I try olcUniqueURI: "ldap:///ou=dept-A,o=example?uidnumber?sub ldap:///ou=dept-B,o=example?uidnumber?sub; ...then I get 5d77d617 ==> unique_new_domain <"ldap:///ou=dept-A,o=example?uidnumber?sub ldap:///ou=dept-B,o=example?uidnumber?sub;> 5d77d617 olcUniqueURI: value #0: <"ldap:///ou=dept-A,o=example?uidnumber?sub ldap:///ou=dept-B,o=example?uidnumber?sub;> invalid ldap urilist from slapadd (so that's not the problem). But the example there does look VERY much like what I tried. So: * I'm pretty sure I shouldn't be able to make slapadd spin its wheels like that. * The manpage text might be a little too telegraphic. While I'm sure it's not _wrong_, it is quite hard to go from that text to a working spec with any confidence. Googling olcUniqueURI produces very little of use. Is this not the correct way to do this? This looks somewhat similar, in symptoms and module, to ITS#8162, but it does seem distinct. Thanks for any pointers. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: slapo-memberof(5) confusing documentation
Michael, hello. On 9 Sep 2019, at 16:16, Michael Ströder wrote: On 9/9/19 4:06 PM, Norman Gray wrote: However, immediately after that, the text says: Note that slapo-memberOf is not compatible with syncrepl based replication, and should not be used in a replicated environment. An alternative is to use slapo-dynlist to emulate slapo-memberOf behavior. This seems to flatly contradict (my understanding of) the first part of the paragraph. The problem is that in syncrepl refresh phase entries can be replicated in any order. So if a group entry comes in before the member entries are present you will see some warnings in the log and the entries may not be consistent. See ITS#8613 for details: Indeed: I quoted ITS#8613 which says 'slapo-memberOf overlay is not safe to use in a replicated environment', because of the ordering issue that you've mentioned here. It seems that one can use the memberof overlay if-and-only-if one has separately done something to deal with the ordering (which would be tricky) or one has some means of completely avoiding 'refresh' mode in the sync (which would be tricky). In practice, that looks very much like a 'no', and the contradiction in the manpage still seems to be present. For what it's worth, the manpage text looks as if the 'Note that...' sentence were added at some point (possibly in response to ITS#8613?) without amending the text immediately before it, so that the result is a little garbled. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
slapo-memberof(5) confusing documentation
Greetings. The slapo-memberof(5) manpage mentions limitations on when it can be used, in the context of replication. The current text is very confusing, and possibly not self-consistent. This message might be more appropriate as an ITS PR, but I'm sending it here first, partly in case I've got the wrong end of the stick, and partly as google-bait for the benefit of anyone as puzzled as I was. The text currently says: The memberof overlay may be used with any backend that provides full read-write functionality, but it is mainly intended for use with local storage backends. The maintenance operations it performs are internal to the server on which the overlay is configured and are never replicated. Replica servers should be configured with their own instances of the memberOf overlay if it is desired to maintain these memberOf attributes on the replicas. Fine -- that seems to me to say very clearly that the memberof attribute is OK to use across replicas, as long as each replica has its own memberof overlay. However, immediately after that, the text says: Note that slapo-memberOf is not compatible with syncrepl based replication, and should not be used in a replicated environment. An alternative is to use slapo-dynlist to emulate slapo-memberOf behavior. This seems to flatly contradict (my understanding of) the first part of the paragraph. So which is it? A 2009 list thread [1] seems to suggest that you can sort of get away with it, but only just. However another list thread (2015) [2] describes an apparently successful setup using simple syncrepl, with memberof in the provider and consumer, and where the memberof attribute is automatically excluded from the replication (there’s explicit advice not to exclude it using exattrs=memberof in the syncrepl setup). The latter posting refers to ITS issue 7400 [3], ‘Memberof and Syncrepl incompatibility’, which last had activity in 2012, but which still appears to be open. The latter issue notes in passing that ‘memberof must be configured on each replica’, which implies that this is a feasible setup. **However**: closed ITS 8613 (2017) [4] says baldly that The slapo-memberOf overlay is not safe to use in a replicated environment due to the way in which replication occurs. It also explains why, and gives a (compressed but complete) suggestion of how one might use the dynlist overlay to do something similar. A 2018 list message [5] says in passing Unfortunately, there is no defined standard for the “memberOf” functionality (it’s a MS hack) and so there’s nothing that details how it should or shouldn’t behave with replication. Going with the most recent statement, therefore, it seems that this it's simply not possible to use memberOf in a replicated environment, unless (rather precariously, I'd have thought) you completely avoid 'refresh' mode in replication, as mentioned in [4]. I suggest adjusting the text in the slapo-memberof manpage. If the third sentence (‘Replica servers should...’) were simply removed, that would remove most of the contradiction. In the following sentence (‘Note that...’), the sentence first seems to suggest that it's only syncrepl replication that the overlay is incompatible with (ie, that there are other replication mechanisms which will work), but it goes on to state that it's incompatible with _all_ replication methods. Which is it? Finally, given the aside in [5], it might be worth indicating in the manpage that memberOf should only be used when other considerations force it, and should (by the sound of it) be avoided otherwise. Have I misunderstood something? Best wishes, Norman [1] https://www.openldap.org/lists/openldap-software/200910/msg00037.html [2] https://www.openldap.org/lists/openldap-technical/201505/msg00127.html [3] https://www.openldap.org/its/index.cgi/Software%20Bugs?id=7400;selectid=7400 [4] http://www.openldap.org/its/index.cgi/?findid=8613 [5] https://www.openldap.org/lists/openldap-technical/201809/msg00099.html -- Norman Gray : https://nxg.me.uk
Re: SHA-2 and other hashes
Howard, hello. On 3 Jun 2019, at 15:07, Howard Chu wrote: SHA-2 in any form is unsuitable for use as a password hash, simply because it is too easy to compute. At this point the best choice is the one that won the Password Hashing Competition - Argon2. https://github.com/P-H-C/phc-winner-argon2 That makes sense -- thanks. Patches for adding this to OpenLDAP would of course be welcome. I'm sure. However I fear I'm not going to be able to oblige in the short term Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: SHA-2 and other hashes
Ulrich, hello. On 3 Jun 2019, at 13:50, Ulrich Windl wrote: ie, pretty much what I expected ‑‑ but in glibc's crypt(3), the $5$ and $6$ hashes are the result of an unspecified number of rounds of such hashing (the $1$/MD5 glibc hash does appear to be compatible with OpenLDAP {SMD5}, though). (Quite possibly everyone else in the world already knew this, but I didn't!) Hi! First the number of rounds is NOT unspecified: It s explicitly specified, it's optional, and (I think) it defaults to one. Good point -- the number of rounds is indeed exposed. If I'm correctly reading crypt/sha256-crypt.c in <https://ftp.gnu.org/gnu/glibc/>, then the default number of rounds is 5000 and, as you say, the number of rounds can be indicated in a param=value clause in the passwd string (as gestured towards in <https://github.com/P-H-C/phc-string-format/blob/master/phc-sf-spec.md>). But I may have been unclear: by 'unspecified' I meant 'not described in a formal specification' (as far as I can see), so that I would not be comfortable trying to reimplement the glibc password-hashing process based on documentation alone. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
SHA-2 and other hashes
Greetings. I've been doing some reading on password hashes, in OpenLDAP and elsewhere. It's slightly harder to find explicit details about this, than I expected, and some of what I learned was unexpected to me, though probably familiar to many on this list. In the expectation I'm not the only naive one, I'd like to log these on-list, with a broad query about whether I'm understanding things correctly. There are very good discussions at [1] and [2]. SHA-256/SHA-512 on OpenLDAP doesn't mean the same as what is nominally the same hash function in glibc. In OpenLDAP, the MD5 and SHA-whatever hash of a password is the result of hashing the password plus salt -- ie, pretty much what I expected -- but in glibc's crypt(3), the $5$ and $6$ hashes are the result of an unspecified number of rounds of such hashing (the $1$/MD5 glibc hash does appear to be compatible with OpenLDAP {SMD5}, though). (Quite possibly everyone else in the world already knew this, but I didn't!) While the glibc $5$ and $6$ hashes are therefore, in principle, somewhat resistant to brute-forcing, the OpenLDAP ones are not, and so should be managed as confidential data. MD5 _isn't_ significantly worse than plain SHA-whatever for password hashing, since its discovered defects with respect to collisions are not a problem for this application (it's still resistant to 'preimages'). However both are unsafe for secure password storage, simply because they're too fast (by design), and MD5 is unusable simply because 'everyone knows MD5 is broken', so it would require tedious explanation. Thus it would seem that the SHA-2 support optionally available in the OpenLDAP distribution (in contrib/slapd-modules/passwd/sha2) doesn't actually benefit me much, over and above the MD5 and SHA-1 support available by default. However PBKDF2 (available in contrib/slapd-modules/passwd/pbkdf2) would be usable immediately, though this appears to be not the best such function available. So the two options for password storage seem to be * use {SSHA} and -- as [3] stresses -- protect password attributes as if they were clear text; or * one way or another use a hash-stretching algorithm such as bcrypt (though [4] -- *sigh*) or scrypt [5] (maybe the best option?) or PBKDF2 * (and don't worry about having to protect password attributes?). Is that about right? Is there a consensus OpenLDAP best practice here? Best wishes, Norman [1] https://crackstation.net/hashing-security.htm [2] https://security.stackexchange.com/questions/211/how-to-securely-hash-passwords/31846#31846 [3] https://openldap.org/doc/admin24/security.html [4] https://github.com/wclarie/openldap-bcrypt/issues/1 [5] https://github.com/Tarsnap/scrypt -- Norman Gray : https://nxg.me.uk
Re: OpenLDAP instances crashes
Saurabh, hello. On 22 Aug 2018, at 19:25, Saurabh Lahoti wrote: This is a custom overlay build in 2006 by our ex team mate & he did the installation on older versions. This August we did a migration from old version to 2.4.46 & lmdb backend. Is it that we skipped some steps in this movement..? Ah, that's _very_ relevant information. Without knowing anything more about your setup, that custom overlay would be the very first thing you should look at. There's very little a mailing list can help with, concerning a custom overlay. Probably the best thing you can do in the immediate term is to revert to a backup of your previous system, presuming such a thing exists, and that the previous system is (at least) basically working. Then you need to find someone who's able to look at the custom overlay code. It might be worth trying to work out if you can do without the overlay. I notice that there's an ongoing list thread 'help to get our openldap updated and replicated'. That thread might be worth monitoring, on general principles. Best wishes (and good luck), Norman -- Norman Gray : https://nxg.me.uk SUPA School of Physics and Astronomy, University of Glasgow, UK
Re: ldapi and StartTLS
Michael and Richard, hello. On 16 Jul 2018, at 5:09, Richard Gray wrote: Have a look at 'olcLocalSSF' in slapd-config(5), which lets you set the security strength factor for local (i.e. ldapi://) sessions. It defaults to 71, which is likely why you're seeing that error message. Personally, I bump it up to 256, to match the ssf=256 I have set in the olcSecurity attribute on cn=config. Many thanks for this advice -- it works perfectly. I've set olcLocalSSF to 256. Hmm: 71 is an oddly-chosen default. Is there a story there, I wonder? (Apologies, also, for taking so long to respond: this project had swapped right out of my head, and it was only a couple of days ago that it was able to page back in). Best wishes, Norman -- Norman Gray : https://nxg.me.uk
ldapi and StartTLS
Greetings. I would have thought (possibly naively) that StartTLS was unnecessary when connecting to slapd through a unix socket -- the client and the server are on the same machine, and so don't need to be reassured about each other's identity. However this seems not to be be the case: % ldapsearch -LLL -H ldapi://%2Fvar%2Frun%2Fopenldap%2Fldapi '(uid=foo)' ldap_sasl_interactive_bind_s: Confidentiality required (13) additional info: stronger confidentiality required (same result with ldapi:///). What am I misunderstanding? In the slapd.ldif I have: dn: cn=config objectClass: olcGlobal cn: config olcArgsFile: /var/run/openldap/slapd.args olcPidFile: /var/run/openldap/slapd.pid olcSecurity: ssf=128 olcTLSCertificateFile: /usr/local/etc/openldap/certs/XXX.crt olcTLSCertificateKeyFile: /usr/local/etc/openldap/certs/XXX.key olcTLSCACertificateFile: /usr/local/etc/openldap/certs/FOO olcLogLevel: 0 The machine is also listening on ldap://0.0.0.0 and requiring TLS. I don't see anything in the documentation which seems to suggest I can have different TLS rules on different interfaces or protocols (ie, ldap: vs ldapi:) -- am I just missing that? The /usr/local/etc/ldap.conf doesn't mention TLS, so the TLS requirement isn't coming in from there. My practical problem is that I'm trying to get nslcd (on the same machine) to talk to OpenLDAP locally. If there's a certificate problem I can sort that out, but I can't help feeling that that ought to be unnecessary -- that I'm missing something simple. This is 2.4.45 on FreeBSD. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: Switching from bdb to lmdb with Openldap-2.4.46
Saurabh, hello. On 4 Jun 2018, at 19:56, Frank Swasey wrote: you would shutdown your ldap server. slapcat the bdb database out change your slapd.conf file to use MDB instead of BDB slapadd your database into mdb As a parenthesis to Frank's advice, I'll mention that this is good advice not just for the sort of significant configuration change you're talking about. The way I manage all such configuration changes, major and minor, is by keeping the server's config in a version-controlled slapd.ldif, and making almost no configuration changes to the running system. Thus the way I make config changes is: * stop slapd (leaving the mirrors running) * slapcat >dump.ldif (all the non-configuration data) * nuke-and-reinitialise.sh (deletes slapd.d; copies into place slapd.ldif, certificates, and some schema mods; then calls slapadd to load slapd.ldif into slapd.d) * slapadd dump.ldif * start slapd That way, I know exactly what configuration is running _and_ I can test _exactly_ the modified configuration in a VM, beforehand. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: Error Loading Schema
Greetings. On 16 Apr 2018, at 18:58, Net Warrior wrote: Im trying to load this schema http://pig.made-it.com/ldap-openssh.html And I get this error. ldapmodify: invalid format (line 1) entry: "" ldapmodify will upload files in LDIF format (which you might be able to see documented using 'man ldif'), but the file at <http://pig.made-it.com/ldap-openssh/openssh-ldap.schema> is a schema file, which has a slightly different syntax. That means: 1. that you'll have to convert the schema file to a corresponding .ldif file; and 2. that it has to be installed in the openldap directory on your LDAP server (you'll see other .schema and .ldif files in there), and included into your slapd configuration file (something like .../openldap/slapd.ldif) with a line like include: file://blah/blah/blah/openldap/schema/openssh-ldap.ldif Here is a variant of that file that I've used, in .ldif format: # # LDAP Public Key Patch schema for use with openssh-ldappubkey # Author: Eric AUGE <e...@phear.org> # # Based on the proposal of : Mark Ruijter # # See https://github.com/jirutka/ssh-getkey-ldap # Converted by hand from openssh-lpk.schema to .ldif format, # following the advice in openldap.ldif. # # # dn: cn=openssh-lpk,cn=schema,cn=config objectClass: olcSchemaConfig cn: openssh-lpk # # octetString SYNTAX olcAttributeTypes: ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' DESC 'OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) # # printableString SYNTAX yes|no olcObjectClasses: ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY DESC 'OpenSSH LPK objectclass' MUST uid MAY sshPublicKey ) Best wishes, Norman -- Norman Gray : https://nxg.me.uk
Re: OpenLDAP: ACLs using sockname and DN?
Qanah, hello. On 22 Mar 2018, at 14:04, Quanah Gibson-Mount wrote: Hi Norman, sockname.exact="/var/run/openldap/ldapi" write ITS#3050 has an example of using both sockname and sockurl in an ACL. I'll see about having an example added to the admin guide. <http://www.openldap.org/its/index.cgi/?findid=3050> Ah, many thanks. I've just confirmed that by dn.base="uid=pwreset,ou=service,dc=example,dc=edu" sockname.exact="PATH=/var/run/openldap/ldapi" =dxw ...does indeed work: the uid=pwreset DN does have write access with -H ldapi:///, but doesn't have that access when connecting over the network. Looking again at the relevant paragraph in the slapd.access(5) manpage, I read it as indicating that the `PATH=` syntax applies only to `peername`, and that `sockname` should have 'the named pipe file name'. It might be worth checking that the manpage does still completely reflect the code. If that manpage is being revisited, then it would be useful to be explicit that the various constraints in a stanza are ANDed together. This might naturally go near the text 'They may be specified in combination'. And an example in the admin guide would indeed be most welcome. Best wishes, Norman -- Norman Gray : https://nxg.me.uk
OpenLDAP: ACLs using sockname and DN?
Greetings. In OpenLDAP (2.4.45, on FreeBSD), I'm trying to constrain the access of a DN to an attribute, by giving a DN access only when the connection is made via a socket; but without success. I may just be looking for an example of correct use. What I'm trying is olcAccess: to attrs=userPassword by dn.base="uid=pwreset,ou=service,dc=example,dc=edu" sockname.exact="/var/run/openldap/ldapi" write (the idea is that the pwreset DN can be used by an automatic password-reset script, but that that DN will have that access only when the script is running on the same machine as the LDAP server). This `by` phrase appears to match the production in Sect. 8.3 of the OpenLDAP access control documentation, and the remark in slapd.access(5) that the items in the field ‘may be specified in combination’. And indeed there are no syntax warnings generated. I'm presuming that the combination implies an AND rather than an OR – this isn't made explicit in the documentation. I can find no examples covering this in either OpenLDAP documentation or on the web. This stanza works when the sockname element is absent, suggesting that the configuration is otherwise working as I expect. When I try to write the userPassword attribute using this DN, I get an ldap_modify: Insufficient access (50) error. The OpenLDAP documentation doesn't (somewhat surprisingly) explicitly state what the effect of this sockname element is, and the slapd.access(5) page says, rather obliquely, that: The statements peername=, sockname=, domain=, and sockurl= mean that the contacting host IP (in the form IP=: for IPv4, or IP=[]: for IPv6) or the contacting host named pipe file name (in the form PATH= if connecting through a named pipe) for peername, the named pipe file name for sockname, the contacting host name for domain, and the contacting URL for sockurl are compared against pattern to determine access. Saying 'determine access' doesn't actually say very much. Have I completely misunderstood the point of this access specification, or is there another way to do this? Best wishes, Norman -- Norman Gray : https://nxg.me.uk