]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ypldap/ldapclient.c
MFV r294491: ntp 4.2.8p6.
[FreeBSD/FreeBSD.git] / usr.sbin / ypldap / ldapclient.c
1 /* $OpenBSD: ldapclient.c,v 1.31 2014/11/16 23:24:44 tedu Exp $ */
2 /* $FreeBSD$ */
3
4 /*
5  * Copyright (c) 2008 Alexander Schrijver <aschrijver@openbsd.org>
6  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20
21 #include <sys/types.h>
22 #include <sys/param.h>
23 #include <sys/queue.h>
24 #include <sys/socket.h>
25 #include <sys/tree.h>
26
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29
30 #include <netdb.h>
31 #include <errno.h>
32 #include <err.h>
33 #include <event.h>
34 #include <fcntl.h>
35 #include <unistd.h>
36 #include <pwd.h>
37 #include <signal.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41
42 #include "aldap.h"
43 #include "ypldap.h"
44
45 void    client_sig_handler(int, short, void *);
46 void    client_dispatch_dns(int, short, void *);
47 void    client_dispatch_parent(int, short, void *);
48 void    client_shutdown(void);
49 void    client_connect(int, short, void *);
50 void    client_configure(struct env *);
51 void    client_periodic_update(int, short, void *);
52 int     client_build_req(struct idm *, struct idm_req *, struct aldap_message *,
53             int, int);
54 int     client_search_idm(struct env *, struct idm *, struct aldap *,
55             char **, char *, int, int, enum imsg_type);
56 int     client_try_idm(struct env *, struct idm *);
57 int     client_addr_init(struct idm *);
58 int     client_addr_free(struct idm *);
59
60 struct aldap    *client_aldap_open(struct ypldap_addr *);
61
62 /*
63  * dummy wrapper to provide aldap_init with its fd's.
64  */
65 struct aldap *
66 client_aldap_open(struct ypldap_addr *addr)
67 {
68         int                      fd = -1;
69         struct ypldap_addr       *p;
70
71         for (p = addr; p != NULL; p = p->next) {
72                 char                     hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
73                 struct sockaddr         *sa = (struct sockaddr *)&p->ss;
74
75                 if (getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf,
76                         sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV))
77                                 errx(1, "could not get numeric hostname");
78
79                 if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) < 0)
80                         return NULL;
81
82                 if (connect(fd, sa, sa->sa_len) == 0)
83                         break;
84
85                 warn("connect to %s port %s (%s) failed", hbuf, sbuf, "tcp");
86                 close(fd);
87         }
88
89         if (fd == -1)
90                 return NULL;
91
92         return aldap_init(fd);
93 }
94
95 int
96 client_addr_init(struct idm *idm)
97 {
98         struct sockaddr_in      *sa_in;
99         struct sockaddr_in6     *sa_in6;
100         struct ypldap_addr         *h;
101
102         for (h = idm->idm_addr; h != NULL; h = h->next) {
103                 switch (h->ss.ss_family) {
104                 case AF_INET:
105                         sa_in = (struct sockaddr_in *)&h->ss;
106                         if (ntohs(sa_in->sin_port) == 0)
107                                 sa_in->sin_port = htons(LDAP_PORT);
108                         idm->idm_state = STATE_DNS_DONE;
109                         break;
110                 case AF_INET6:
111                         sa_in6 = (struct sockaddr_in6 *)&h->ss;
112                         if (ntohs(sa_in6->sin6_port) == 0)
113                                 sa_in6->sin6_port = htons(LDAP_PORT);
114                         idm->idm_state = STATE_DNS_DONE;
115                         break;
116                 default:
117                         fatalx("king bula sez: wrong AF in client_addr_init");
118                         /* not reached */
119                 }
120         }
121
122         return (0);
123 }
124
125 int
126 client_addr_free(struct idm *idm)
127 {
128         struct ypldap_addr         *h, *p;
129
130         if (idm->idm_addr == NULL)
131                 return (-1);
132
133         for (h = idm->idm_addr; h != NULL; h = p) {
134                 p = h->next;
135                 free(h);
136         }
137
138         idm->idm_addr = NULL;
139
140         return (0);
141 }
142
143 void
144 client_sig_handler(int sig, short event, void *p)
145 {
146         switch (sig) {
147         case SIGINT:
148         case SIGTERM:
149                 client_shutdown();
150                 break;
151         default:
152                 fatalx("unexpected signal");
153         }
154 }
155
156 void
157 client_dispatch_dns(int fd, short events, void *p)
158 {
159         struct imsg              imsg;
160         u_int16_t                dlen;
161         u_char                  *data;
162         struct ypldap_addr      *h;
163         int                      n, wait_cnt = 0;
164         struct idm              *idm;
165         int                      shut = 0;
166
167         struct env              *env = p;
168         struct imsgev           *iev = env->sc_iev_dns;
169         struct imsgbuf          *ibuf = &iev->ibuf;
170
171         if ((events & (EV_READ | EV_WRITE)) == 0)
172                 fatalx("unknown event");
173
174         if (events & EV_READ) {
175                 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
176                         fatal("imsg_read error");
177                 if (n == 0)
178                         shut = 1;
179         }
180         if (events & EV_WRITE) {
181                 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
182                         fatal("msgbuf_write");
183                 if (n == 0)
184                         shut = 1;
185                 goto done;
186         }
187
188         for (;;) {
189                 if ((n = imsg_get(ibuf, &imsg)) == -1)
190                         fatal("client_dispatch_dns: imsg_get error");
191                 if (n == 0)
192                         break;
193
194                 switch (imsg.hdr.type) {
195                 case IMSG_HOST_DNS:
196                         TAILQ_FOREACH(idm, &env->sc_idms, idm_entry)
197                                 if (idm->idm_id == imsg.hdr.peerid)
198                                         break;
199                         if (idm == NULL) {
200                                 log_warnx("IMSG_HOST_DNS with invalid peerID");
201                                 break;
202                         }
203                         if (idm->idm_addr != NULL) {
204                                 log_warnx("IMSG_HOST_DNS but addr != NULL!");
205                                 break;
206                         }
207
208                         dlen = imsg.hdr.len - IMSG_HEADER_SIZE;
209                         if (dlen == 0) {        /* no data -> temp error */
210                                 idm->idm_state = STATE_DNS_TEMPFAIL;
211                                 break;
212                         }
213
214                         data = (u_char *)imsg.data;
215                         while (dlen >= sizeof(struct sockaddr_storage)) {
216                                 if ((h = calloc(1, sizeof(struct ypldap_addr))) ==
217                                     NULL)
218                                         fatal(NULL);
219                                 memcpy(&h->ss, data, sizeof(h->ss));
220
221                                 if (idm->idm_addr == NULL)
222                                         h->next = NULL;
223                                 else
224                                         h->next = idm->idm_addr;
225
226                                 idm->idm_addr = h;
227
228                                 data += sizeof(h->ss);
229                                 dlen -= sizeof(h->ss);
230                         }
231                         if (dlen != 0)
232                                 fatalx("IMSG_HOST_DNS: dlen != 0");
233
234                         client_addr_init(idm);
235
236                         break;
237                 default:
238                         break;
239                 }
240                 imsg_free(&imsg);
241         }
242
243         TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
244                 if (client_try_idm(env, idm) == -1)
245                         idm->idm_state = STATE_LDAP_FAIL;
246
247                 if (idm->idm_state < STATE_LDAP_DONE)
248                         wait_cnt++;
249         }
250         if (wait_cnt == 0)
251                 imsg_compose_event(env->sc_iev, IMSG_END_UPDATE, 0, 0, -1,
252                     NULL, 0);
253
254 done:
255         if (!shut)
256                 imsg_event_add(iev);
257         else {
258                 /* this pipe is dead, so remove the event handler */
259                 event_del(&iev->ev);
260                 event_loopexit(NULL);
261         }
262 }
263
264 void
265 client_dispatch_parent(int fd, short events, void *p)
266 {
267         int                      n;
268         int                      shut = 0;
269         struct imsg              imsg;
270         struct env              *env = p;
271         struct imsgev           *iev = env->sc_iev;
272         struct imsgbuf          *ibuf = &iev->ibuf;
273
274         if ((events & (EV_READ | EV_WRITE)) == 0)
275                 fatalx("unknown event");
276
277         if (events & EV_READ) {
278                 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
279                         fatal("imsg_read error");
280                 if (n == 0)
281                         shut = 1;
282         }
283         if (events & EV_WRITE) {
284                 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
285                         fatal("msgbuf_write");
286                 if (n == 0)
287                         shut = 1;
288                 goto done;
289         }
290
291         for (;;) {
292                 if ((n = imsg_get(ibuf, &imsg)) == -1)
293                         fatal("client_dispatch_parent: imsg_get error");
294                 if (n == 0)
295                         break;
296
297                 switch (imsg.hdr.type) {
298                 case IMSG_CONF_START: {
299                         struct env      params;
300
301                         if (env->sc_flags & F_CONFIGURING) {
302                                 log_warnx("configuration already in progress");
303                                 break;
304                         }
305                         memcpy(&params, imsg.data, sizeof(params));
306                         log_debug("configuration starting");
307                         env->sc_flags |= F_CONFIGURING;
308                         purge_config(env);
309                         memcpy(&env->sc_conf_tv, &params.sc_conf_tv,
310                             sizeof(env->sc_conf_tv));
311                         env->sc_flags |= params.sc_flags;
312                         break;
313                 }
314                 case IMSG_CONF_IDM: {
315                         struct idm      *idm;
316
317                         if (!(env->sc_flags & F_CONFIGURING))
318                                 break;
319                         if ((idm = calloc(1, sizeof(*idm))) == NULL)
320                                 fatal(NULL);
321                         memcpy(idm, imsg.data, sizeof(*idm));
322                         idm->idm_env = env;
323                         TAILQ_INSERT_TAIL(&env->sc_idms, idm, idm_entry);
324                         break;
325                 }
326                 case IMSG_CONF_END:
327                         env->sc_flags &= ~F_CONFIGURING;
328                         log_debug("applying configuration");
329                         client_configure(env);
330                         break;
331                 default:
332                         log_debug("client_dispatch_parent: unexpect imsg %d",
333                             imsg.hdr.type);
334
335                         break;
336                 }
337                 imsg_free(&imsg);
338         }
339
340 done:
341         if (!shut)
342                 imsg_event_add(iev);
343         else {
344                 /* this pipe is dead, so remove the event handler */
345                 event_del(&iev->ev);
346                 event_loopexit(NULL);
347         }
348 }
349
350 void
351 client_shutdown(void)
352 {
353         log_info("ldap client exiting");
354         _exit(0);
355 }
356
357 pid_t
358 ldapclient(int pipe_main2client[2])
359 {
360         pid_t            pid, dns_pid;
361         int              pipe_dns[2];
362         struct passwd   *pw;
363         struct event     ev_sigint;
364         struct event     ev_sigterm;
365         struct env       env;
366
367         switch (pid = fork()) {
368         case -1:
369                 fatal("cannot fork");
370                 break;
371         case 0:
372                 break;
373         default:
374                 return (pid);
375         }
376
377         bzero(&env, sizeof(env));
378         TAILQ_INIT(&env.sc_idms);
379
380         if ((pw = getpwnam(YPLDAP_USER)) == NULL)
381                 fatal("getpwnam");
382
383         if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_dns) == -1)
384                 fatal("socketpair");
385         dns_pid = ypldap_dns(pipe_dns, pw);
386         close(pipe_dns[1]);
387
388 #ifndef DEBUG
389         if (chroot(pw->pw_dir) == -1)
390                 fatal("chroot");
391         if (chdir("/") == -1)
392                 fatal("chdir");
393 #else
394 #warning disabling chrooting in DEBUG mode
395 #endif
396         setproctitle("ldap client");
397         ypldap_process = PROC_CLIENT;
398
399 #ifndef DEBUG
400         if (setgroups(1, &pw->pw_gid) ||
401             setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
402             setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
403                 fatal("cannot drop privileges");
404 #else
405 #warning disabling privilege revocation in DEBUG mode
406 #endif
407
408         event_init();
409         signal(SIGPIPE, SIG_IGN);
410         signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL);
411         signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL);
412         signal_add(&ev_sigint, NULL);
413         signal_add(&ev_sigterm, NULL);
414
415         close(pipe_main2client[0]);
416         if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
417                 fatal(NULL);
418         if ((env.sc_iev_dns = calloc(1, sizeof(*env.sc_iev_dns))) == NULL)
419                 fatal(NULL);
420
421         env.sc_iev->events = EV_READ;
422         env.sc_iev->data = &env;
423         imsg_init(&env.sc_iev->ibuf, pipe_main2client[1]);
424         env.sc_iev->handler = client_dispatch_parent;
425         event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
426             env.sc_iev->handler, &env);
427         event_add(&env.sc_iev->ev, NULL);
428
429         env.sc_iev_dns->events = EV_READ;
430         env.sc_iev_dns->data = &env;
431         imsg_init(&env.sc_iev_dns->ibuf, pipe_dns[0]);
432         env.sc_iev_dns->handler = client_dispatch_dns;
433         event_set(&env.sc_iev_dns->ev, env.sc_iev_dns->ibuf.fd,
434             env.sc_iev_dns->events, env.sc_iev_dns->handler, &env);
435         event_add(&env.sc_iev_dns->ev, NULL);
436
437         event_dispatch();
438         client_shutdown();
439
440         return (0);
441
442 }
443
444 int
445 client_build_req(struct idm *idm, struct idm_req *ir, struct aldap_message *m,
446     int min_attr, int max_attr)
447 {
448         char    **ldap_attrs;
449         int      i, k;
450
451         bzero(ir, sizeof(*ir));
452         for (i = min_attr; i < max_attr; i++) {
453                 if (idm->idm_flags & F_FIXED_ATTR(i)) {
454                         if (strlcat(ir->ir_line, idm->idm_attrs[i],
455                             sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
456                                 /*
457                                  * entry yields a line > 1024, trash it.
458                                  */
459                                 return (-1);
460
461                         if (i == ATTR_UID) {
462                                 ir->ir_key.ik_uid = strtonum(
463                                     idm->idm_attrs[i], 0,
464                                     UID_MAX, NULL);
465                         } else if (i == ATTR_GR_GID) {
466                                 ir->ir_key.ik_gid = strtonum(
467                                     idm->idm_attrs[i], 0,
468                                     GID_MAX, NULL);
469                         }
470                 } else if (idm->idm_list & F_LIST(i)) {
471                         aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs);
472                         for (k = 0; k >= 0 && ldap_attrs && ldap_attrs[k] != NULL; k++) {
473                                 /* XXX: Fail when attributes have illegal characters e.g. ',' */
474                                 if (strlcat(ir->ir_line, ldap_attrs[k],
475                                     sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
476                                         continue;
477                                 if (ldap_attrs[k+1] != NULL)
478                                         if (strlcat(ir->ir_line, ",",
479                                                     sizeof(ir->ir_line))
480                                             >= sizeof(ir->ir_line)) {
481                                                 aldap_free_attr(ldap_attrs);
482                                                 return (-1);
483                                         }
484                         }
485                         aldap_free_attr(ldap_attrs);
486                 } else {
487                         if (aldap_match_attr(m, idm->idm_attrs[i], &ldap_attrs) == -1)
488                                 return (-1);
489                         if (ldap_attrs[0] == NULL)
490                                 return (-1);
491                         if (strlcat(ir->ir_line, ldap_attrs[0],
492                             sizeof(ir->ir_line)) >= sizeof(ir->ir_line)) {
493                                 aldap_free_attr(ldap_attrs);
494                                 return (-1);
495                         }
496                         if (i == ATTR_UID) {
497                                 ir->ir_key.ik_uid = strtonum(
498                                     ldap_attrs[0], 0, UID_MAX, NULL);
499                         } else if (i == ATTR_GR_GID) {
500                                 ir->ir_key.ik_uid = strtonum(
501                                     ldap_attrs[0], 0, GID_MAX, NULL);
502                         }
503                         aldap_free_attr(ldap_attrs);
504                 }
505
506                 if (i + 1 != max_attr)
507                         if (strlcat(ir->ir_line, ":",
508                             sizeof(ir->ir_line)) >= sizeof(ir->ir_line))
509                                 return (-1);
510         }
511
512         return (0);
513 }
514
515 int
516 client_search_idm(struct env *env, struct idm *idm, struct aldap *al,
517     char **attrs, char *filter, int min_attr, int max_attr,
518     enum imsg_type type)
519 {
520         struct idm_req           ir;
521         struct aldap_message    *m;
522         struct aldap_page_control *pg = NULL;
523         const char              *errstr;
524         char                    *dn;
525
526         dn = idm->idm_basedn;
527         if (type == IMSG_GRP_ENTRY && idm->idm_groupdn[0] != '\0')
528                 dn = idm->idm_groupdn;
529
530         do {
531                 if (aldap_search(al, dn, LDAP_SCOPE_SUBTREE,
532                     filter, attrs, 0, 0, 0, pg) == -1) {
533                         aldap_get_errno(al, &errstr);
534                         log_debug("%s", errstr);
535                         return (-1);
536                 }
537
538                 if (pg != NULL) {
539                         aldap_freepage(pg);
540                         pg = NULL;
541                 }
542
543                 while ((m = aldap_parse(al)) != NULL) {
544                         if (al->msgid != m->msgid) {
545                                 goto fail;
546                         }
547                         
548                         if (m->message_type == LDAP_RES_SEARCH_RESULT) {
549                                 if (m->page != NULL && m->page->cookie_len != 0)
550                                         pg = m->page;
551                                 else
552                                         pg = NULL;
553
554                                 aldap_freemsg(m);
555                                 break;
556                         }
557
558                         if (m->message_type != LDAP_RES_SEARCH_ENTRY) {
559                                 goto fail;
560                         }
561
562                         if (client_build_req(idm, &ir, m, min_attr, max_attr) == 0)
563                                 imsg_compose_event(env->sc_iev, type, 0, 0, -1,
564                                     &ir, sizeof(ir));
565
566                         aldap_freemsg(m);       
567                 }
568         } while (pg != NULL);
569
570         return (0);
571
572 fail:
573         aldap_freemsg(m);       
574         if (pg != NULL) {
575                 aldap_freepage(pg);
576         }
577
578         return (-1);
579 }
580
581 int
582 client_try_idm(struct env *env, struct idm *idm)
583 {
584         const char              *where;
585         char                    *attrs[ATTR_MAX+1];
586         int                      i, j;
587         struct aldap_message    *m;
588         struct aldap            *al;
589
590         where = "connect";
591         if ((al = client_aldap_open(idm->idm_addr)) == NULL)
592                 return (-1);
593
594         if (idm->idm_flags & F_NEEDAUTH) {
595                 where = "binding";
596                 if (aldap_bind(al, idm->idm_binddn, idm->idm_bindcred) == -1)
597                         goto bad;
598
599                 where = "parsing";
600                 if ((m = aldap_parse(al)) == NULL)
601                         goto bad;
602                 where = "verifying msgid";
603                 if (al->msgid != m->msgid) {
604                         aldap_freemsg(m);
605                         goto bad;
606                 }
607                 aldap_freemsg(m);
608         }
609
610         bzero(attrs, sizeof(attrs));
611         for (i = 0, j = 0; i < ATTR_MAX; i++) {
612                 if (idm->idm_flags & F_FIXED_ATTR(i))
613                         continue;
614                 attrs[j++] = idm->idm_attrs[i];
615         }
616         attrs[j] = NULL;
617
618         /*
619          * build password line.
620          */
621         where = "search";
622         log_debug("searching password entries");
623         if (client_search_idm(env, idm, al, attrs,
624             idm->idm_filters[FILTER_USER], 0, ATTR_MAX, IMSG_PW_ENTRY) == -1)
625                 goto bad;
626
627         bzero(attrs, sizeof(attrs));
628         for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) {
629                 if (idm->idm_flags & F_FIXED_ATTR(i))
630                         continue;
631                 attrs[j++] = idm->idm_attrs[i];
632         }
633         attrs[j] = NULL;
634
635         /*
636          * build group line.
637          */
638         where = "search";
639         log_debug("searching group entries");
640         if (client_search_idm(env, idm, al, attrs,
641             idm->idm_filters[FILTER_GROUP], ATTR_GR_MIN, ATTR_GR_MAX,
642             IMSG_GRP_ENTRY) == -1)
643                 goto bad;
644
645         aldap_close(al);
646
647         idm->idm_state = STATE_LDAP_DONE;
648
649         return (0);
650 bad:
651         aldap_close(al);
652         log_debug("directory %s errored out in %s", idm->idm_name, where);
653         return (-1);
654 }
655
656 void
657 client_periodic_update(int fd, short event, void *p)
658 {
659         struct env      *env = p;
660
661         struct idm      *idm;
662         int              fail_cnt = 0;
663
664         /* If LDAP isn't finished, notify the master process to trash the
665          * update. */
666         TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
667                 if (idm->idm_state < STATE_LDAP_DONE)
668                         fail_cnt++;
669
670                 idm->idm_state = STATE_NONE;
671
672                 client_addr_free(idm);
673         }
674         if (fail_cnt > 0) {
675                 log_debug("trash the update");
676                 imsg_compose_event(env->sc_iev, IMSG_TRASH_UPDATE, 0, 0, -1,
677                     NULL, 0);
678         }
679
680         client_configure(env);
681 }
682
683 void
684 client_configure(struct env *env)
685 {
686         struct timeval   tv;
687         struct idm      *idm;
688         u_int16_t        dlen;
689
690         log_debug("connecting to directories");
691
692         imsg_compose_event(env->sc_iev, IMSG_START_UPDATE, 0, 0, -1, NULL, 0);
693
694         /* Start the DNS lookups */
695         TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
696                 dlen = strlen(idm->idm_name) + 1;
697                 imsg_compose_event(env->sc_iev_dns, IMSG_HOST_DNS, idm->idm_id,
698                     0, -1, idm->idm_name, dlen);
699         }
700
701         tv.tv_sec = env->sc_conf_tv.tv_sec;
702         tv.tv_usec = env->sc_conf_tv.tv_usec;
703         evtimer_set(&env->sc_conf_ev, client_periodic_update, env);
704         evtimer_add(&env->sc_conf_ev, &tv);
705 }