ldape.c revision 1.8
1/* $OpenBSD: ldape.c,v 1.8 2010/06/29 21:54:38 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/socket.h> 22#include <sys/stat.h> 23#include <sys/un.h> 24#include <sys/wait.h> 25 26#include <err.h> 27#include <errno.h> 28#include <signal.h> 29#include <stdlib.h> 30#include <string.h> 31#include <unistd.h> 32 33#include "ldapd.h" 34 35void ldape_sig_handler(int fd, short why, void *data); 36void ldape_dispatch_ldapd(int fd, short event, void *ptr); 37static void ldape_auth_result(struct imsg *imsg); 38static void ldape_open_result(struct imsg *imsg); 39 40int ldap_starttls(struct request *req); 41void send_ldap_extended_response(struct conn *conn, 42 int msgid, unsigned long type, 43 long long result_code, 44 const char *extended_oid); 45 46struct imsgev *iev_ldapd; 47struct control_sock csock; 48 49void 50ldape_sig_handler(int sig, short why, void *data) 51{ 52 log_debug("ldape: got signal %d", sig); 53 if (sig == SIGCHLD) { 54 for (;;) { 55 pid_t pid; 56 int status; 57 58 pid = waitpid(WAIT_ANY, &status, WNOHANG); 59 if (pid <= 0) 60 break; 61 } 62 return; 63 } 64 65 event_loopexit(NULL); 66} 67 68void 69send_ldap_extended_response(struct conn *conn, int msgid, unsigned long type, 70 long long result_code, const char *extended_oid) 71{ 72 int rc; 73 struct ber_element *root, *elm; 74 void *buf; 75 76 log_debug("sending response %u with result %lld", type, result_code); 77 78 if ((root = ber_add_sequence(NULL)) == NULL) 79 goto fail; 80 81 elm = ber_printf_elements(root, "d{tEss", 82 msgid, BER_CLASS_APP, type, result_code, "", ""); 83 if (elm == NULL) 84 goto fail; 85 86 if (extended_oid) 87 elm = ber_add_string(elm, extended_oid); 88 89 rc = ber_write_elements(&conn->ber, root); 90 ber_free_elements(root); 91 92 if (rc < 0) 93 log_warn("failed to create ldap result"); 94 else { 95 ber_get_writebuf(&conn->ber, &buf); 96 if (bufferevent_write(conn->bev, buf, rc) != 0) 97 log_warn("failed to send ldap result"); 98 } 99 100 return; 101fail: 102 if (root) 103 ber_free_elements(root); 104} 105 106int 107ldap_refer(struct request *req, const char *basedn, struct search *search, 108 struct referrals *refs) 109{ 110 struct ber_element *root, *elm, *ref_root = NULL; 111 struct referral *ref; 112 long long result_code = LDAP_REFERRAL; 113 unsigned long type; 114 int rc; 115 void *buf; 116 char *url, *scope_str = NULL; 117 118 if (req->type == LDAP_REQ_SEARCH) 119 type = LDAP_RES_SEARCH_RESULT; 120 else 121 type = req->type + 1; 122 123 if (search != NULL) { 124 if (search->scope != LDAP_SCOPE_SUBTREE) 125 scope_str = "base"; 126 else 127 scope_str = "sub"; 128 } 129 130 log_debug("sending referral in response %u on msgid %i", type, req->msgid); 131 132 if ((root = ber_add_sequence(NULL)) == NULL) 133 goto fail; 134 135 if ((elm = ref_root = ber_add_sequence(NULL)) == NULL) 136 goto fail; 137 ber_set_header(ref_root, BER_CLASS_CONTEXT, LDAP_REQ_SEARCH); 138 SLIST_FOREACH(ref, refs, next) { 139 if (search != NULL) 140 asprintf(&url, "%s/%s??%s", ref->url, basedn, 141 scope_str); 142 else 143 asprintf(&url, "%s/%s", ref->url, basedn); 144 if (url == NULL) { 145 log_warn("asprintf"); 146 goto fail; 147 } 148 log_debug("adding referral '%s'", url); 149 elm = ber_add_string(elm, url); 150 free(url); 151 if (elm == NULL) 152 goto fail; 153 } 154 155 elm = ber_printf_elements(root, "d{tEsse", 156 req->msgid, BER_CLASS_APP, type, result_code, "", "", ref_root); 157 if (elm == NULL) 158 goto fail; 159 ref_root = NULL; 160 161 rc = ber_write_elements(&req->conn->ber, root); 162 ber_free_elements(root); 163 164 if (rc < 0) 165 log_warn("failed to create ldap result"); 166 else { 167 ber_get_writebuf(&req->conn->ber, &buf); 168 if (bufferevent_write(req->conn->bev, buf, rc) != 0) 169 log_warn("failed to send ldap result"); 170 } 171 172 request_free(req); 173 return LDAP_REFERRAL; 174 175fail: 176 if (root != NULL) 177 ber_free_elements(root); 178 if (ref_root != NULL) 179 ber_free_elements(ref_root); 180 request_free(req); 181 return LDAP_REFERRAL; 182} 183 184void 185send_ldap_result(struct conn *conn, int msgid, unsigned long type, 186 long long result_code) 187{ 188 send_ldap_extended_response(conn, msgid, type, result_code, NULL); 189} 190 191int 192ldap_respond(struct request *req, int code) 193{ 194 if (code >= 0) 195 send_ldap_result(req->conn, req->msgid, req->type + 1, code); 196 request_free(req); 197 return code; 198} 199 200int 201ldap_abandon(struct request *req) 202{ 203 long long msgid; 204 struct search *search; 205 206 if (ber_scanf_elements(req->op, "i", &msgid) != 0) { 207 request_free(req); 208 return -1; /* protocol error, but don't respond */ 209 } 210 211 TAILQ_FOREACH(search, &req->conn->searches, next) { 212 if (search->req->msgid == msgid) { 213 /* unlinks the search from conn->searches */ 214 search_close(search); 215 break; 216 } 217 } 218 request_free(req); 219 return -1; 220} 221 222int 223ldap_unbind(struct request *req) 224{ 225 log_debug("current bind dn = %s", req->conn->binddn); 226 conn_disconnect(req->conn); 227 request_free(req); 228 return -1; /* don't send any response */ 229} 230 231int 232ldap_starttls(struct request *req) 233{ 234 if ((req->conn->listener->flags & F_STARTTLS) == 0) { 235 log_debug("StartTLS not configured for this connection"); 236 return LDAP_OPERATIONS_ERROR; 237 } 238 239 req->conn->s_flags |= F_STARTTLS; 240 return LDAP_SUCCESS; 241} 242 243int 244ldap_extended(struct request *req) 245{ 246 int i, rc = LDAP_PROTOCOL_ERROR; 247 char *oid = NULL; 248 struct ber_element *ext_val = NULL; 249 struct { 250 const char *oid; 251 int (*fn)(struct request *); 252 } extended_ops[] = { 253 { "1.3.6.1.4.1.1466.20037", ldap_starttls }, 254 { NULL } 255 }; 256 257 if (ber_scanf_elements(req->op, "{se", &oid, &ext_val) != 0) 258 goto done; 259 260 log_debug("got extended operation %s", oid); 261 req->op = ext_val; 262 263 for (i = 0; extended_ops[i].oid != NULL; i++) { 264 if (strcmp(oid, extended_ops[i].oid) == 0) { 265 rc = extended_ops[i].fn(req); 266 break; 267 } 268 } 269 270 if (extended_ops[i].fn == NULL) 271 log_warnx("unimplemented extended operation %s", oid); 272 273done: 274 send_ldap_extended_response(req->conn, req->msgid, LDAP_RES_EXTENDED, 275 rc, oid); 276 277 request_free(req); 278 return 0; 279} 280 281pid_t 282ldape(struct passwd *pw, char *csockpath, int pipe_parent2ldap[2]) 283{ 284 int on = 1; 285 pid_t pid; 286 struct namespace *ns; 287 struct listener *l; 288 struct sockaddr_un *sun = NULL; 289 struct event ev_sigint; 290 struct event ev_sigterm; 291 struct event ev_sigchld; 292 struct event ev_sighup; 293 char host[128]; 294 295 TAILQ_INIT(&conn_list); 296 297 pid = fork(); 298 if (pid < 0) 299 fatal("ldape: fork"); 300 if (pid > 0) 301 return pid; 302 303 setproctitle("ldap server"); 304 event_init(); 305 306 signal_set(&ev_sigint, SIGINT, ldape_sig_handler, NULL); 307 signal_set(&ev_sigterm, SIGTERM, ldape_sig_handler, NULL); 308 signal_set(&ev_sigchld, SIGCHLD, ldape_sig_handler, NULL); 309 signal_set(&ev_sighup, SIGHUP, ldape_sig_handler, NULL); 310 signal_add(&ev_sigint, NULL); 311 signal_add(&ev_sigterm, NULL); 312 signal_add(&ev_sigchld, NULL); 313 signal_add(&ev_sighup, NULL); 314 signal(SIGPIPE, SIG_IGN); 315 316 close(pipe_parent2ldap[0]); 317 318 /* Initialize parent imsg events. */ 319 if ((iev_ldapd = calloc(1, sizeof(struct imsgev))) == NULL) 320 fatal("calloc"); 321 imsg_init(&iev_ldapd->ibuf, pipe_parent2ldap[1]); 322 iev_ldapd->handler = ldape_dispatch_ldapd; 323 imsg_event_add(iev_ldapd); 324 325 /* Initialize control socket. */ 326 bzero(&csock, sizeof(csock)); 327 csock.cs_name = csockpath; 328 control_init(&csock); 329 control_listen(&csock); 330 TAILQ_INIT(&ctl_conns); 331 332 /* Initialize LDAP listeners. 333 */ 334 TAILQ_FOREACH(l, &conf->listeners, entry) { 335 l->fd = socket(l->ss.ss_family, SOCK_STREAM, 0); 336 if (l->fd < 0) 337 fatal("ldape: socket"); 338 339 setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); 340 341 if (l->ss.ss_family == AF_UNIX) { 342 sun = (struct sockaddr_un *)&l->ss; 343 log_info("listening on %s", sun->sun_path); 344 if (unlink(sun->sun_path) == -1 && errno != ENOENT) 345 fatal("ldape: unlink"); 346 } else { 347 print_host(&l->ss, host, sizeof(host)); 348 log_info("listening on %s:%d", host, ntohs(l->port)); 349 } 350 351 if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) != 0) 352 fatal("ldape: bind"); 353 if (listen(l->fd, 20) != 0) 354 fatal("ldape: listen"); 355 356 if (l->ss.ss_family == AF_UNIX) { 357 mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; 358 if (chmod(sun->sun_path, mode) == -1) { 359 unlink(sun->sun_path); 360 fatal("ldape: chmod"); 361 } 362 } 363 364 fd_nonblock(l->fd); 365 366 event_set(&l->ev, l->fd, EV_READ|EV_PERSIST, conn_accept, l); 367 event_add(&l->ev, NULL); 368 369 ssl_setup(conf, l); 370 } 371 372 TAILQ_FOREACH(ns, &conf->namespaces, next) { 373 if (!namespace_has_referrals(ns) && namespace_open(ns) != 0) 374 fatal(ns->suffix); 375 } 376 377 if (pw != NULL) { 378 if (chroot(pw->pw_dir) == -1) 379 fatal("chroot"); 380 if (chdir("/") == -1) 381 fatal("chdir(\"/\")"); 382 383 if (setgroups(1, &pw->pw_gid) || 384 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 385 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 386 fatal("cannot drop privileges"); 387 } 388 389 log_debug("ldape: entering event loop"); 390 event_dispatch(); 391 392 while ((ns = TAILQ_FIRST(&conf->namespaces)) != NULL) 393 namespace_remove(ns); 394 395 control_cleanup(&csock); 396 397 log_info("ldape: exiting"); 398 _exit(0); 399} 400 401void 402ldape_dispatch_ldapd(int fd, short event, void *ptr) 403{ 404 struct imsgev *iev = ptr; 405 struct imsgbuf *ibuf; 406 struct imsg imsg; 407 ssize_t n; 408 409 if (imsg_event_handle(iev, event) != 0) 410 return; 411 412 ibuf = &iev->ibuf; 413 for (;;) { 414 if ((n = imsg_get(ibuf, &imsg)) == -1) 415 fatal("ldape_dispatch_ldapd: imsg_read error"); 416 if (n == 0) 417 break; 418 419 switch (imsg.hdr.type) { 420 case IMSG_LDAPD_AUTH_RESULT: 421 ldape_auth_result(&imsg); 422 break; 423 case IMSG_LDAPD_OPEN_RESULT: 424 ldape_open_result(&imsg); 425 break; 426 default: 427 log_debug("ldape_dispatch_ldapd: unexpected imsg %d", 428 imsg.hdr.type); 429 break; 430 } 431 imsg_free(&imsg); 432 } 433 imsg_event_add(iev); 434} 435 436static void 437ldape_auth_result(struct imsg *imsg) 438{ 439 struct conn *conn; 440 struct auth_res *ares = imsg->data; 441 442 log_debug("authentication on conn %i/%lld = %d", ares->fd, ares->msgid, 443 ares->ok); 444 conn = conn_by_fd(ares->fd); 445 if (conn->bind_req != NULL && conn->bind_req->msgid == ares->msgid) 446 ldap_bind_continue(conn, ares->ok); 447 else 448 log_warnx("spurious auth result"); 449} 450 451static void 452ldape_open_result(struct imsg *imsg) 453{ 454 struct namespace *ns; 455 struct open_req *oreq = imsg->data; 456 457 if (imsg->hdr.len != sizeof(*oreq) + IMSG_HEADER_SIZE) 458 fatal("invalid size of open result"); 459 460 /* make sure path is null-terminated */ 461 oreq->path[MAXPATHLEN] = '\0'; 462 463 log_debug("open(%s) returned fd %i", oreq->path, imsg->fd); 464 465 TAILQ_FOREACH(ns, &conf->namespaces, next) { 466 if (strcmp(oreq->path, ns->data_path) == 0) { 467 namespace_set_data_fd(ns, imsg->fd); 468 break; 469 } 470 if (strcmp(oreq->path, ns->indx_path) == 0) { 471 namespace_set_indx_fd(ns, imsg->fd); 472 break; 473 } 474 } 475 476 if (ns == NULL) { 477 log_warnx("spurious open result"); 478 close(imsg->fd); 479 } else 480 namespace_queue_schedule(ns); 481} 482 483