1/******************************************************************************* 2 * 3 * ipc_unix.c 4 * 5 * Description: Implements the AF_UNIX IPC method. 6 * 7 * Copyright (c) 1997-2000 Messaging Direct Ltd. 8 * All rights reserved. 9 * 10 * Portions Copyright (c) 2003 Jeremy Rumpf 11 * jrumpf@heavyload.net 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY MESSAGING DIRECT LTD. ``AS IS'' AND ANY 23 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MESSAGING DIRECT LTD. OR 26 * ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 29 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 31 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 32 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 33 * DAMAGE. 34 * 35 * 36 * HISTORY 37 * 38 * 39 * This source file created using 8 space tabs. 40 * 41 ********************************************************************************/ 42 43/**************************************** 44 * enable/disable ifdef 45*****************************************/ 46#include "saslauthd-main.h" 47 48#ifdef USE_UNIX_IPC 49/****************************************/ 50 51/**************************************** 52 * includes 53*****************************************/ 54#include <stdlib.h> 55#include <unistd.h> 56#include <sys/types.h> 57#include <sys/stat.h> 58#include <fcntl.h> 59#include <sys/socket.h> 60#include <sys/un.h> 61#include <string.h> 62#include <errno.h> 63#include <netinet/in.h> 64 65#include "globals.h" 66#include "utils.h" 67 68/**************************************** 69 * declarations/protos 70 *****************************************/ 71static void do_request(int); 72static void send_no(int, char *); 73static int rel_accept_lock(); 74static int get_accept_lock(); 75 76/**************************************** 77 * module globals 78 *****************************************/ 79static int sock_fd; /* descriptor for the socket */ 80static int accept_fd; /* descriptor for the accept lock */ 81static struct sockaddr_un server; /* domain socket control, server side */ 82static struct sockaddr_un client; /* domain socket control, client side */ 83static SALEN_TYPE len; /* length for the client sockaddr_un */ 84static char *sock_file; /* path to the AF_UNIX socket */ 85static char *accept_file;/* path to the accept() lock file */ 86 87/**************************************** 88 * flags global from saslauthd-main.c 89 * run_path global from saslauthd-main.c 90 * num_procs global from saslauthd-main.c 91 * detach_tty() function from saslauthd-main.c 92 * rx_rec() function from utils.c 93 * tx_rec() function from utils.c 94 * logger() function from utils.c 95 *****************************************/ 96 97 98/************************************************************* 99 * IPC init. Initialize the environment specific to the 100 * AF_UNIX IPC method. 101 * 102 * __Required Function__ 103 **************************************************************/ 104void ipc_init() { 105 int rc; 106 size_t sock_file_len; 107 108 /********************************************************* 109 * When we're not preforking, using an accept lock is a 110 * waste of resources. Otherwise, setup the accept lock 111 * file. 112 **********************************************************/ 113 if (num_procs == 0) 114 flags &= ~USE_ACCEPT_LOCK; 115 116 if (flags & USE_ACCEPT_LOCK) { 117 size_t accept_file_len; 118 119 accept_file_len = strlen(run_path) + sizeof(ACCEPT_LOCK_FILE) + 1; 120 if ((accept_file = malloc(accept_file_len)) == NULL) { 121 logger(L_ERR, L_FUNC, "could not allocate memory"); 122 exit(1); 123 } 124 125 strlcpy(accept_file, run_path, accept_file_len); 126 strlcat(accept_file, ACCEPT_LOCK_FILE, accept_file_len); 127 128 if ((accept_fd = open(accept_file, O_RDWR|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR)) == -1) { 129 rc = errno; 130 logger(L_ERR, L_FUNC, "could not open accept lock file: %s", accept_file); 131 logger(L_ERR, L_FUNC, "open: %s", strerror(rc)); 132 exit(1); 133 } 134 135 if (flags & VERBOSE) 136 logger(L_DEBUG, L_FUNC, "using accept lock file: %s", accept_file); 137 } 138 139 /************************************************************** 140 * We're at the point where we can't really do anything else 141 * until we attempt to detach or daemonize. 142 **************************************************************/ 143 detach_tty(); 144 145 /************************************************************** 146 * Setup the UNIX domain socket 147 **************************************************************/ 148 sock_file_len = strlen(run_path) + sizeof(SOCKET_FILE) + 1; 149 if ((sock_file = malloc(sock_file_len)) == NULL) { 150 logger(L_ERR, L_FUNC, "could not allocate memory"); 151 exit(1); 152 } 153 154 strlcpy(sock_file, run_path, sock_file_len); 155 strlcat(sock_file, SOCKET_FILE, sock_file_len); 156 157 unlink(sock_file); 158 memset(&server, 0, sizeof(server)); 159 strlcpy(server.sun_path, sock_file, sizeof(server.sun_path)); 160 server.sun_family = AF_UNIX; 161 162 if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { 163 rc = errno; 164 logger(L_ERR, L_FUNC, "could not create socket"); 165 logger(L_ERR, L_FUNC, "socket: %s", strerror(rc)); 166 exit(1); 167 } 168 169 umask(0); 170 171 if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1) { 172 rc = errno; 173 logger(L_ERR, L_FUNC, "could not bind to socket: %s", sock_file); 174 logger(L_ERR, L_FUNC, "bind: %s", strerror(rc)); 175 exit(1); 176 } 177 178 if (chmod(sock_file, S_IRWXU|S_IRWXG|S_IRWXO) == -1) { 179 rc = errno; 180 logger(L_ERR, L_FUNC, "could not chmod socket: %s", sock_file); 181 logger(L_ERR, L_FUNC, "chmod: %s", strerror(rc)); 182 exit(1); 183 } 184 185 fchmod(sock_fd, S_IRWXU|S_IRWXG|S_IRWXO); 186 187 umask(077); 188 189 if (listen(sock_fd, SOCKET_BACKLOG) == -1) { 190 rc = errno; 191 logger(L_ERR, L_FUNC, "could not listen on socket: %s", sock_file); 192 logger(L_ERR, L_FUNC, "listen: %s", strerror(rc)); 193 exit(1); 194 } 195 196 197 logger(L_INFO, L_FUNC, "listening on socket: %s", sock_file); 198 199 /************************************************************** 200 * Ok boys... Let's procreate... If necessary of course... 201 * Num_procs == 0 means we're running one shot per process. In 202 * that case, we'll handle forking on a per connection basis. 203 **************************************************************/ 204 if (num_procs != 0) 205 flags |= USE_PROCESS_MODEL; 206 207 return; 208} 209 210/************************************************************* 211 * Main IPC loop. Handle all the socket accept stuff, fork if 212 * needed, then pass things off to do_request(). 213 * 214 * __Required Function__ 215 **************************************************************/ 216void ipc_loop() { 217 218 int rc; 219 int conn_fd; 220 221 222 while(1) { 223 224 len = sizeof(client); 225 226 /************************************************************** 227 * First, if needed, get the accept lock. If it fails, take a 228 * nap and go to the top of the loop. (or should we just die?) 229 *************************************************************/ 230 if (get_accept_lock() != 0) { 231 sleep(5); 232 continue; 233 } 234 235 conn_fd = accept(sock_fd, (struct sockaddr *)&client, (socklen_t *)&len); /* APPLE: cast */ 236 rc = errno; 237 238 rel_accept_lock(); 239 240 if (conn_fd == -1) { 241 if (rc != EINTR) { 242 logger(L_ERR, L_FUNC, "socket accept failure"); 243 logger(L_ERR, L_FUNC, "accept: %s", strerror(rc)); 244 sleep(5); 245 } 246 continue; 247 } 248 249 /************************************************************** 250 * If we're running one shot, drop off a kid to handle the 251 * connection. 252 *************************************************************/ 253 if (num_procs == 0) { 254 if(flags & DETACH_TTY) { 255 if (have_baby() > 0) { /* parent */ 256 close(conn_fd); 257 continue; 258 } 259 260 close(sock_fd); /* child */ 261 } 262 263 do_request(conn_fd); 264 close(conn_fd); 265 266 if(flags & DETACH_TTY) { 267 exit(0); 268 } else { 269 continue; 270 } 271 272 } 273 274 /************************************************************** 275 * Normal prefork mode. 276 *************************************************************/ 277 do_request(conn_fd); 278 close(conn_fd); 279 } 280 281 return; 282} 283 284 285/************************************************************* 286 * General cleanup. Unlock, close, and unlink our files. 287 * 288 * __Required Function__ 289 **************************************************************/ 290void ipc_cleanup() { 291 292 struct flock lock_st; 293 294 if (flags & USE_ACCEPT_LOCK) { 295 296 lock_st.l_type = F_UNLCK; 297 lock_st.l_start = 0; 298 lock_st.l_whence = SEEK_SET; 299 lock_st.l_len = 1; 300 301 fcntl(accept_fd, F_SETLK, &lock_st); 302 303 close(accept_fd); 304 unlink(accept_file); 305 306 if (flags & VERBOSE) 307 logger(L_DEBUG, L_FUNC, "accept lock file removed: %s", accept_file); 308 } 309 310 close(sock_fd); 311 unlink(sock_file); 312 313 if (flags & VERBOSE) 314 logger(L_DEBUG, L_FUNC, "socket removed: %s", sock_file); 315} 316 317 318/************************************************************* 319 * Handle the comms on the socket, pass the request off to 320 * do_auth() back in saslauthd-main.c, then transmit the 321 * result back out on the socket. 322 **************************************************************/ 323void do_request(int conn_fd) { 324 325 unsigned short count; /* input/output data byte count */ 326 unsigned short ncount; /* input/output data byte count, network */ 327 char *response; /* response to send to the client */ 328 char login[MAX_REQ_LEN + 1]; /* account name to authenticate */ 329 char password[MAX_REQ_LEN + 1]; /* password for authentication */ 330 char service[MAX_REQ_LEN + 1]; /* service name for authentication */ 331 char realm[MAX_REQ_LEN + 1]; /* user realm for authentication */ 332 333 334 /************************************************************** 335 * The input data stream consists of the login id, password, 336 * service name and user realm as counted length strings. 337 * We read in each string, then dispatch the data. 338 **************************************************************/ 339 340 /* login id */ 341 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 342 return; 343 344 count = ntohs(count); 345 346 if (count > MAX_REQ_LEN) { 347 logger(L_ERR, L_FUNC, "login exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN); 348 send_no(conn_fd, ""); 349 return; 350 } 351 352 if (rx_rec(conn_fd, (void *)login, (size_t)count) != (ssize_t)count) 353 return; 354 355 login[count] = '\0'; 356 357 /* password */ 358 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 359 return; 360 361 count = ntohs(count); 362 363 if (count > MAX_REQ_LEN) { 364 logger(L_ERR, L_FUNC, "password exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN); 365 send_no(conn_fd, ""); 366 return; 367 } 368 369 if (rx_rec(conn_fd, (void *)password, (size_t)count) != (ssize_t)count) 370 return; 371 372 password[count] = '\0'; 373 374 /* service */ 375 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 376 return; 377 378 count = ntohs(count); 379 380 if (count > MAX_REQ_LEN) { 381 logger(L_ERR, L_FUNC, "service exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN); 382 send_no(conn_fd, ""); 383 return; 384 } 385 386 if (rx_rec(conn_fd, (void *)service, (size_t)count) != (ssize_t)count) 387 return; 388 389 service[count] = '\0'; 390 391 /* realm */ 392 if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 393 return; 394 395 count = ntohs(count); 396 397 if (count > MAX_REQ_LEN) { 398 logger(L_ERR, L_FUNC, "realm exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN); 399 send_no(conn_fd, ""); 400 return; 401 } 402 403 if (rx_rec(conn_fd, (void *)realm, (size_t)count) != (ssize_t)count) 404 return; 405 406 realm[count] = '\0'; 407 408 /************************************************************** 409 * We don't allow NULL passwords or login names 410 **************************************************************/ 411 if (*login == '\0') { 412 logger(L_ERR, L_FUNC, "NULL login received"); 413 send_no(conn_fd, "NULL login received"); 414 return; 415 } 416 417 if (*password == '\0') { 418 logger(L_ERR, L_FUNC, "NULL password received"); 419 send_no(conn_fd, "NULL password received"); 420 return; 421 } 422 423 /************************************************************** 424 * Get the mechanism response from do_auth() and send it back. 425 **************************************************************/ 426 response = do_auth(login, password, service, realm); 427 428 memset(password, 0, strlen(password)); 429 430 if (response == NULL) { 431 send_no(conn_fd, "NULL response from mechanism"); 432 return; 433 } 434 435 count = strlen(response); 436 ncount = htons(count); 437 438 if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount)) { 439 free(response); 440 return; 441 } 442 443 if (tx_rec(conn_fd, (void *)response, (size_t)count) != (ssize_t)sizeof(count)) { 444 free(response); 445 return; 446 } 447 448 if (flags & VERBOSE) 449 logger(L_DEBUG, L_FUNC, "response: %s", response); 450 451 free(response); 452 453 return; 454} 455 456 457/************************************************************* 458 * In case something went out to lunch while reading in the 459 * request data, we may want to attempt to send out a default 460 * "NO" response on the socket. The mesg is optional. 461 **************************************************************/ 462void send_no(int conn_fd, char *mesg) { 463 char buff[1024]; 464 unsigned short count; 465 unsigned short ncount; 466 467 buff[0] = 'N'; 468 buff[1] = 'O'; 469 buff[2] = ' '; 470 471 /* buff, except for the trailing NUL and 'NO ' */ 472 strncpy(buff + 3, mesg, sizeof(buff) - 1 - 3); 473 buff[1023] = '\0'; 474 475 count = strlen(buff); 476 ncount = htons(count); 477 478 if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount)) 479 return; 480 481 if (tx_rec(conn_fd, (void *)buff, (size_t)count) != (ssize_t)sizeof(count)) 482 return; 483 484 if (flags & VERBOSE) 485 logger(L_DEBUG, L_FUNC, "response: %s", buff); 486 487 return; 488} 489 490 491/************************************************************* 492 * Attempt to get a write lock on the accept lock file. 493 * Return 0 if everything went ok, return -1 if something bad 494 * happened. This function is expected to block. 495 **************************************************************/ 496int get_accept_lock() { 497 498 struct flock lock_st; 499 int rc; 500 501 502 if (!(flags & USE_ACCEPT_LOCK)) 503 return 0; 504 505 lock_st.l_type = F_WRLCK; 506 lock_st.l_start = 0; 507 lock_st.l_whence = SEEK_SET; 508 lock_st.l_len = 1; 509 510 errno = 0; 511 512 do { 513 rc = fcntl(accept_fd, F_SETLKW, &lock_st); 514 } while (rc != 0 && errno == EINTR); 515 516 if (rc != 0) { 517 rc = errno; 518 logger(L_ERR, L_FUNC, "could not acquire accept lock"); 519 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc)); 520 return -1; 521 } 522 523 if (flags & VERBOSE) 524 logger(L_DEBUG, L_FUNC, "acquired accept lock"); 525 526 return 0; 527} 528 529 530/************************************************************* 531 * Attempt to release the write lock on the accept lock file. 532 * Return 0 if everything went ok, return -1 if something bad 533 * happened. 534 **************************************************************/ 535int rel_accept_lock() { 536 537 struct flock lock_st; 538 int rc; 539 540 541 if (!(flags & USE_ACCEPT_LOCK)) 542 return 0; 543 544 lock_st.l_type = F_UNLCK; 545 lock_st.l_start = 0; 546 lock_st.l_whence = SEEK_SET; 547 lock_st.l_len = 1; 548 549 errno = 0; 550 551 do { 552 rc = fcntl(accept_fd, F_SETLKW, &lock_st); 553 } while (rc != 0 && errno == EINTR); 554 555 if (rc != 0) { 556 rc = errno; 557 logger(L_ERR, L_FUNC, "could not release accept lock"); 558 logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc)); 559 return -1; 560 } 561 562 if (flags & VERBOSE) 563 logger(L_DEBUG, L_FUNC, "released accept lock"); 564 565 return 0; 566} 567 568 569 570#endif /* USE_UNIX_IPC */ 571