]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ypldap/ypldap.c
sys/{x86,amd64}: remove one of doubled ;s
[FreeBSD/FreeBSD.git] / usr.sbin / ypldap / ypldap.c
1 /*      $OpenBSD: ypldap.c,v 1.16 2015/11/02 10:06:06 jmatthew Exp $ */
2 /*      $FreeBSD */
3
4 /*
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19
20 #include <sys/types.h>
21 #include <sys/param.h>
22 #include <sys/queue.h>
23 #include <sys/socket.h>
24 #include <sys/signal.h>
25 #include <sys/tree.h>
26 #include <sys/wait.h>
27
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30
31 #include <err.h>
32 #include <errno.h>
33 #include <event.h>
34 #include <unistd.h>
35 #include <pwd.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <limits.h>
40
41 #include "ypldap.h"
42
43 __dead2 void     usage(void);
44 int              check_child(pid_t, const char *);
45 void             main_sig_handler(int, short, void *);
46 void             main_shutdown(void);
47 void             main_dispatch_client(int, short, void *);
48 void             main_configure_client(struct env *);
49 void             main_init_timer(int, short, void *);
50 void             main_start_update(struct env *);
51 void             main_trash_update(struct env *);
52 void             main_end_update(struct env *);
53 int              main_create_user_groups(struct env *);
54 void             purge_config(struct env *);
55 void             reconfigure(struct env *);
56
57 int              pipe_main2client[2];
58
59 pid_t            client_pid = 0;
60 char            *conffile = YPLDAP_CONF_FILE;
61 int              opts = 0;
62
63 void
64 usage(void)
65 {
66         extern const char       *__progname;
67
68         fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
69             __progname);
70         exit(1);
71 }
72
73 int
74 check_child(pid_t pid, const char *pname)
75 {
76         int     status;
77
78         if (waitpid(pid, &status, WNOHANG) > 0) {
79                 if (WIFEXITED(status)) {
80                         log_warnx("check_child: lost child %s exited", pname);
81                         return (1);
82                 }
83                 if (WIFSIGNALED(status)) {
84                         log_warnx("check_child: lost child %s terminated; "
85                             "signal %d", pname, WTERMSIG(status));
86                         return (1);
87                 }
88         }
89         return (0);
90 }
91
92 /* ARGUSED */
93 void
94 main_sig_handler(int sig, short event, void *p)
95 {
96         int              die = 0;
97
98         switch (sig) {
99         case SIGTERM:
100         case SIGINT:
101                 die = 1;
102                 /* FALLTHROUGH */
103         case SIGCHLD:
104                 if (check_child(client_pid, "ldap client")) {
105                         client_pid = 0;
106                         die = 1;
107                 }
108                 if (die)
109                         main_shutdown();
110                 break;
111         case SIGHUP:
112                 /* reconfigure */
113                 break;
114         default:
115                 fatalx("unexpected signal");
116         }
117 }
118
119 void
120 main_shutdown(void)
121 {
122         _exit(0);
123 }
124
125 void
126 main_start_update(struct env *env)
127 {
128         env->update_trashed = 0;
129
130         log_debug("starting directory update");
131         env->sc_user_line_len = 0;
132         env->sc_group_line_len = 0;
133         if ((env->sc_user_names_t = calloc(1,
134             sizeof(*env->sc_user_names_t))) == NULL ||
135             (env->sc_group_names_t = calloc(1,
136             sizeof(*env->sc_group_names_t))) == NULL)
137                 fatal(NULL);
138         RB_INIT(env->sc_user_names_t);
139         RB_INIT(env->sc_group_names_t);
140 }
141
142 /*
143  * XXX: Currently this function should only be called when updating is
144  * finished. A notification should be send to ldapclient that it should stop
145  * sending new pwd/grp entries before it can be called from different places.
146  */
147 void
148 main_trash_update(struct env *env)
149 {
150         struct userent  *ue;
151         struct groupent *ge;
152
153         env->update_trashed = 1;
154
155         while ((ue = RB_ROOT(env->sc_user_names_t)) != NULL) {
156                 RB_REMOVE(user_name_tree,
157                     env->sc_user_names_t, ue);
158                 free(ue->ue_line);
159                 free(ue->ue_netid_line);
160                 free(ue);
161         }
162         free(env->sc_user_names_t);
163         env->sc_user_names_t = NULL;
164         while ((ge = RB_ROOT(env->sc_group_names_t))
165             != NULL) {
166                 RB_REMOVE(group_name_tree,
167                     env->sc_group_names_t, ge);
168                 free(ge->ge_line);
169                 free(ge);
170         }
171         free(env->sc_group_names_t);
172         env->sc_group_names_t = NULL;
173 }
174
175 int
176 main_create_user_groups(struct env *env)
177 {
178         struct userent          *ue;
179         struct userent           ukey;
180         struct groupent         *ge;
181         gid_t                    pw_gid;
182         char                    *bp, *cp;
183         char                    *p;
184         const char              *errstr = NULL;
185         size_t                   len;
186
187         RB_FOREACH(ue, user_name_tree, env->sc_user_names_t) {
188                 bp = cp = ue->ue_line;
189
190                 /* name */
191                 bp += strlen(bp) + 1;
192
193                 /* password */
194                 bp += strcspn(bp, ":") + 1;
195
196                 /* uid */
197                 bp += strcspn(bp, ":") + 1;
198
199                 /* gid */
200                 bp[strcspn(bp, ":")] = '\0';
201
202                 pw_gid = (gid_t)strtonum(bp, 0, GID_MAX, &errstr);
203                 if (errstr) {
204                         log_warnx("main: failed to parse gid for uid: %d\n", ue->ue_uid);
205                         return (-1);
206                 }
207
208                 /* bring gid column back to its proper state */
209                 bp[strlen(bp)] = ':';
210
211                 if ((ue->ue_netid_line = calloc(1, LINE_WIDTH)) == NULL) {
212                         return (-1);
213                 }
214
215                 if (snprintf(ue->ue_netid_line, LINE_WIDTH-1, "%d:%d", ue->ue_uid, pw_gid) >= LINE_WIDTH) {
216
217                         return (-1);
218                 }
219
220                 ue->ue_gid = pw_gid;
221         }
222
223         RB_FOREACH(ge, group_name_tree, env->sc_group_names_t) {
224                 bp = cp = ge->ge_line;
225
226                 /* name */
227                 bp += strlen(bp) + 1;
228
229                 /* password */
230                 bp += strcspn(bp, ":") + 1;
231
232                 /* gid */
233                 bp += strcspn(bp, ":") + 1;
234
235                 cp = bp;
236                 if (*bp == '\0')
237                         continue;
238                 bp = cp;
239                 for (;;) { 
240                         if (!(cp = strsep(&bp, ",")))
241                                 break;
242                         ukey.ue_line = cp;
243                         if ((ue = RB_FIND(user_name_tree, env->sc_user_names_t,
244                             &ukey)) == NULL) {
245                                 /* User not found */
246                                 log_warnx("main: unknown user %s in group %s\n",
247                                    ukey.ue_line, ge->ge_line);
248                                 if (bp != NULL)
249                                         *(bp-1) = ',';
250                                 continue;
251                         }
252                         if (bp != NULL)
253                                 *(bp-1) = ',';
254
255                         /* Make sure the new group doesn't equal to the main gid */
256                         if (ge->ge_gid == ue->ue_gid)
257                                 continue;
258
259                         len = strlen(ue->ue_netid_line);
260                         p = ue->ue_netid_line + len;
261
262                         if ((snprintf(p, LINE_WIDTH-len-1, ",%d",
263                                 ge->ge_gid)) >= (int)(LINE_WIDTH-len)) {
264                                 return (-1);
265                         }
266                 }
267         }
268
269         return (0);
270 }
271
272 void
273 main_end_update(struct env *env)
274 {
275         struct userent          *ue;
276         struct groupent         *ge;
277
278         if (env->update_trashed)
279                 return;
280
281         log_debug("updates are over, cleaning up trees now");
282
283         if (main_create_user_groups(env) == -1) {
284                 main_trash_update(env);
285                 return;
286         }
287
288         if (env->sc_user_names == NULL) {
289                 env->sc_user_names = env->sc_user_names_t;
290                 env->sc_user_lines = NULL;
291                 env->sc_user_names_t = NULL;
292
293                 env->sc_group_names = env->sc_group_names_t;
294                 env->sc_group_lines = NULL;
295                 env->sc_group_names_t = NULL;
296
297                 flatten_entries(env);
298                 goto make_uids;
299         }
300
301         /*
302          * clean previous tree.
303          */
304         while ((ue = RB_ROOT(env->sc_user_names)) != NULL) {
305                 RB_REMOVE(user_name_tree, env->sc_user_names,
306                     ue);
307                 free(ue->ue_netid_line);
308                 free(ue);
309         }
310         free(env->sc_user_names);
311         free(env->sc_user_lines);
312
313         env->sc_user_names = env->sc_user_names_t;
314         env->sc_user_lines = NULL;
315         env->sc_user_names_t = NULL;
316
317         while ((ge = RB_ROOT(env->sc_group_names)) != NULL) {
318                 RB_REMOVE(group_name_tree,
319                     env->sc_group_names, ge);
320                 free(ge);
321         }
322         free(env->sc_group_names);
323         free(env->sc_group_lines);
324
325         env->sc_group_names = env->sc_group_names_t;
326         env->sc_group_lines = NULL;
327         env->sc_group_names_t = NULL;
328
329
330         flatten_entries(env);
331
332         /*
333          * trees are flat now. build up uid, gid and netid trees.
334          */
335
336 make_uids:
337         RB_INIT(&env->sc_user_uids);
338         RB_INIT(&env->sc_group_gids);
339         RB_FOREACH(ue, user_name_tree, env->sc_user_names)
340                 RB_INSERT(user_uid_tree,
341                     &env->sc_user_uids, ue);
342         RB_FOREACH(ge, group_name_tree, env->sc_group_names)
343                 RB_INSERT(group_gid_tree,
344                     &env->sc_group_gids, ge);
345
346 }
347
348 void
349 main_dispatch_client(int fd, short events, void *p)
350 {
351         int              n;
352         int              shut = 0;
353         struct env      *env = p;
354         struct imsgev   *iev = env->sc_iev;
355         struct imsgbuf  *ibuf = &iev->ibuf;
356         struct idm_req   ir;
357         struct imsg      imsg;
358
359         if ((events & (EV_READ | EV_WRITE)) == 0)
360                 fatalx("unknown event");
361
362         if (events & EV_READ) {
363                 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
364                         fatal("imsg_read error");
365                 if (n == 0)
366                         shut = 1;
367         }
368         if (events & EV_WRITE) {
369                 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
370                         fatal("msgbuf_write");
371                 if (n == 0)
372                         shut = 1;
373                 goto done;
374         }
375
376         for (;;) {
377                 if ((n = imsg_get(ibuf, &imsg)) == -1)
378                         fatal("main_dispatch_client: imsg_get error");
379                 if (n == 0)
380                         break;
381
382                 switch (imsg.hdr.type) {
383                 case IMSG_START_UPDATE:
384                         main_start_update(env);
385                         break;
386                 case IMSG_PW_ENTRY: {
387                         struct userent  *ue;
388                         size_t           len;
389
390                         if (env->update_trashed)
391                                 break;
392
393                         (void)memcpy(&ir, imsg.data, sizeof(ir));
394                         if ((ue = calloc(1, sizeof(*ue))) == NULL ||
395                             (ue->ue_line = strdup(ir.ir_line)) == NULL) {
396                                 /*
397                                  * should cancel tree update instead.
398                                  */
399                                 fatal("out of memory");
400                         }
401                         ue->ue_uid = ir.ir_key.ik_uid;
402                         len = strlen(ue->ue_line) + 1;
403                         ue->ue_line[strcspn(ue->ue_line, ":")] = '\0';
404                         if (RB_INSERT(user_name_tree, env->sc_user_names_t,
405                             ue) != NULL) { /* dup */
406                                 free(ue->ue_line);
407                                 free(ue);
408                         } else
409                                 env->sc_user_line_len += len;
410                         break;
411                 }
412                 case IMSG_GRP_ENTRY: {
413                         struct groupent *ge;
414                         size_t           len;
415
416                         if (env->update_trashed)
417                                 break;
418
419                         (void)memcpy(&ir, imsg.data, sizeof(ir));
420                         if ((ge = calloc(1, sizeof(*ge))) == NULL ||
421                             (ge->ge_line = strdup(ir.ir_line)) == NULL) {
422                                 /*
423                                  * should cancel tree update instead.
424                                  */
425                                 fatal("out of memory");
426                         }
427                         ge->ge_gid = ir.ir_key.ik_gid;
428                         len = strlen(ge->ge_line) + 1;
429                         ge->ge_line[strcspn(ge->ge_line, ":")] = '\0';
430                         if (RB_INSERT(group_name_tree, env->sc_group_names_t,
431                             ge) != NULL) { /* dup */
432                                 free(ge->ge_line);
433                                 free(ge);
434                         } else
435                                 env->sc_group_line_len += len;
436                         break;
437                 }
438                 case IMSG_TRASH_UPDATE:
439                         main_trash_update(env);
440                         break;
441                 case IMSG_END_UPDATE: {
442                         main_end_update(env);
443                         break;
444                 }
445                 default:
446                         log_debug("main_dispatch_client: unexpected imsg %d",
447                            imsg.hdr.type);
448                         break;
449                 }
450                 imsg_free(&imsg);
451         }
452
453 done:
454         if (!shut)
455                 imsg_event_add(iev);
456         else {
457                 log_debug("king bula sez: ran into dead pipe");
458                 event_del(&iev->ev);
459                 event_loopexit(NULL);
460         }
461 }
462
463 void
464 main_configure_client(struct env *env)
465 {
466         struct idm      *idm;
467         struct imsgev   *iev = env->sc_iev;
468
469         imsg_compose_event(iev, IMSG_CONF_START, 0, 0, -1, env, sizeof(*env));
470         TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
471                 imsg_compose_event(iev, IMSG_CONF_IDM, 0, 0, -1,
472                     idm, sizeof(*idm));
473         }
474         imsg_compose_event(iev, IMSG_CONF_END, 0, 0, -1, NULL, 0);
475 }
476
477 void
478 main_init_timer(int fd, short event, void *p)
479 {
480         struct env      *env = p;
481
482         main_configure_client(env);
483 }
484
485 void
486 purge_config(struct env *env)
487 {
488         struct idm      *idm;
489
490         while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) {
491                 TAILQ_REMOVE(&env->sc_idms, idm, idm_entry);
492                 free(idm);
493         }
494 }
495
496 int
497 main(int argc, char *argv[])
498 {
499         int              c;
500         int              debug;
501         struct passwd   *pw;
502         struct env       env;
503         struct event     ev_sigint;
504         struct event     ev_sigterm;
505         struct event     ev_sigchld;
506         struct event     ev_sighup;
507         struct event     ev_timer;
508         struct timeval   tv;
509
510         debug = 0;
511         ypldap_process = PROC_MAIN;
512
513         log_init(1);
514
515         while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
516                 switch (c) {
517                 case 'd':
518                         debug = 2;
519                         break;
520                 case 'D':
521                         if (cmdline_symset(optarg) < 0)
522                                 log_warnx("could not parse macro definition %s",
523                                     optarg);
524                         break;
525                 case 'n':
526                         debug = 2;
527                         opts |= YPLDAP_OPT_NOACTION;
528                         break;
529                 case 'f':
530                         conffile = optarg;
531                         break;
532                 case 'v':
533                         opts |= YPLDAP_OPT_VERBOSE;
534                         break;
535                 default:
536                         usage();
537                 }
538         }
539
540         argc -= optind;
541         argv += optind;
542
543         if (argc)
544                 usage();
545
546         RB_INIT(&env.sc_user_uids);
547         RB_INIT(&env.sc_group_gids);
548
549         if (parse_config(&env, conffile, opts))
550                 exit(1);
551         if (opts & YPLDAP_OPT_NOACTION) {
552                 fprintf(stderr, "configuration OK\n");
553                 exit(0);
554         }
555
556         if (geteuid())
557                 errx(1, "need root privileges");
558
559         log_init(debug);
560
561         if (!debug) {
562                 if (daemon(1, 0) == -1)
563                         err(1, "failed to daemonize");
564         }
565
566         log_info("startup%s", (debug > 1)?" [debug mode]":"");
567
568         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC,
569             pipe_main2client) == -1)
570                 fatal("socketpair");
571
572         client_pid = ldapclient(pipe_main2client);
573
574         setproctitle("parent");
575         event_init();
576
577         signal_set(&ev_sigint, SIGINT, main_sig_handler, &env);
578         signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env);
579         signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env);
580         signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env);
581         signal_add(&ev_sigint, NULL);
582         signal_add(&ev_sigterm, NULL);
583         signal_add(&ev_sighup, NULL);
584         signal_add(&ev_sigchld, NULL);
585
586         close(pipe_main2client[1]);
587         if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
588                 fatal(NULL);
589         imsg_init(&env.sc_iev->ibuf, pipe_main2client[0]);
590         env.sc_iev->handler = main_dispatch_client;
591
592         env.sc_iev->events = EV_READ;
593         env.sc_iev->data = &env;
594         event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
595              env.sc_iev->handler, &env);
596         event_add(&env.sc_iev->ev, NULL);
597
598         yp_init(&env);
599
600         if ((pw = getpwnam(YPLDAP_USER)) == NULL)
601                 fatal("getpwnam");
602
603 #ifndef DEBUG
604         if (setgroups(1, &pw->pw_gid) ||
605             setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
606             setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
607                 fatal("cannot drop privileges");
608 #else
609 #warning disabling privilege revocation in debug mode
610 #endif
611
612         memset(&tv, 0, sizeof(tv));
613         evtimer_set(&ev_timer, main_init_timer, &env);
614         evtimer_add(&ev_timer, &tv);
615
616         yp_enable_events();
617         event_dispatch();
618         main_shutdown();
619
620         return (0);
621 }
622
623 void
624 imsg_event_add(struct imsgev *iev)
625 {
626         if (iev->handler == NULL) {
627                 imsg_flush(&iev->ibuf);
628                 return;
629         }
630
631         iev->events = EV_READ;
632         if (iev->ibuf.w.queued)
633                 iev->events |= EV_WRITE;
634
635         event_del(&iev->ev);
636         event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
637         event_add(&iev->ev, NULL);
638 }
639
640 int
641 imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
642     pid_t pid, int fd, void *data, u_int16_t datalen)
643 {
644         int     ret;
645
646         if ((ret = imsg_compose(&iev->ibuf, type, peerid,
647             pid, fd, data, datalen)) != -1)
648                 imsg_event_add(iev);
649         return (ret);
650 }