ypldap currently can't do SRV lookups to locate the directory servers for
a domain, which makes it slightly harder than it should be to connect it to
an AD domain.  The diff below lets you specify a directory service like this:

  directory "eait.uq.edu.au" srv {
    ...
  }

which will make ypldap do an SRV lookup every time it refreshes, then try
each of the resulting hostnames in order (according to priority and weighting)
until one works.


Index: ldapclient.c
===================================================================
RCS file: /cvs/src/usr.sbin/ypldap/ldapclient.c,v
retrieving revision 1.36
diff -u -p -u -p -r1.36 ldapclient.c
--- ldapclient.c        10 Apr 2016 09:59:21 -0000      1.36
+++ ldapclient.c        1 May 2016 23:47:33 -0000
@@ -54,7 +54,9 @@ int   client_search_idm(struct env *, stru
            char **, char *, int, int, enum imsg_type);
 int    client_try_idm(struct env *, struct idm *);
 int    client_addr_init(struct idm *);
+int    client_srv_next(struct idm *);
 int    client_addr_free(struct idm *);
+int    client_srv_free(struct idm *);
 
 struct aldap   *client_aldap_open(struct ypldap_addr_list *);
 
@@ -122,6 +124,37 @@ client_addr_init(struct idm *idm)
 }
 
 int
+client_srv_next(struct idm *idm)
+{
+       struct ypldap_srv       *s, *sel;
+       int                      bestprio, weight;
+
+       bestprio = 0xffff;
+       weight = 0;
+       sel = NULL;
+       TAILQ_FOREACH(s, &idm->idm_srv, next) {
+               if (s->priority < bestprio) {
+                       bestprio = s->priority;
+                       weight = s->weight;
+                       sel = s;
+               } else if (s->priority == bestprio) {
+                       weight += s->weight;
+                       if (arc4random_uniform(weight) < s->weight)
+                               sel = s;
+               }
+       }
+
+       if (sel == NULL)
+               fatalx("couldn't pick a srv record to try next?");
+
+       client_addr_free(idm);
+
+       TAILQ_REMOVE(&idm->idm_srv, sel, next);
+       strlcpy(idm->idm_srv_name, sel->hostname, sizeof(idm->idm_srv_name));
+       return (0);
+}
+
+int
 client_addr_free(struct idm *idm)
 {
         struct ypldap_addr     *h;
@@ -131,7 +164,19 @@ client_addr_free(struct idm *idm)
                TAILQ_REMOVE(&idm->idm_addr, h, next);
                free(h);
        }
+       return (0);
+}
+
+int
+client_srv_free(struct idm *idm)
+{
+       struct ypldap_srv       *s;
 
+       while (!TAILQ_EMPTY(&idm->idm_srv)) {
+               s = TAILQ_FIRST(&idm->idm_srv);
+               TAILQ_REMOVE(&idm->idm_srv, s, next);
+               free(s);
+       }
        return (0);
 }
 
