1/* $OpenBSD: ldapd.c,v 1.32 2022/02/10 13:06:46 robert Exp $ */ 2 3/* 4 * Copyright (c) 2009, 2010 Martin Hedenfalk <martin@bzero.se> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19#include <sys/queue.h> 20#include <sys/stat.h> 21#include <sys/un.h> 22#include <sys/types.h> 23#include <sys/wait.h> 24 25#include <assert.h> 26#include <bsd_auth.h> 27#include <ctype.h> 28#include <err.h> 29#include <errno.h> 30#include <event.h> 31#include <fcntl.h> 32#include <login_cap.h> 33#include <paths.h> 34#include <signal.h> 35#include <stdio.h> 36#include <stdlib.h> 37#include <string.h> 38#include <time.h> 39#include <unistd.h> 40#include <syslog.h> 41 42#include "ldapd.h" 43#include "log.h" 44 45void usage(void); 46void ldapd_sig_handler(int fd, short why, void *data); 47void ldapd_sigchld_handler(int sig, short why, void *data); 48static void ldapd_imsgev(struct imsgev *iev, int code, struct imsg *imsg); 49static void ldapd_needfd(struct imsgev *iev); 50static void ldapd_auth_request(struct imsgev *iev, struct imsg *imsg); 51static void ldapd_open_request(struct imsgev *iev, struct imsg *imsg); 52static void ldapd_log_verbose(struct imsg *imsg); 53static pid_t start_child(enum ldapd_process, char *, int, int, int, 54 char *, char *); 55 56struct ldapd_stats stats; 57pid_t ldape_pid; 58char *datadir = DATADIR; 59 60void 61usage(void) 62{ 63 extern char *__progname; 64 65 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] " 66 "[-f file] [-r directory] [-s file]\n", __progname); 67 exit(1); 68} 69 70void 71ldapd_sig_handler(int sig, short why, void *data) 72{ 73 log_info("ldapd: got signal %d", sig); 74 if (sig == SIGINT || sig == SIGTERM) 75 event_loopexit(NULL); 76} 77 78void 79ldapd_sigchld_handler(int sig, short why, void *data) 80{ 81 pid_t pid; 82 int status; 83 84 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) { 85 if (pid == -1) { 86 if (errno == EINTR) 87 continue; 88 if (errno != ECHILD) 89 log_warn("waitpid"); 90 break; 91 } 92 93 if (WIFEXITED(status)) 94 log_debug("child %d exited with status %d", 95 pid, WEXITSTATUS(status)); 96 else if (WIFSIGNALED(status)) 97 log_debug("child %d exited due to signal %d", 98 pid, WTERMSIG(status)); 99 else 100 log_debug("child %d terminated abnormally", pid); 101 102 if (pid == ldape_pid) { 103 log_info("ldapd: lost ldap server"); 104 event_loopexit(NULL); 105 break; 106 } 107 } 108} 109 110int 111main(int argc, char *argv[]) 112{ 113 int c; 114 int debug = 0, verbose = 0, eflag = 0; 115 int configtest = 0; 116 int pipe_parent2ldap[2]; 117 char *conffile = CONFFILE; 118 char *csockpath = LDAPD_SOCKET; 119 char *saved_argv0; 120 struct imsgev *iev_ldape; 121 struct event ev_sigint; 122 struct event ev_sigterm; 123 struct event ev_sigchld; 124 struct event ev_sighup; 125 struct stat sb; 126 127 log_init(1, LOG_DAEMON); /* log to stderr until daemonized */ 128 129 saved_argv0 = argv[0]; 130 if (saved_argv0 == NULL) 131 saved_argv0 = "ldapd"; 132 133 while ((c = getopt(argc, argv, "dhvD:f:nr:s:E")) != -1) { 134 135 switch (c) { 136 case 'd': 137 debug = 1; 138 break; 139 case 'D': 140 if (cmdline_symset(optarg) < 0) { 141 warnx("could not parse macro definition %s", 142 optarg); 143 } 144 break; 145 case 'f': 146 conffile = optarg; 147 break; 148 case 'h': 149 usage(); 150 /* NOTREACHED */ 151 case 'n': 152 configtest = 1; 153 break; 154 case 'r': 155 datadir = optarg; 156 break; 157 case 's': 158 csockpath = optarg; 159 break; 160 case 'v': 161 verbose++; 162 break; 163 case 'E': 164 eflag = 1; 165 break; 166 default: 167 usage(); 168 /* NOTREACHED */ 169 } 170 } 171 172 argc -= optind; 173 if (argc > 0) 174 usage(); 175 176 /* check for root privileges */ 177 if (geteuid()) 178 errx(1, "need root privileges"); 179 180 /* check for ldapd user */ 181 if (getpwnam(LDAPD_USER) == NULL) 182 errx(1, "unknown user %s", LDAPD_USER); 183 184 log_setverbose(verbose); 185 stats.started_at = time(0); 186 187 if (parse_config(conffile) != 0) 188 exit(2); 189 190 if (configtest) { 191 fprintf(stderr, "configuration ok\n"); 192 exit(0); 193 } 194 195 log_init(debug, LOG_DAEMON); 196 197 if (eflag) 198 ldape(debug, verbose, csockpath); 199 200 if (stat(datadir, &sb) == -1) 201 err(1, "%s", datadir); 202 if (!S_ISDIR(sb.st_mode)) 203 errx(1, "%s is not a directory", datadir); 204 205 if (!debug) { 206 if (daemon(1, 0) == -1) 207 err(1, "failed to daemonize"); 208 } 209 210 log_info("startup"); 211 212 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 213 PF_UNSPEC, pipe_parent2ldap) != 0) 214 fatal("socketpair"); 215 216 ldape_pid = start_child(PROC_LDAP_SERVER, saved_argv0, 217 pipe_parent2ldap[1], debug, verbose, csockpath, conffile); 218 219 ldap_loginit("auth", debug, verbose); 220 setproctitle("auth"); 221 event_init(); 222 223 signal_set(&ev_sigint, SIGINT, ldapd_sig_handler, NULL); 224 signal_set(&ev_sigterm, SIGTERM, ldapd_sig_handler, NULL); 225 signal_set(&ev_sigchld, SIGCHLD, ldapd_sigchld_handler, NULL); 226 signal_set(&ev_sighup, SIGHUP, ldapd_sig_handler, NULL); 227 signal_add(&ev_sigint, NULL); 228 signal_add(&ev_sigterm, NULL); 229 signal_add(&ev_sigchld, NULL); 230 signal_add(&ev_sighup, NULL); 231 signal(SIGPIPE, SIG_IGN); 232 233 if ((iev_ldape = calloc(1, sizeof(struct imsgev))) == NULL) 234 fatal("calloc"); 235 imsgev_init(iev_ldape, pipe_parent2ldap[0], NULL, ldapd_imsgev, 236 ldapd_needfd); 237 238 if (unveil(_PATH_NOLOGIN, "r") == -1) 239 err(1, "unveil %s", _PATH_NOLOGIN); 240 if (unveil(_PATH_LOGIN_CONF, "r") == -1) 241 err(1, "unveil %s", _PATH_LOGIN_CONF); 242 if (unveil(_PATH_LOGIN_CONF ".db", "r") == -1) 243 err(1, "unveil %s.db", _PATH_LOGIN_CONF); 244 if (unveil(_PATH_LOGIN_CONF_D, "r") == -1) 245 err(1, "unveil %s", _PATH_LOGIN_CONF_D); 246 if (unveil(_PATH_AUTHPROGDIR, "x") == -1) 247 err(1, "unveil %s", _PATH_AUTHPROGDIR); 248 if (unveil(datadir, "rwc") == -1) 249 err(1, "unveil %s", datadir); 250 if (unveil(NULL, NULL) == -1) 251 err(1, "unveil"); 252 253 if (pledge("stdio rpath wpath cpath getpw sendfd proc exec", 254 NULL) == -1) 255 err(1, "pledge"); 256 257 event_dispatch(); 258 259 log_debug("ldapd: exiting"); 260 261 return 0; 262} 263 264static void 265ldapd_imsgev(struct imsgev *iev, int code, struct imsg *imsg) 266{ 267 switch (code) { 268 case IMSGEV_IMSG: 269 log_debug("%s: got imsg %d on fd %d", 270 __func__, imsg->hdr.type, iev->ibuf.fd); 271 switch (imsg->hdr.type) { 272 case IMSG_LDAPD_AUTH: 273 ldapd_auth_request(iev, imsg); 274 break; 275 case IMSG_CTL_LOG_VERBOSE: 276 ldapd_log_verbose(imsg); 277 break; 278 case IMSG_LDAPD_OPEN: 279 ldapd_open_request(iev, imsg); 280 break; 281 default: 282 log_debug("%s: unexpected imsg %d", 283 __func__, imsg->hdr.type); 284 break; 285 } 286 break; 287 case IMSGEV_EREAD: 288 case IMSGEV_EWRITE: 289 case IMSGEV_EIMSG: 290 fatal("imsgev read/write error"); 291 break; 292 case IMSGEV_DONE: 293 event_loopexit(NULL); 294 break; 295 } 296} 297 298static void 299ldapd_needfd(struct imsgev *iev) 300{ 301 fatal("should never need an fd for parent messages"); 302} 303 304static int 305ldapd_auth_classful(char *name, char *password) 306{ 307 login_cap_t *lc = NULL; 308 char *class = NULL, *style = NULL; 309 auth_session_t *as; 310 311 if ((class = strchr(name, '#')) == NULL) { 312 log_debug("regular auth"); 313 return auth_userokay(name, NULL, "auth-ldap", password); 314 } 315 *class++ = '\0'; 316 317 if ((lc = login_getclass(class)) == NULL) { 318 log_debug("login_getclass(%s) for [%s] failed", class, name); 319 return 0; 320 } 321 if ((style = login_getstyle(lc, style, "auth-ldap")) == NULL) { 322 log_debug("login_getstyle() for [%s] failed", name); 323 login_close(lc); 324 return 0; 325 } 326 if (password) { 327 if ((as = auth_open()) == NULL) { 328 login_close(lc); 329 return 0; 330 } 331 auth_setitem(as, AUTHV_SERVICE, "response"); 332 auth_setdata(as, "", 1); 333 auth_setdata(as, password, strlen(password) + 1); 334 explicit_bzero(password, strlen(password)); 335 } else 336 as = NULL; 337 338 as = auth_verify(as, style, name, lc->lc_class, (char *)NULL); 339 login_close(lc); 340 return (as != NULL ? auth_close(as) : 0); 341} 342 343static void 344ldapd_auth_request(struct imsgev *iev, struct imsg *imsg) 345{ 346 struct auth_req *areq = imsg->data; 347 struct auth_res ares; 348 349 if (imsg->hdr.len != sizeof(*areq) + IMSG_HEADER_SIZE) 350 fatal("invalid size of auth request"); 351 352 /* make sure name and password are null-terminated */ 353 areq->name[sizeof(areq->name) - 1] = '\0'; 354 areq->password[sizeof(areq->password) - 1] = '\0'; 355 356 log_debug("authenticating [%s]", areq->name); 357 ares.ok = ldapd_auth_classful(areq->name, areq->password); 358 ares.fd = areq->fd; 359 ares.msgid = areq->msgid; 360 memset(areq, 0, sizeof(*areq)); 361 imsgev_compose(iev, IMSG_LDAPD_AUTH_RESULT, 0, 0, -1, &ares, 362 sizeof(ares)); 363} 364 365static void 366ldapd_log_verbose(struct imsg *imsg) 367{ 368 int verbose; 369 370 if (imsg->hdr.len != sizeof(verbose) + IMSG_HEADER_SIZE) 371 fatal("invalid size of log verbose request"); 372 373 bcopy(imsg->data, &verbose, sizeof(verbose)); 374 log_setverbose(verbose); 375} 376 377static void 378ldapd_open_request(struct imsgev *iev, struct imsg *imsg) 379{ 380 struct open_req *oreq = imsg->data; 381 int oflags, fd; 382 383 if (imsg->hdr.len != sizeof(*oreq) + IMSG_HEADER_SIZE) 384 fatal("invalid size of open request"); 385 386 if (oreq->path[PATH_MAX-1] != '\0') 387 fatal("bogus path"); 388 389 if (strncmp(oreq->path, datadir, strlen(datadir)) != 0) { 390 log_warnx("refusing to open file %s", oreq->path); 391 fatal("ldape sent invalid open request"); 392 } 393 394 if (oreq->rdonly) 395 oflags = O_RDONLY; 396 else 397 oflags = O_RDWR | O_CREAT | O_APPEND; 398 399 log_debug("opening [%s]", oreq->path); 400 fd = open(oreq->path, oflags | O_NOFOLLOW, 0600); 401 if (fd == -1) 402 log_warn("%s", oreq->path); 403 404 imsgev_compose(iev, IMSG_LDAPD_OPEN_RESULT, 0, 0, fd, oreq, 405 sizeof(*oreq)); 406} 407 408static pid_t 409start_child(enum ldapd_process p, char *argv0, int fd, int debug, 410 int verbose, char *csockpath, char *conffile) 411{ 412 char *argv[11]; 413 int argc = 0; 414 pid_t pid; 415 416 switch (pid = fork()) { 417 case -1: 418 fatal("cannot fork"); 419 case 0: 420 break; 421 default: 422 close(fd); 423 return (pid); 424 } 425 426 if (fd != PROC_PARENT_SOCK_FILENO) { 427 if (dup2(fd, PROC_PARENT_SOCK_FILENO) == -1) 428 fatal("cannot setup imsg fd"); 429 } else if (fcntl(fd, F_SETFD, 0) == -1) 430 fatal("cannot setup imsg fd"); 431 432 argv[argc++] = argv0; 433 switch (p) { 434 case PROC_MAIN_AUTH: 435 fatalx("Can not start main process"); 436 case PROC_LDAP_SERVER: 437 argv[argc++] = "-E"; 438 break; 439 } 440 if (debug) 441 argv[argc++] = "-d"; 442 if (verbose >= 3) 443 argv[argc++] = "-vvv"; 444 else if (verbose == 2) 445 argv[argc++] = "-vv"; 446 else if (verbose == 1) 447 argv[argc++] = "-v"; 448 if (csockpath) { 449 argv[argc++] = "-s"; 450 argv[argc++] = csockpath; 451 } 452 if (conffile) { 453 argv[argc++] = "-f"; 454 argv[argc++] = conffile; 455 } 456 if (datadir) { 457 argv[argc++] = "-r"; 458 argv[argc++] = datadir; 459 } 460 461 argv[argc++] = NULL; 462 463 execvp(argv0, argv); 464 fatal("execvp"); 465} 466