]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ypldap/ypldap.c
MFV r298178:
[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: user: %s is referenced as a "
247                                         "group member, but can't be found in the "
248                                         "users map.\n", ukey.ue_line);
249                                 if (bp != NULL)
250                                         *(bp-1) = ',';
251                                 continue;
252                         }
253                         if (bp != NULL)
254                                 *(bp-1) = ',';
255
256                         /* Make sure the new group doesn't equal to the main gid */
257                         if (ge->ge_gid == ue->ue_gid)
258                                 continue;
259
260                         len = strlen(ue->ue_netid_line);
261                         p = ue->ue_netid_line + len;
262
263                         if ((snprintf(p, LINE_WIDTH-len-1, ",%d",
264                                 ge->ge_gid)) >= (int)(LINE_WIDTH-len)) {
265                                 return (-1);
266                         }
267                 }
268         }
269
270         return (0);
271 }
272
273 void
274 main_end_update(struct env *env)
275 {
276         struct userent          *ue;
277         struct groupent         *ge;
278
279         if (env->update_trashed)
280                 return;
281
282         log_debug("updates are over, cleaning up trees now");
283
284         if (main_create_user_groups(env) == -1) {
285                 main_trash_update(env);
286                 return;
287         }
288
289         if (env->sc_user_names == NULL) {
290                 env->sc_user_names = env->sc_user_names_t;
291                 env->sc_user_lines = NULL;
292                 env->sc_user_names_t = NULL;
293
294                 env->sc_group_names = env->sc_group_names_t;
295                 env->sc_group_lines = NULL;
296                 env->sc_group_names_t = NULL;
297
298                 flatten_entries(env);
299                 goto make_uids;
300         }
301
302         /*
303          * clean previous tree.
304          */
305         while ((ue = RB_ROOT(env->sc_user_names)) != NULL) {
306                 RB_REMOVE(user_name_tree, env->sc_user_names,
307                     ue);
308                 free(ue->ue_netid_line);
309                 free(ue);
310         }
311         free(env->sc_user_names);
312         free(env->sc_user_lines);
313
314         env->sc_user_names = env->sc_user_names_t;
315         env->sc_user_lines = NULL;
316         env->sc_user_names_t = NULL;
317
318         while ((ge = RB_ROOT(env->sc_group_names)) != NULL) {
319                 RB_REMOVE(group_name_tree,
320                     env->sc_group_names, ge);
321                 free(ge);
322         }
323         free(env->sc_group_names);
324         free(env->sc_group_lines);
325
326         env->sc_group_names = env->sc_group_names_t;
327         env->sc_group_lines = NULL;
328         env->sc_group_names_t = NULL;
329
330
331         flatten_entries(env);
332
333         /*
334          * trees are flat now. build up uid, gid and netid trees.
335          */
336
337 make_uids:
338         RB_INIT(&env->sc_user_uids);
339         RB_INIT(&env->sc_group_gids);
340         RB_FOREACH(ue, user_name_tree, env->sc_user_names)
341                 RB_INSERT(user_uid_tree,
342                     &env->sc_user_uids, ue);
343         RB_FOREACH(ge, group_name_tree, env->sc_group_names)
344                 RB_INSERT(group_gid_tree,
345                     &env->sc_group_gids, ge);
346
347 }
348
349 void
350 main_dispatch_client(int fd, short events, void *p)
351 {
352         int              n;
353         int              shut = 0;
354         struct env      *env = p;
355         struct imsgev   *iev = env->sc_iev;
356         struct imsgbuf  *ibuf = &iev->ibuf;
357         struct idm_req   ir;
358         struct imsg      imsg;
359
360         if ((events & (EV_READ | EV_WRITE)) == 0)
361                 fatalx("unknown event");
362
363         if (events & EV_READ) {
364                 if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
365                         fatal("imsg_read error");
366                 if (n == 0)
367                         shut = 1;
368         }
369         if (events & EV_WRITE) {
370                 if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
371                         fatal("msgbuf_write");
372                 if (n == 0)
373                         shut = 1;
374                 goto done;
375         }
376
377         for (;;) {
378                 if ((n = imsg_get(ibuf, &imsg)) == -1)
379                         fatal("main_dispatch_client: imsg_get error");
380                 if (n == 0)
381                         break;
382
383                 switch (imsg.hdr.type) {
384                 case IMSG_START_UPDATE:
385                         main_start_update(env);
386                         break;
387                 case IMSG_PW_ENTRY: {
388                         struct userent  *ue;
389                         size_t           len;
390
391                         if (env->update_trashed)
392                                 break;
393
394                         (void)memcpy(&ir, imsg.data, sizeof(ir));
395                         if ((ue = calloc(1, sizeof(*ue))) == NULL ||
396                             (ue->ue_line = strdup(ir.ir_line)) == NULL) {
397                                 /*
398                                  * should cancel tree update instead.
399                                  */
400                                 fatal("out of memory");
401                         }
402                         ue->ue_uid = ir.ir_key.ik_uid;
403                         len = strlen(ue->ue_line) + 1;
404                         ue->ue_line[strcspn(ue->ue_line, ":")] = '\0';
405                         if (RB_INSERT(user_name_tree, env->sc_user_names_t,
406                             ue) != NULL) { /* dup */
407                                 free(ue->ue_line);
408                                 free(ue);
409                         } else
410                                 env->sc_user_line_len += len;
411                         break;
412                 }
413                 case IMSG_GRP_ENTRY: {
414                         struct groupent *ge;
415                         size_t           len;
416
417                         if (env->update_trashed)
418                                 break;
419
420                         (void)memcpy(&ir, imsg.data, sizeof(ir));
421                         if ((ge = calloc(1, sizeof(*ge))) == NULL ||
422                             (ge->ge_line = strdup(ir.ir_line)) == NULL) {
423                                 /*
424                                  * should cancel tree update instead.
425                                  */
426                                 fatal("out of memory");
427                         }
428                         ge->ge_gid = ir.ir_key.ik_gid;
429                         len = strlen(ge->ge_line) + 1;
430                         ge->ge_line[strcspn(ge->ge_line, ":")] = '\0';
431                         if (RB_INSERT(group_name_tree, env->sc_group_names_t,
432                             ge) != NULL) { /* dup */
433                                 free(ge->ge_line);
434                                 free(ge);
435                         } else
436                                 env->sc_group_line_len += len;
437                         break;
438                 }
439                 case IMSG_TRASH_UPDATE:
440                         main_trash_update(env);
441                         break;
442                 case IMSG_END_UPDATE: {
443                         main_end_update(env);
444                         break;
445                 }
446                 default:
447                         log_debug("main_dispatch_client: unexpected imsg %d",
448                            imsg.hdr.type);
449                         break;
450                 }
451                 imsg_free(&imsg);
452         }
453
454 done:
455         if (!shut)
456                 imsg_event_add(iev);
457         else {
458                 log_debug("king bula sez: ran into dead pipe");
459                 event_del(&iev->ev);
460                 event_loopexit(NULL);
461         }
462 }
463
464 void
465 main_configure_client(struct env *env)
466 {
467         struct idm      *idm;
468         struct imsgev   *iev = env->sc_iev;
469
470         imsg_compose_event(iev, IMSG_CONF_START, 0, 0, -1, env, sizeof(*env));
471         TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) {
472                 imsg_compose_event(iev, IMSG_CONF_IDM, 0, 0, -1,
473                     idm, sizeof(*idm));
474         }
475         imsg_compose_event(iev, IMSG_CONF_END, 0, 0, -1, NULL, 0);
476 }
477
478 void
479 main_init_timer(int fd, short event, void *p)
480 {
481         struct env      *env = p;
482
483         main_configure_client(env);
484 }
485
486 void
487 purge_config(struct env *env)
488 {
489         struct idm      *idm;
490
491         while ((idm = TAILQ_FIRST(&env->sc_idms)) != NULL) {
492                 TAILQ_REMOVE(&env->sc_idms, idm, idm_entry);
493                 free(idm);
494         }
495 }
496
497 int
498 main(int argc, char *argv[])
499 {
500         int              c;
501         int              debug;
502         struct passwd   *pw;
503         struct env       env;
504         struct event     ev_sigint;
505         struct event     ev_sigterm;
506         struct event     ev_sigchld;
507         struct event     ev_sighup;
508         struct event     ev_timer;
509         struct timeval   tv;
510
511         debug = 0;
512         ypldap_process = PROC_MAIN;
513
514         log_init(1);
515
516         while ((c = getopt(argc, argv, "dD:nf:v")) != -1) {
517                 switch (c) {
518                 case 'd':
519                         debug = 2;
520                         break;
521                 case 'D':
522                         if (cmdline_symset(optarg) < 0)
523                                 log_warnx("could not parse macro definition %s",
524                                     optarg);
525                         break;
526                 case 'n':
527                         debug = 2;
528                         opts |= YPLDAP_OPT_NOACTION;
529                         break;
530                 case 'f':
531                         conffile = optarg;
532                         break;
533                 case 'v':
534                         opts |= YPLDAP_OPT_VERBOSE;
535                         break;
536                 default:
537                         usage();
538                 }
539         }
540
541         argc -= optind;
542         argv += optind;
543
544         if (argc)
545                 usage();
546
547         RB_INIT(&env.sc_user_uids);
548         RB_INIT(&env.sc_group_gids);
549
550         if (parse_config(&env, conffile, opts))
551                 exit(1);
552         if (opts & YPLDAP_OPT_NOACTION) {
553                 fprintf(stderr, "configuration OK\n");
554                 exit(0);
555         }
556
557         if (geteuid())
558                 errx(1, "need root privileges");
559
560         log_init(debug);
561
562         if (!debug) {
563                 if (daemon(1, 0) == -1)
564                         err(1, "failed to daemonize");
565         }
566
567         log_info("startup%s", (debug > 1)?" [debug mode]":"");
568
569         if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, PF_UNSPEC,
570             pipe_main2client) == -1)
571                 fatal("socketpair");
572
573         client_pid = ldapclient(pipe_main2client);
574
575         setproctitle("parent");
576         event_init();
577
578         signal_set(&ev_sigint, SIGINT, main_sig_handler, &env);
579         signal_set(&ev_sigterm, SIGTERM, main_sig_handler, &env);
580         signal_set(&ev_sighup, SIGHUP, main_sig_handler, &env);
581         signal_set(&ev_sigchld, SIGCHLD, main_sig_handler, &env);
582         signal_add(&ev_sigint, NULL);
583         signal_add(&ev_sigterm, NULL);
584         signal_add(&ev_sighup, NULL);
585         signal_add(&ev_sigchld, NULL);
586
587         close(pipe_main2client[1]);
588         if ((env.sc_iev = calloc(1, sizeof(*env.sc_iev))) == NULL)
589                 fatal(NULL);
590         imsg_init(&env.sc_iev->ibuf, pipe_main2client[0]);
591         env.sc_iev->handler = main_dispatch_client;
592
593         env.sc_iev->events = EV_READ;
594         env.sc_iev->data = &env;
595         event_set(&env.sc_iev->ev, env.sc_iev->ibuf.fd, env.sc_iev->events,
596              env.sc_iev->handler, &env);
597         event_add(&env.sc_iev->ev, NULL);
598
599         yp_init(&env);
600
601         if ((pw = getpwnam(YPLDAP_USER)) == NULL)
602                 fatal("getpwnam");
603
604 #ifndef DEBUG
605         if (setgroups(1, &pw->pw_gid) ||
606             setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
607             setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
608                 fatal("cannot drop privileges");
609 #else
610 #warning disabling privilege revocation in debug mode
611 #endif
612
613         bzero(&tv, sizeof(tv));
614         evtimer_set(&ev_timer, main_init_timer, &env);
615         evtimer_add(&ev_timer, &tv);
616
617         yp_enable_events();
618         event_dispatch();
619         main_shutdown();
620
621         return (0);
622 }
623
624 void
625 imsg_event_add(struct imsgev *iev)
626 {
627         if (iev->handler == NULL) {
628                 imsg_flush(&iev->ibuf);
629                 return;
630         }
631
632         iev->events = EV_READ;
633         if (iev->ibuf.w.queued)
634                 iev->events |= EV_WRITE;
635
636         event_del(&iev->ev);
637         event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
638         event_add(&iev->ev, NULL);
639 }
640
641 int
642 imsg_compose_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
643     pid_t pid, int fd, void *data, u_int16_t datalen)
644 {
645         int     ret;
646
647         if ((ret = imsg_compose(&iev->ibuf, type, peerid,
648             pid, fd, data, datalen)) != -1)
649                 imsg_event_add(iev);
650         return (ret);
651 }