@@ -155,6 +200,7 @@ client_dispatch_dns(int fd, short events
        u_int16_t                dlen;
        u_char                  *data;
        struct ypldap_addr      *h;
+       struct ypldap_srv       *s;
        int                      n, wait_cnt = 0;
        struct idm              *idm;
        int                      shut = 0;
@@ -186,26 +232,29 @@ client_dispatch_dns(int fd, short events
                if (n == 0)
                        break;
 
-               switch (imsg.hdr.type) {
-               case IMSG_HOST_DNS:
-                       TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
-                               if (idm->idm_id == imsg.hdr.peerid)
-                                       break;
-                       if (idm == NULL) {
-                               log_warnx("IMSG_HOST_DNS with invalid peerID");
-                               break;
-                       }
-                       if (!TAILQ_EMPTY(&idm->idm_addr)) {
-                               log_warnx("IMSG_HOST_DNS but addrs set!");
+               TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
+                       if (idm->idm_id == imsg.hdr.peerid)
                                break;
-                       }
+               if (idm == NULL) {
+                       log_warnx("dns message with invalid peerID");
+                       imsg_free(&imsg);
+                       continue;
+               }
 
-                       dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
-                       if (dlen == 0) {        /* no data -> temp error */
-                               idm->idm_state = STATE_DNS_TEMPFAIL;
-                               break;
-                       }
+               if (!TAILQ_EMPTY(&idm->idm_addr)) {
+                       log_warnx("got dns message, but we already have addrs");
+                       break;
+               }
+
+               dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
+               if (dlen == 0) {        /* no data -> temp error */
+                       idm->idm_state = STATE_DNS_TEMPFAIL;
+                       imsg_free(&imsg);
+                       continue;
+               }
 
+               switch (imsg.hdr.type) {
+               case IMSG_HOST_DNS:
                        data = (u_char *)imsg.data;
                        while (dlen >= sizeof(struct sockaddr_storage)) {
                                if ((h = calloc(1, sizeof(*h))) == NULL)
@@ -220,8 +269,25 @@ client_dispatch_dns(int fd, short events
                                fatalx("IMSG_HOST_DNS: dlen != 0");
 
                        client_addr_init(idm);
+                       break;
 
+               case IMSG_SRV_DNS:
+                       data = (u_char *)imsg.data;
+                       while (dlen >= sizeof(struct ypldap_srv)) {
+                               if ((s = calloc(1, sizeof(*s))) == NULL)
+                                       fatal(NULL);
+                               memcpy(s, data, sizeof(*s));
+                               TAILQ_INSERT_HEAD(&idm->idm_srv, s, next);
+
+                               data += sizeof(*s);
+                               dlen -= sizeof(*s);
+                       }
+                       if (dlen != 0)
+                               fatalx("IMSG_SRV_DNS: dlen != 0");
+
+                       idm->idm_state = STATE_DNS_SRV;
                        break;
+
                default:
                        break;
                }
@@ -229,11 +295,21 @@ client_dispatch_dns(int fd, short events
        }
 
        TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
-               if (client_try_idm(env, idm) == -1)
-                       idm->idm_state = STATE_LDAP_FAIL;
+               if (idm->idm_state == STATE_DNS_DONE) {
+                       if (client_try_idm(env, idm) != 0)
+                               idm->idm_state = STATE_LDAP_FAIL;
+               }
 
-               if (idm->idm_state < STATE_LDAP_DONE)
+               if (idm->idm_state < STATE_LDAP_DONE) {
                        wait_cnt++;
+                       if (!TAILQ_EMPTY(&idm->idm_srv)) {
+                               client_srv_next(idm);
+                               imsg_compose_event(env->sc_iev_dns,
+                                   IMSG_HOST_DNS, idm->idm_id, 0, -1,
+                                   idm->idm_srv_name,
+                                   strlen(idm->idm_srv_name) + 1);
+                       }
+               }
        }
        if (wait_cnt == 0)
                imsg_compose_event(env->sc_iev, IMSG_END_UPDATE, 0, 0, -1,
@@ -661,6 +737,7 @@ client_periodic_update(int fd, short eve
                idm->idm_state = STATE_NONE;
 
                client_addr_free(idm);
+               client_srv_free(idm);
        }
        if (fail_cnt > 0) {
                log_debug("trash the update");
@@ -677,6 +754,7 @@ client_configure(struct env *env)
        struct timeval   tv;
        struct idm      *idm;
         u_int16_t        dlen;
+       int              msg;
 
        log_debug("connecting to directories");
 
@@ -685,7 +763,14 @@ client_configure(struct env *env)
        /* Start the DNS lookups */
        TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
                dlen = strlen(idm->idm_name) + 1;
-               imsg_compose_event(env->sc_iev_dns, IMSG_HOST_DNS, idm->idm_id,
+               if (idm->idm_flags & F_SRV_LOOKUP) {
+                       msg = IMSG_SRV_DNS;
+               } else {
+                       msg = IMSG_HOST_DNS;
+                       strlcpy(idm->idm_srv_name, idm->idm_name,
+                           sizeof(idm->idm_srv_name));
+               }
+               imsg_compose_event(env->sc_iev_dns, msg, idm->idm_id,
                    0, -1, idm->idm_name, dlen);
        }
 
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/ypldap/parse.y,v
retrieving revision 1.18
diff -u -p -u -p -r1.18 parse.y
--- parse.y     16 Jan 2015 06:40:22 -0000      1.18
+++ parse.y     1 May 2016 23:47:34 -0000
@@ -101,11 +101,12 @@ typedef struct {
 %token SERVER FILTER ATTRIBUTE BASEDN BINDDN GROUPDN BINDCRED MAPS CHANGE 
DOMAIN PROVIDE
 %token USER GROUP TO EXPIRE HOME SHELL GECOS UID GID INTERVAL
 %token PASSWD NAME FIXED LIST GROUPNAME GROUPPASSWD GROUPGID MAP
-%token INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS
+%token INCLUDE DIRECTORY CLASS PORT ERROR GROUPMEMBERS SRV
 %token <v.string>      STRING
 %token  <v.number>     NUMBER
 %type  <v.number>      opcode attribute
 %type  <v.string>      port
+%type  <v.number>      srv
 
 %%
 
@@ -259,8 +260,11 @@ diropt             : BINDDN STRING                         
{
                        free($5);
                }
                ;
+srv            : /* empty */                           { $$ = 0; }
+               | SRV                                   { $$ = F_SRV_LOOKUP; }
+               ;
 
-directory      : DIRECTORY STRING port {
+directory      : DIRECTORY STRING port srv {
                        if ((idm = calloc(1, sizeof(*idm))) == NULL)
                                fatal(NULL);
                        idm->idm_id = conf->sc_maxid++;
@@ -274,6 +278,10 @@ directory  : DIRECTORY STRING port {
                        }
 
                        free($2);
+
+                       if ($4 != 0)
+                               idm->idm_flags |= F_SRV_LOOKUP;
+
                } '{' optnl diropts '}'                 {
                        TAILQ_INSERT_TAIL(&conf->sc_idms, idm, idm_entry);
                        idm = NULL;
@@ -387,6 +395,7 @@ lookup(char *s)
                { "provide",            PROVIDE },
                { "server",             SERVER },
                { "shell",              SHELL },
+               { "srv",                SRV },
                { "to",                 TO },
                { "uid",                UID },
                { "user",               USER },
Index: ypldap.conf.5
===================================================================
RCS file: /cvs/src/usr.sbin/ypldap/ypldap.conf.5,v
retrieving revision 1.19
diff -u -p -u -p -r1.19 ypldap.conf.5
--- ypldap.conf.5       30 Apr 2012 11:28:25 -0000      1.19
+++ ypldap.conf.5       1 May 2016 23:47:34 -0000
@@ -83,12 +83,19 @@ convert LDAP entries to
 and
 .Xr group 5
 lines.
-A directory declaration is of the following form:
+.Bl -tag -width Ds
+.It Ic directory Ar server Oo Ic srv Oc Ic
+Specify a directory server to connect to.
 .Bd -literal -offset indent
-directory "some.host" {
+directory "some.host" srv {
        # directives
 }
+.El
 .Ed
+.Pp
+If 'srv' is specified, the daemon will use DNS SRV records to identify the
+directory servers for the specified domain, rather than performing a normal
+hostname lookup.
 .Pp
 Valid directives for directories are:
 .Bl -tag -width Ds
Index: ypldap.h
===================================================================
RCS file: /cvs/src/usr.sbin/ypldap/ypldap.h,v
retrieving revision 1.17
diff -u -p -u -p -r1.17 ypldap.h
--- ypldap.h    10 Apr 2016 09:59:21 -0000      1.17
+++ ypldap.h    1 May 2016 23:47:34 -0000
@@ -17,6 +17,7 @@
  */
 
 #include <imsg.h>
+#include <netdb.h>
 
 #define YPLDAP_USER            "_ypldap"
 #define YPLDAP_CONF_FILE       "/etc/ypldap.conf"
@@ -37,7 +38,8 @@ enum imsg_type {
        IMSG_TRASH_UPDATE,
        IMSG_PW_ENTRY,
        IMSG_GRP_ENTRY,
-       IMSG_HOST_DNS
+       IMSG_HOST_DNS,
+       IMSG_SRV_DNS
 };
 
 struct ypldap_addr {
@@ -46,6 +48,15 @@ struct ypldap_addr {
 };
 TAILQ_HEAD(ypldap_addr_list, ypldap_addr);
 
+struct ypldap_srv {
+       TAILQ_ENTRY(ypldap_srv)          next;
+       char                             hostname[NI_MAXHOST];
+       in_port_t                        port;
+       uint16_t                         priority;
+       uint16_t                         weight;
+};
+TAILQ_HEAD(ypldap_srv_list, ypldap_srv);
+
 enum {
        PROC_MAIN,
        PROC_CLIENT
@@ -69,7 +80,7 @@ struct groupent {
 
 enum client_state {
         STATE_NONE,
-        STATE_DNS_INPROGRESS,
+        STATE_DNS_SRV,
         STATE_DNS_TEMPFAIL,
         STATE_DNS_DONE,
        STATE_LDAP_FAIL,
@@ -86,12 +97,15 @@ struct idm {
 #define F_SSL                           0x00100000
 #define F_CONFIGURING                   0x00200000
 #define F_NEEDAUTH                      0x00400000
+#define F_SRV_LOOKUP                    0x00800000
 #define F_FIXED_ATTR(n)                         (1<<n)
 #define F_LIST(n)                        (1<<n)
        enum client_state                idm_state;
        u_int32_t                        idm_flags; /* lower 20 reserved */
        u_int32_t                        idm_list;
        struct ypldap_addr_list          idm_addr;
+       struct ypldap_srv_list           idm_srv;
+       char                             idm_srv_name[HOST_NAME_MAX+1];
        in_port_t                        idm_port;
        char                             idm_binddn[LINE_WIDTH];
        char                             idm_bindcred[LINE_WIDTH];
Index: ypldap_dns.c
===================================================================
RCS file: /cvs/src/usr.sbin/ypldap/ypldap_dns.c,v
retrieving revision 1.11
diff -u -p -u -p -r1.11 ypldap_dns.c
--- ypldap_dns.c        10 Apr 2016 09:59:21 -0000      1.11
+++ ypldap_dns.c        1 May 2016 23:47:34 -0000
@@ -47,6 +47,7 @@ void  dns_dispatch_imsg(int, short, void 
 void   dns_sig_handler(int, short, void *);
 void   dns_shutdown(void);
 int    host_dns(const char *, struct ypldap_addr_list *);
+int    srv_dns(const char *, struct ypldap_srv_list *);
 
 void
 dns_sig_handler(int sig, short event, void *p)
@@ -132,6 +133,8 @@ dns_dispatch_imsg(int fd, short events, 
        char                    *name;
        struct ypldap_addr_list  hn = TAILQ_HEAD_INITIALIZER(hn);
        struct ypldap_addr      *h;
+       struct ypldap_srv_list   sn = TAILQ_HEAD_INITIALIZER(sn);
+       struct ypldap_srv       *s;
        struct ibuf             *buf;
        struct env              *env = p;
        struct imsgev           *iev = env->sc_iev;
@@ -161,15 +164,16 @@ dns_dispatch_imsg(int fd, short events, 
                if (n == 0)
                        break;
 
+               name = imsg.data;
+               if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
+                       fatalx("invalid dns message received");
+               imsg.hdr.len -= 1 + IMSG_HEADER_SIZE;
+               if (name[imsg.hdr.len] != '\0' ||
+                   strlen(name) != imsg.hdr.len)
+                       fatalx("invalid dns message received");
+
                switch (imsg.hdr.type) {
                case IMSG_HOST_DNS:
-                       name = imsg.data;
-                       if (imsg.hdr.len < 1 + IMSG_HEADER_SIZE)
-                               fatalx("invalid IMSG_HOST_DNS received");
-                       imsg.hdr.len -= 1 + IMSG_HEADER_SIZE;
-                       if (name[imsg.hdr.len] != '\0' ||
-                           strlen(name) != imsg.hdr.len)
-                               fatalx("invalid IMSG_HOST_DNS received");
                        if ((cnt = host_dns(name, &hn)) == -1)
                                break;
                        buf = imsg_create(ibuf, IMSG_HOST_DNS,
@@ -188,6 +192,29 @@ dns_dispatch_imsg(int fd, short events, 
 
                        imsg_close(ibuf, buf);
                        break;
+
+               case IMSG_SRV_DNS:
+                       if ((cnt = srv_dns(name, &sn)) == -1)
+                               break;
+
+                       buf = imsg_create(ibuf, IMSG_SRV_DNS,
+                           imsg.hdr.peerid, 0,
+                           cnt * (NI_MAXHOST + sizeof(uint16_t)));
+                       if (buf == NULL)
+                               break;
+
+                       if (cnt > 0) {
+                               while (!TAILQ_EMPTY(&sn)) {
+                                       s = TAILQ_FIRST(&sn);
+                                       TAILQ_REMOVE(&sn, s, next);
+                                       imsg_add(buf, s, sizeof(*s));
+                                       free(s);
+                               }
+                       }
+                       
+                       imsg_close(ibuf, buf);
+                       break;
+
                default:
                        break;
                }
@@ -248,5 +275,62 @@ host_dns(const char *s, struct ypldap_ad
                cnt++;
        }
        freeaddrinfo(res0);
+       return (cnt);
+}
+
+int
+srv_dns(const char *s, struct ypldap_srv_list *sn)
+{
+       struct rrsetinfo        *res;
+       struct rdatainfo        *rrdata;
+       struct ypldap_srv       *srv;
+       uint16_t                *sdata;
+       unsigned int             i;
+       int                      error, cnt = 0;
+       char                     rr[NI_MAXHOST];
+
+       if (snprintf(rr, NI_MAXHOST, "_ldap._tcp.%s", s) >= NI_MAXHOST) {
+               log_warnx("srv domain name %s is too long", s);
+               return (-1);
+       }
+
+       error = getrrsetbyname(rr, C_IN, T_SRV, 0, &res);
+       switch (error) {
+       case ERRSET_NOMEMORY:
+       case ERRSET_NODATA:
+       case ERRSET_NONAME:
+               return (0);
+       case 0:
+               break;
+       default:
+               log_warnx("could not parse \"%s\"", s);
+               return (-1);
+       }
+
+       for (i = 0; i < res->rri_nrdatas; i++) {
+               rrdata = &res->rri_rdatas[i];
+               if (rrdata->rdi_length < 7) {
+                       log_warnx("short srv record for \"%s\"", s);
+                       continue;
+               }
+
+               srv = calloc(1, sizeof(*srv));
+               if (srv == NULL)
+                       break;
+
+               sdata = (uint16_t *)rrdata->rdi_data;
+               srv->priority = ntohs(sdata[0]);
+               srv->weight = ntohs(sdata[1]);
+               srv->port = ntohs(sdata[2]);
+
+               dn_expand(rrdata->rdi_data,
+                   rrdata->rdi_data + rrdata->rdi_length,
+                   rrdata->rdi_data + (3 * sizeof(uint16_t)),
+                   srv->hostname, NI_MAXHOST);
+
+               TAILQ_INSERT_HEAD(sn, srv, next);
+               cnt++;
+       }
+       freerrset(res);
        return (cnt);
 }

Reply via email to