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