1 /* $OpenBSD: ldapclient.c,v 1.2 2008/07/02 17:36:15 pyr Exp $ */ 2 3 /* 4 * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 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/types.h> 20 #include <sys/param.h> 21 #include <sys/queue.h> 22 #include <sys/socket.h> 23 #include <sys/tree.h> 24 25 #include <netinet/in.h> 26 #include <arpa/inet.h> 27 28 #include <errno.h> 29 #include <event.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 #include <pwd.h> 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 37 #define LDAP_DEPRECATED 1 38 #include <ldap.h> 39 40 #include "ypldap.h" 41 42 void client_sig_handler(int, short, void *); 43 void client_dispatch_parent(int, short, void *); 44 void client_shutdown(void); 45 void client_connect(int, short, void *); 46 void client_configure(struct env *); 47 void client_configure_wrapper(int, short, void *); 48 int client_try_idm(struct env *, struct idm *); 49 void client_try_idm_wrapper(int, short, void *); 50 void client_try_server_wrapper(int, short, void *); 51 52 void 53 client_sig_handler(int sig, short event, void *p) 54 { 55 switch (sig) { 56 case SIGINT: 57 case SIGTERM: 58 client_shutdown(); 59 break; 60 default: 61 fatalx("unexpected signal"); 62 } 63 } 64 65 void 66 client_dispatch_parent(int fd, short event, void *p) 67 { 68 int n; 69 int shut = 0; 70 struct imsg imsg; 71 struct env *env = p; 72 struct imsgbuf *ibuf = env->sc_ibuf; 73 74 75 switch (event) { 76 case EV_READ: 77 if ((n = imsg_read(ibuf)) == -1) 78 fatal("imsg_read error"); 79 if (n == 0) 80 shut = 1; 81 break; 82 case EV_WRITE: 83 if (msgbuf_write(&ibuf->w) == -1) 84 fatal("msgbuf_write"); 85 imsg_event_add(ibuf); 86 return; 87 default: 88 fatalx("unknown event"); 89 } 90 91 for (;;) { 92 if ((n = imsg_get(ibuf, &imsg)) == -1) 93 fatal("client_dispatch_parent: imsg_read_error"); 94 if (n == 0) 95 break; 96 97 switch (imsg.hdr.type) { 98 case IMSG_CONF_START: { 99 struct env params; 100 101 if (env->sc_flags & F_CONFIGURING) { 102 log_warnx("configuration already in progress"); 103 break; 104 } 105 memcpy(¶ms, imsg.data, sizeof(params)); 106 log_debug("configuration starting"); 107 env->sc_flags |= F_CONFIGURING; 108 purge_config(env); 109 memcpy(&env->sc_conf_tv, ¶ms.sc_conf_tv, 110 sizeof(env->sc_conf_tv)); 111 env->sc_flags |= params.sc_flags; 112 break; 113 } 114 case IMSG_CONF_IDM: { 115 struct idm *idm; 116 117 if (!(env->sc_flags & F_CONFIGURING)) 118 break; 119 if ((idm = calloc(1, sizeof(*idm))) == NULL) 120 fatal(NULL); 121 memcpy(idm, imsg.data, sizeof(*idm)); 122 idm->idm_env = env; 123 TAILQ_INSERT_TAIL(&env->sc_idms, idm, idm_entry); 124 break; 125 } 126 case IMSG_CONF_END: 127 env->sc_flags &= ~F_CONFIGURING; 128 log_debug("applying configuration"); 129 client_configure(env); 130 break; 131 default: 132 log_debug("client_dispatch_parent: unexpect imsg %d", 133 imsg.hdr.type); 134 135 break; 136 } 137 imsg_free(&imsg); 138 } 139 if (!shut) 140 imsg_event_add(ibuf); 141 else { 142 /* this pipe is dead, so remove the event handler */ 143 event_del(&ibuf->ev); 144 event_loopexit(NULL); 145 } 146 } 147 148 void 149 client_shutdown(void) 150 { 151 log_info("ldap client exiting"); 152 _exit(0); 153 } 154 155 pid_t 156 ldapclient(int pipe_main2client[2]) 157 { 158 pid_t pid; 159 struct passwd *pw; 160 struct event ev_sigint; 161 struct event ev_sigterm; 162 struct env env; 163 164 switch (pid = fork()) { 165 case -1: 166 fatal("cannot fork"); 167 break; 168 case 0: 169 break; 170 default: 171 return (pid); 172 } 173 174 bzero(&env, sizeof(env)); 175 TAILQ_INIT(&env.sc_idms); 176 177 if ((pw = getpwnam(YPLDAP_USER)) == NULL) 178 fatal("getpwnam"); 179 180 #ifndef DEBUG 181 if (chroot(pw->pw_dir) == -1) 182 fatal("chroot"); 183 if (chdir("/") == -1) 184 fatal("chdir"); 185 #else 186 #warning disabling chrooting in DEBUG mode 187 #endif 188 setproctitle("ldap client"); 189 ypldap_process = PROC_CLIENT; 190 191 #ifndef DEBUG 192 if (setgroups(1, &pw->pw_gid) || 193 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 194 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 195 fatal("cannot drop privileges"); 196 #else 197 #warning disabling privilege revocation in DEBUG mode 198 #endif 199 200 event_init(); 201 signal_set(&ev_sigint, SIGINT, client_sig_handler, NULL); 202 signal_set(&ev_sigterm, SIGTERM, client_sig_handler, NULL); 203 signal_add(&ev_sigint, NULL); 204 signal_add(&ev_sigterm, NULL); 205 206 close(pipe_main2client[0]); 207 if ((env.sc_ibuf = calloc(1, sizeof(*env.sc_ibuf))) == NULL) 208 fatal(NULL); 209 210 env.sc_ibuf->events = EV_READ; 211 env.sc_ibuf->data = &env; 212 imsg_init(env.sc_ibuf, pipe_main2client[1], client_dispatch_parent); 213 event_set(&env.sc_ibuf->ev, env.sc_ibuf->fd, env.sc_ibuf->events, 214 env.sc_ibuf->handler, &env); 215 event_add(&env.sc_ibuf->ev, NULL); 216 217 event_dispatch(); 218 client_shutdown(); 219 220 return (0); 221 222 } 223 224 void 225 client_configure_wrapper(int fd, short event, void *p) 226 { 227 struct env *env = p; 228 229 client_configure(env); 230 } 231 232 int 233 client_try_idm(struct env *env, struct idm *idm) 234 { 235 int i; 236 int j; 237 int msgid; 238 size_t n; 239 LDAP *ld; 240 LDAPMessage *lm; 241 char **ldap_attrs; 242 char *attrs[ATTR_MAX+1]; 243 const char *where; 244 struct idm_req ir; 245 246 log_debug("trying directory: %s", idm->idm_name); 247 248 bzero(&ir, sizeof(ir)); 249 imsg_compose(env->sc_ibuf, IMSG_START_UPDATE, 0, 0, &ir, sizeof(ir)); 250 251 where = "ldap_open"; 252 if ((ld = ldap_open(idm->idm_name, LDAP_PORT)) == NULL) 253 goto bad; 254 255 where = "ldap_bind"; 256 if (ldap_bind(ld, idm->idm_binddn, 257 idm->idm_bindcred, LDAP_AUTH_SIMPLE) < 0) 258 goto bad; 259 260 bzero(attrs, sizeof(attrs)); 261 for (i = 0, j = 0; i < ATTR_MAX; i++) { 262 if (idm->idm_flags & F_FIXED_ATTR(i)) 263 continue; 264 attrs[j++] = idm->idm_attrs[i]; 265 } 266 attrs[j] = NULL; 267 268 where = "ldap_search"; 269 if ((msgid = ldap_search(ld, idm->idm_binddn, LDAP_SCOPE_SUBTREE, 270 idm->idm_filters[FILTER_USER], attrs, 0)) < 0) 271 goto bad; 272 273 where = "ldap_result"; 274 if (ldap_result(ld, msgid, 1, NULL, &lm) == -1) 275 goto bad; 276 277 where = "ldap_result_message"; 278 if (lm == NULL) 279 goto bad; 280 where = "ldap_first_message"; 281 if ((lm = ldap_first_message(ld, lm)) == NULL) 282 goto bad; 283 284 /* 285 * build password line. 286 */ 287 n = ldap_count_entries(ld, lm); 288 while (n-- > 0) { 289 bzero(&ir, sizeof(ir)); 290 for (i = 0, j = 0; i < ATTR_MAX; i++) { 291 if (idm->idm_flags & F_FIXED_ATTR(i)) { 292 if (strlcat(ir.ir_line, idm->idm_attrs[i], 293 sizeof(ir.ir_line)) >= sizeof(ir.ir_line)) 294 /* 295 * entry yields a line > 1024, trash it. 296 */ 297 goto next_entry; 298 if (i == ATTR_UID) { 299 ir.ir_key.ik_uid = strtonum( 300 idm->idm_attrs[i], 0, 301 UID_MAX, NULL); 302 } 303 } else { 304 ldap_attrs = (char **)ldap_get_values(ld, 305 lm, attrs[j++]); 306 if (ldap_attrs == NULL) 307 goto next_entry; 308 if (strlcat(ir.ir_line, ldap_attrs[0], 309 sizeof(ir.ir_line)) >= sizeof(ir.ir_line)) 310 goto next_entry; 311 if (i == ATTR_UID) { 312 ir.ir_key.ik_uid = strtonum( 313 ldap_attrs[0], 0, UID_MAX, NULL); 314 } 315 ldap_value_free(ldap_attrs); 316 } 317 if (i != ATTR_SHELL) 318 if (strlcat(ir.ir_line, ":", 319 sizeof(ir.ir_line)) >= sizeof(ir.ir_line)) 320 goto next_entry; 321 322 } 323 imsg_compose(env->sc_ibuf, IMSG_PW_ENTRY, 0, 0, 324 &ir, sizeof(ir)); 325 next_entry: 326 where = "ldap_next_message"; 327 if ((lm = ldap_next_message(ld, lm)) == NULL) 328 goto bad; 329 } 330 331 /* 332 * exact same code but for groups. 333 */ 334 335 bzero(attrs, sizeof(attrs)); 336 for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) { 337 if (idm->idm_flags & F_FIXED_ATTR(i)) 338 continue; 339 attrs[j++] = idm->idm_attrs[i]; 340 } 341 attrs[j] = NULL; 342 343 where = "ldap_search"; 344 if ((msgid = ldap_search(ld, idm->idm_binddn, LDAP_SCOPE_SUBTREE, 345 idm->idm_filters[FILTER_GROUP], attrs, 0)) < 0) 346 goto bad; 347 348 where = "ldap_result"; 349 if (ldap_result(ld, msgid, 1, NULL, &lm) == -1) 350 goto bad; 351 352 where = "ldap_result_message"; 353 if (lm == NULL) 354 goto bad; 355 356 where = "ldap_first_message"; 357 if ((lm = ldap_first_message(ld, lm)) == NULL) 358 goto bad; 359 360 /* 361 * build group line. 362 */ 363 364 n = ldap_count_entries(ld, lm); 365 while (n--) { 366 bzero(&ir, sizeof(ir)); 367 for (i = ATTR_GR_MIN, j = 0; i < ATTR_GR_MAX; i++) { 368 if (idm->idm_flags & F_FIXED_ATTR(i)) { 369 if (strlcat(ir.ir_line, idm->idm_attrs[i], 370 sizeof(ir.ir_line)) >= sizeof(ir.ir_line)) 371 /* 372 * entry yields a line > 1024, trash it. 373 */ 374 goto next_group_entry; 375 if (i == ATTR_GR_GID) { 376 ir.ir_key.ik_gid = strtonum( 377 idm->idm_attrs[i], 0, 378 GID_MAX, NULL); 379 } 380 } else { 381 ldap_attrs = (char **)ldap_get_values(ld, 382 lm, attrs[j++]); 383 if (ldap_attrs == NULL) 384 goto next_group_entry; 385 if (strlcat(ir.ir_line, ldap_attrs[0], 386 sizeof(ir.ir_line)) >= sizeof(ir.ir_line)) 387 goto next_group_entry; 388 if (i == ATTR_GR_GID) { 389 ir.ir_key.ik_gid = strtonum( 390 ldap_attrs[0], 0, GID_MAX, NULL); 391 } 392 ldap_value_free(ldap_attrs); 393 } 394 if (i != ATTR_GR_MEMBERS) 395 if (strlcat(ir.ir_line, ":", 396 sizeof(ir.ir_line)) >= sizeof(ir.ir_line)) 397 goto next_group_entry; 398 399 } 400 imsg_compose(env->sc_ibuf, IMSG_GRP_ENTRY, 0, 0, 401 &ir, sizeof(ir)); 402 next_group_entry: 403 where = "ldap_next_message"; 404 if ((lm = ldap_next_message(ld, lm)) == NULL) 405 goto bad; 406 } 407 408 return (0); 409 bad: 410 log_debug("directory %s errored out in %s", idm->idm_name, where); 411 return (-1); 412 } 413 414 void 415 client_configure(struct env *env) 416 { 417 enum imsg_type finish; 418 struct timeval tv; 419 struct idm *idm; 420 421 log_debug("connecting to directories"); 422 finish = IMSG_END_UPDATE; 423 imsg_compose(env->sc_ibuf, IMSG_START_UPDATE, 0, 0, NULL, 0); 424 TAILQ_FOREACH(idm, &env->sc_idms, idm_entry) 425 if (client_try_idm(env, idm) == -1) { 426 finish = IMSG_TRASH_UPDATE; 427 break; 428 } 429 imsg_compose(env->sc_ibuf, finish, 0, 0, NULL, 0); 430 tv.tv_sec = env->sc_conf_tv.tv_sec; 431 tv.tv_usec = env->sc_conf_tv.tv_usec; 432 evtimer_set(&env->sc_conf_ev, client_configure_wrapper, env); 433 evtimer_add(&env->sc_conf_ev, &tv); 434 } 435