1 /* $OpenBSD: ldapd.c,v 1.10 2013/11/02 13:31:51 deraadt 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/types.h> 21 #include <sys/wait.h> 22 23 #include <assert.h> 24 #include <bsd_auth.h> 25 #include <ctype.h> 26 #include <err.h> 27 #include <errno.h> 28 #include <event.h> 29 #include <fcntl.h> 30 #include <login_cap.h> 31 #include <signal.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 #include <unistd.h> 37 38 #include "ldapd.h" 39 40 void usage(void); 41 void ldapd_sig_handler(int fd, short why, void *data); 42 void ldapd_sigchld_handler(int sig, short why, void *data); 43 static void ldapd_imsgev(struct imsgev *iev, int code, struct imsg *imsg); 44 static void ldapd_needfd(struct imsgev *iev); 45 static void ldapd_auth_request(struct imsgev *iev, struct imsg *imsg); 46 static void ldapd_open_request(struct imsgev *iev, struct imsg *imsg); 47 static void ldapd_log_verbose(struct imsg *imsg); 48 49 struct ldapd_stats stats; 50 pid_t ldape_pid; 51 52 void 53 usage(void) 54 { 55 extern char *__progname; 56 57 fprintf(stderr, "usage: %s [-dnv] [-D macro=value] " 58 "[-f file] [-s file]\n", __progname); 59 exit(1); 60 } 61 62 void 63 ldapd_sig_handler(int sig, short why, void *data) 64 { 65 log_info("ldapd: got signal %d", sig); 66 if (sig == SIGINT || sig == SIGTERM) 67 event_loopexit(NULL); 68 } 69 70 void 71 ldapd_sigchld_handler(int sig, short why, void *data) 72 { 73 pid_t pid; 74 int status; 75 76 while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) != 0) { 77 if (pid == -1) { 78 if (errno == EINTR) 79 continue; 80 if (errno != ECHILD) 81 log_warn("waitpid"); 82 break; 83 } 84 85 if (WIFEXITED(status)) 86 log_debug("child %d exited with status %d", 87 pid, WEXITSTATUS(status)); 88 else if (WIFSIGNALED(status)) 89 log_debug("child %d exited due to signal %d", 90 pid, WTERMSIG(status)); 91 else 92 log_debug("child %d terminated abnormally", pid); 93 94 if (pid == ldape_pid) { 95 log_info("ldapd: lost ldap server"); 96 event_loopexit(NULL); 97 break; 98 } 99 } 100 } 101 102 /* set socket non-blocking */ 103 void 104 fd_nonblock(int fd) 105 { 106 int flags = fcntl(fd, F_GETFL, 0); 107 int rc = fcntl(fd, F_SETFL, flags | O_NONBLOCK); 108 if (rc == -1) { 109 log_warn("failed to set fd %d non-blocking", fd); 110 } 111 } 112 113 114 int 115 main(int argc, char *argv[]) 116 { 117 int c; 118 int debug = 0, verbose = 0; 119 int configtest = 0, skip_chroot = 0; 120 int pipe_parent2ldap[2]; 121 char *conffile = CONFFILE; 122 char *csockpath = LDAPD_SOCKET; 123 struct passwd *pw = NULL; 124 struct imsgev *iev_ldape; 125 struct event ev_sigint; 126 struct event ev_sigterm; 127 struct event ev_sigchld; 128 struct event ev_sighup; 129 130 log_init(1); /* log to stderr until daemonized */ 131 132 while ((c = getopt(argc, argv, "dhvD:f:ns:")) != -1) { 133 switch (c) { 134 case 'd': 135 debug = 1; 136 break; 137 case 'D': 138 if (cmdline_symset(optarg) < 0) { 139 warnx("could not parse macro definition %s", 140 optarg); 141 } 142 break; 143 case 'f': 144 conffile = optarg; 145 break; 146 case 'h': 147 usage(); 148 /* NOTREACHED */ 149 case 'n': 150 configtest = 1; 151 break; 152 case 's': 153 csockpath = optarg; 154 break; 155 case 'v': 156 verbose++; 157 break; 158 default: 159 usage(); 160 /* NOTREACHED */ 161 } 162 } 163 164 argc -= optind; 165 if (argc > 0) 166 usage(); 167 168 log_verbose(verbose); 169 stats.started_at = time(0); 170 ssl_init(); 171 172 if (parse_config(conffile) != 0) 173 exit(2); 174 175 if (configtest) { 176 fprintf(stderr, "configuration ok\n"); 177 exit(0); 178 } 179 180 if (geteuid()) { 181 if (!debug) 182 errx(1, "need root privileges"); 183 skip_chroot = 1; 184 } 185 186 if (!skip_chroot && (pw = getpwnam(LDAPD_USER)) == NULL) 187 err(1, "%s", LDAPD_USER); 188 189 if (!debug) { 190 if (daemon(1, 0) == -1) 191 err(1, "failed to daemonize"); 192 } 193 194 log_init(debug); 195 log_info("startup"); 196 197 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pipe_parent2ldap) != 0) 198 fatal("socketpair"); 199 200 fd_nonblock(pipe_parent2ldap[0]); 201 fd_nonblock(pipe_parent2ldap[1]); 202 203 ldape_pid = ldape(pw, csockpath, pipe_parent2ldap); 204 205 setproctitle("auth"); 206 event_init(); 207 208 signal_set(&ev_sigint, SIGINT, ldapd_sig_handler, NULL); 209 signal_set(&ev_sigterm, SIGTERM, ldapd_sig_handler, NULL); 210 signal_set(&ev_sigchld, SIGCHLD, ldapd_sigchld_handler, NULL); 211 signal_set(&ev_sighup, SIGHUP, ldapd_sig_handler, NULL); 212 signal_add(&ev_sigint, NULL); 213 signal_add(&ev_sigterm, NULL); 214 signal_add(&ev_sigchld, NULL); 215 signal_add(&ev_sighup, NULL); 216 signal(SIGPIPE, SIG_IGN); 217 218 close(pipe_parent2ldap[1]); 219 220 if ((iev_ldape = calloc(1, sizeof(struct imsgev))) == NULL) 221 fatal("calloc"); 222 imsgev_init(iev_ldape, pipe_parent2ldap[0], NULL, ldapd_imsgev, 223 ldapd_needfd); 224 225 event_dispatch(); 226 log_debug("ldapd: exiting"); 227 228 return 0; 229 } 230 231 static void 232 ldapd_imsgev(struct imsgev *iev, int code, struct imsg *imsg) 233 { 234 switch (code) { 235 case IMSGEV_IMSG: 236 log_debug("%s: got imsg %d on fd %d", 237 __func__, imsg->hdr.type, iev->ibuf.fd); 238 switch (imsg->hdr.type) { 239 case IMSG_LDAPD_AUTH: 240 ldapd_auth_request(iev, imsg); 241 break; 242 case IMSG_CTL_LOG_VERBOSE: 243 ldapd_log_verbose(imsg); 244 break; 245 case IMSG_LDAPD_OPEN: 246 ldapd_open_request(iev, imsg); 247 break; 248 default: 249 log_debug("%s: unexpected imsg %d", 250 __func__, imsg->hdr.type); 251 break; 252 } 253 break; 254 case IMSGEV_EREAD: 255 case IMSGEV_EWRITE: 256 case IMSGEV_EIMSG: 257 fatal("imsgev read/write error"); 258 break; 259 case IMSGEV_DONE: 260 event_loopexit(NULL); 261 break; 262 } 263 } 264 265 static void 266 ldapd_needfd(struct imsgev *iev) 267 { 268 fatal("should never need an fd for parent messages"); 269 } 270 271 static int 272 ldapd_auth_classful(char *name, char *password) 273 { 274 login_cap_t *lc = NULL; 275 char *class = NULL, *style = NULL; 276 auth_session_t *as; 277 278 if ((class = strchr(name, '#')) == NULL) { 279 log_debug("regular auth"); 280 return auth_userokay(name, NULL, "auth-ldap", password); 281 } 282 *class++ = '\0'; 283 284 if ((lc = login_getclass(class)) == NULL) { 285 log_debug("login_getclass(%s) for [%s] failed", class, name); 286 return 0; 287 } 288 if ((style = login_getstyle(lc, style, "auth-ldap")) == NULL) { 289 log_debug("login_getstyle() for [%s] failed", name); 290 login_close(lc); 291 return 0; 292 } 293 if (password) { 294 if ((as = auth_open()) == NULL) { 295 login_close(lc); 296 return 0; 297 } 298 auth_setitem(as, AUTHV_SERVICE, "response"); 299 auth_setdata(as, "", 1); 300 auth_setdata(as, password, strlen(password) + 1); 301 memset(password, 0, strlen(password)); 302 } else 303 as = NULL; 304 305 as = auth_verify(as, style, name, lc->lc_class, (char *)NULL); 306 login_close(lc); 307 return (as != NULL ? auth_close(as) : 0); 308 } 309 310 static void 311 ldapd_auth_request(struct imsgev *iev, struct imsg *imsg) 312 { 313 struct auth_req *areq = imsg->data; 314 struct auth_res ares; 315 316 if (imsg->hdr.len != sizeof(*areq) + IMSG_HEADER_SIZE) 317 fatal("invalid size of auth request"); 318 319 /* make sure name and password are null-terminated */ 320 areq->name[sizeof(areq->name) - 1] = '\0'; 321 areq->password[sizeof(areq->password) - 1] = '\0'; 322 323 log_debug("authenticating [%s]", areq->name); 324 ares.ok = ldapd_auth_classful(areq->name, areq->password); 325 ares.fd = areq->fd; 326 ares.msgid = areq->msgid; 327 bzero(areq, sizeof(*areq)); 328 imsgev_compose(iev, IMSG_LDAPD_AUTH_RESULT, 0, 0, -1, &ares, 329 sizeof(ares)); 330 } 331 332 static void 333 ldapd_log_verbose(struct imsg *imsg) 334 { 335 int verbose; 336 337 if (imsg->hdr.len != sizeof(verbose) + IMSG_HEADER_SIZE) 338 fatal("invalid size of log verbose request"); 339 340 bcopy(imsg->data, &verbose, sizeof(verbose)); 341 log_verbose(verbose); 342 } 343 344 static void 345 ldapd_open_request(struct imsgev *iev, struct imsg *imsg) 346 { 347 struct open_req *oreq = imsg->data; 348 int oflags, fd; 349 350 if (imsg->hdr.len != sizeof(*oreq) + IMSG_HEADER_SIZE) 351 fatal("invalid size of open request"); 352 353 /* make sure path is null-terminated */ 354 oreq->path[MAXPATHLEN] = '\0'; 355 356 if (strncmp(oreq->path, DATADIR, strlen(DATADIR)) != 0) { 357 log_warnx("refusing to open file %s", oreq->path); 358 fatal("ldape sent invalid open request"); 359 } 360 361 if (oreq->rdonly) 362 oflags = O_RDONLY; 363 else 364 oflags = O_RDWR | O_CREAT | O_APPEND; 365 366 log_debug("opening [%s]", oreq->path); 367 fd = open(oreq->path, oflags | O_NOFOLLOW, 0600); 368 if (fd == -1) 369 log_warn("%s", oreq->path); 370 371 imsgev_compose(iev, IMSG_LDAPD_OPEN_RESULT, 0, 0, fd, oreq, 372 sizeof(*oreq)); 373 } 374 375