Logo Search packages:      
Sourcecode: cyrus-sasl2 version File versions  Download package

ipc_unix.c

/*******************************************************************************
 *
 * ipc_unix.c
 *
 * Description:  Implements the AF_UNIX IPC method.
 *
 * Copyright (c) 1997-2000 Messaging Direct Ltd.
 * All rights reserved.
 *
 * Portions Copyright (c) 2003 Jeremy Rumpf
 * jrumpf@heavyload.net
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY MESSAGING DIRECT LTD. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL MESSAGING DIRECT LTD. OR
 * ITS EMPLOYEES OR AGENTS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 *
 * HISTORY
 *
 * 
 * This source file created using 8 space tabs.
 *
 ********************************************************************************/

/****************************************
 * enable/disable ifdef
*****************************************/
#include "saslauthd-main.h"

#ifdef USE_UNIX_IPC
/****************************************/

/****************************************
 * includes
*****************************************/
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>

#include "globals.h"
#include "utils.h"

/****************************************
 * declarations/protos
 *****************************************/
static void do_request(int);
static void send_no(int, char *);
static int  rel_accept_lock();
static int  get_accept_lock();

/****************************************
 * module globals
 *****************************************/
static int              sock_fd;     /* descriptor for the socket          */
static int              accept_fd;   /* descriptor for the accept lock     */
static struct sockaddr_un     server;      /* domain socket control, server side */
static struct sockaddr_un     client;      /* domain socket control, client side */
static SALEN_TYPE       len;         /* length for the client sockaddr_un  */
static char             *sock_file;  /* path to the AF_UNIX socket         */
static char             *accept_file;/* path to the accept() lock file     */

/****************************************
 * flags          global from saslauthd-main.c
 * run_path       global from saslauthd-main.c
 * num_procs      global from saslauthd-main.c
 * detach_tty()   function from saslauthd-main.c
 * rx_rec()       function from utils.c
 * tx_rec()       function from utils.c
 * logger()       function from utils.c
 *****************************************/


/*************************************************************
 * IPC init. Initialize the environment specific to the 
 * AF_UNIX IPC method.
 *
 * __Required Function__
 **************************************************************/
void ipc_init() {
      int   rc;
      size_t  sock_file_len;
      
        /*********************************************************
       * When we're not preforking, using an accept lock is a
       * waste of resources. Otherwise, setup the accept lock
       * file.
       **********************************************************/
      if (num_procs == 0) 
            flags &= ~USE_ACCEPT_LOCK;
      
      if (flags & USE_ACCEPT_LOCK) {
            size_t accept_file_len;

            accept_file_len = strlen(run_path) + sizeof(ACCEPT_LOCK_FILE) + 1;
            if ((accept_file = malloc(accept_file_len)) == NULL) {
                  logger(L_ERR, L_FUNC, "could not allocate memory");
                  exit(1);
            }

            strlcpy(accept_file, run_path, accept_file_len);
            strlcat(accept_file, ACCEPT_LOCK_FILE, accept_file_len);

            if ((accept_fd = open(accept_file, O_RDWR|O_CREAT|O_TRUNC, S_IWUSR|S_IRUSR)) == -1) {
                  rc = errno;
                  logger(L_ERR, L_FUNC, "could not open accept lock file: %s", accept_file);
                  logger(L_ERR, L_FUNC, "open: %s", strerror(rc));
                  exit(1);
            }

            if (flags & VERBOSE)
                  logger(L_DEBUG, L_FUNC, "using accept lock file: %s", accept_file);
      }

      /**************************************************************
       * We're at the point where we can't really do anything else
       * until we attempt to detach or daemonize.
       **************************************************************/
      detach_tty();

      /**************************************************************
       * Setup the UNIX domain socket
       **************************************************************/
      sock_file_len = strlen(run_path) + sizeof(SOCKET_FILE) + 1;
      if ((sock_file = malloc(sock_file_len)) == NULL) {
            logger(L_ERR, L_FUNC, "could not allocate memory");
            exit(1);
      }

      strlcpy(sock_file, run_path, sock_file_len);
      strlcat(sock_file, SOCKET_FILE, sock_file_len);

      unlink(sock_file);
      memset(&server, 0, sizeof(server));
      strlcpy(server.sun_path, sock_file, sizeof(server.sun_path));     
      server.sun_family = AF_UNIX;
      
      if ((sock_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
            rc = errno;
            logger(L_ERR, L_FUNC, "could not create socket");
            logger(L_ERR, L_FUNC, "socket: %s", strerror(rc));
            exit(1);
      }

      umask(0);

      if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1) {
            rc = errno;
            logger(L_ERR, L_FUNC, "could not bind to socket: %s", sock_file);
            logger(L_ERR, L_FUNC, "bind: %s", strerror(rc));
            exit(1);
      }

      if (chmod(sock_file, S_IRWXU|S_IRWXG|S_IRWXO) == -1) {
            rc = errno;
            logger(L_ERR, L_FUNC, "could not chmod socket: %s", sock_file);
            logger(L_ERR, L_FUNC, "chmod: %s", strerror(rc));
            exit(1);
      }

      fchmod(sock_fd, S_IRWXU|S_IRWXG|S_IRWXO);

      umask(077);

      if (listen(sock_fd, SOCKET_BACKLOG) == -1) {
            rc = errno;
            logger(L_ERR, L_FUNC, "could not listen on socket: %s", sock_file);
            logger(L_ERR, L_FUNC, "listen: %s", strerror(rc));
            exit(1);
      }


      logger(L_INFO, L_FUNC, "listening on socket: %s", sock_file);

      /**************************************************************
       * Ok boys... Let's procreate... If necessary of course...
       * Num_procs == 0 means we're running one shot per process. In
       * that case, we'll handle forking on a per connection basis.
       **************************************************************/
      if (num_procs != 0)
            flags |= USE_PROCESS_MODEL;

      return;
}

/*************************************************************
 * Main IPC loop. Handle all the socket accept stuff, fork if 
 * needed, then pass things off to do_request().
 *
 * __Required Function__
 **************************************************************/
void ipc_loop() {

      int         rc;
      int         conn_fd;


      while(1) {

            len = sizeof(client);

            /**************************************************************
             * First, if needed, get the accept lock. If it fails, take a
             * nap and go to the top of the loop. (or should we just die?)
             *************************************************************/
            if (get_accept_lock() != 0) {
                  sleep(5);
                  continue;
            }

            conn_fd = accept(sock_fd, (struct sockaddr *)&client, &len);
            rc = errno;

            rel_accept_lock();

            if (conn_fd == -1) {
                  if (rc != EINTR) {
                        logger(L_ERR, L_FUNC, "socket accept failure");
                        logger(L_ERR, L_FUNC, "accept: %s", strerror(rc));
                        sleep(5);
                  }
                  continue;
            }

            /**************************************************************
             * If we're running one shot, drop off a kid to handle the
             * connection.
             *************************************************************/
            if (num_procs == 0) {
                if(flags & DETACH_TTY) {
                  if (have_baby() > 0) {  /* parent */
                      close(conn_fd);
                      continue;
                  }
                  
                  close(sock_fd);         /* child  */
                }
                
                do_request(conn_fd);
                close(conn_fd);

                if(flags & DETACH_TTY) {
                  exit(0);    
                } else {
                  continue;
                }
                
            }

            /**************************************************************
             * Normal prefork mode.
             *************************************************************/
            do_request(conn_fd);
            close(conn_fd);
      }

      return;
}


/*************************************************************
 * General cleanup. Unlock, close, and unlink our files.
 *
 * __Required Function__
 **************************************************************/
void ipc_cleanup() {

      struct flock    lock_st;

      if (flags & USE_ACCEPT_LOCK) {

            lock_st.l_type = F_UNLCK;
            lock_st.l_start = 0;
            lock_st.l_whence = SEEK_SET;
            lock_st.l_len = 1;

            fcntl(accept_fd, F_SETLK, &lock_st);

            close(accept_fd);
            unlink(accept_file);

            if (flags & VERBOSE)
                  logger(L_DEBUG, L_FUNC, "accept lock file removed: %s", accept_file);
      }

      close(sock_fd);
      unlink(sock_file);

      if (flags & VERBOSE)
            logger(L_DEBUG, L_FUNC, "socket removed: %s", sock_file);
}


/*************************************************************
 * Handle the comms on the socket, pass the request off to
 * do_auth() back in saslauthd-main.c, then transmit the
 * result back out on the socket.
 **************************************************************/
void do_request(int conn_fd) {

      unsigned short          count;                     /* input/output data byte count           */
      unsigned short          ncount;                    /* input/output data byte count, network  */ 
      char              *response;                 /* response to send to the client         */
      char              login[MAX_REQ_LEN + 1];    /* account name to authenticate           */
      char              password[MAX_REQ_LEN + 1]; /* password for authentication            */
      char              service[MAX_REQ_LEN + 1];  /* service name for authentication        */
      char              realm[MAX_REQ_LEN + 1];    /* user realm for authentication          */


      /**************************************************************
       * The input data stream consists of the login id, password,
       * service name and user realm as counted length strings.
       * We read in each string, then dispatch the data.
       **************************************************************/

      /* login id */
      if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 
            return;

      count = ntohs(count);

      if (count > MAX_REQ_LEN) {
            logger(L_ERR, L_FUNC, "login exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
            send_no(conn_fd, "");
            return;
      }     

      if (rx_rec(conn_fd, (void *)login, (size_t)count) != (ssize_t)count) 
            return;
      
      login[count] = '\0';

      /* password */
      if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 
            return;

      count = ntohs(count);

      if (count > MAX_REQ_LEN) {
            logger(L_ERR, L_FUNC, "password exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
            send_no(conn_fd, "");
            return;
      }     

      if (rx_rec(conn_fd, (void *)password, (size_t)count) != (ssize_t)count) 
            return;
            
      password[count] = '\0';

      /* service */
      if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 
            return;

      count = ntohs(count);

      if (count > MAX_REQ_LEN) {
            logger(L_ERR, L_FUNC, "service exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
            send_no(conn_fd, "");
            return;
      }     

      if (rx_rec(conn_fd, (void *)service, (size_t)count) != (ssize_t)count) 
            return;

      service[count] = '\0';

      /* realm */
      if (rx_rec(conn_fd, (void *)&count, (size_t)sizeof(count)) != (ssize_t)sizeof(count)) 
            return;

      count = ntohs(count);

      if (count > MAX_REQ_LEN) {
            logger(L_ERR, L_FUNC, "realm exceeded MAX_REQ_LEN: %d", MAX_REQ_LEN);
            send_no(conn_fd, "");
            return;
      }     

      if (rx_rec(conn_fd, (void *)realm, (size_t)count) != (ssize_t)count) 
            return;

      realm[count] = '\0';

      /**************************************************************
       * We don't allow NULL passwords or login names
       **************************************************************/
      if (*login == '\0') {
            logger(L_ERR, L_FUNC, "NULL login received");
            send_no(conn_fd, "NULL login received");
            return;
      }     
      
      if (*password == '\0') {
            logger(L_ERR, L_FUNC, "NULL password received");
            send_no(conn_fd, "NULL password received");
            return;
      }     

      /**************************************************************
       * Get the mechanism response from do_auth() and send it back.
       **************************************************************/
      response = do_auth(login, password, service, realm);

      memset(password, 0, strlen(password));

      if (response == NULL) {
            send_no(conn_fd, "NULL response from mechanism");
            return;
      }     

      count = strlen(response);
      ncount = htons(count);

      if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount)) {
            free(response);
            return;
      }

      if (tx_rec(conn_fd, (void *)response, (size_t)count) != (ssize_t)sizeof(count)) {
            free(response);
            return;
      }

      if (flags & VERBOSE)
            logger(L_DEBUG, L_FUNC, "response: %s", response);

      free(response);

      return;
}


/*************************************************************
 * In case something went out to lunch while reading in the
 * request data, we may want to attempt to send out a default 
 * "NO" response on the socket. The mesg is optional.
 **************************************************************/
void send_no(int conn_fd, char *mesg) {
      char        buff[1024];
      unsigned short    count; 
      unsigned short    ncount; 

      buff[0] = 'N';
      buff[1] = 'O';
      buff[2] = ' ';

      /* buff, except for the trailing NUL and 'NO ' */
      strncpy(buff + 3, mesg, sizeof(buff) - 1 - 3);
      buff[1023] = '\0';

      count = strlen(buff);
      ncount = htons(count);

      if (tx_rec(conn_fd, (void *)&ncount, (size_t)sizeof(ncount)) != (ssize_t)sizeof(ncount))
            return;

      if (tx_rec(conn_fd, (void *)buff, (size_t)count) != (ssize_t)sizeof(count))
            return;

      if (flags & VERBOSE)
            logger(L_DEBUG, L_FUNC, "response: %s", buff);
      
      return;     
}


/*************************************************************
 * Attempt to get a write lock on the accept lock file.
 * Return 0 if everything went ok, return -1 if something bad
 * happened. This function is expected to block.
 **************************************************************/
int get_accept_lock() {

      struct flock    lock_st;
      int             rc;


      if (!(flags & USE_ACCEPT_LOCK))
            return 0;

      lock_st.l_type = F_WRLCK;
      lock_st.l_start = 0;
      lock_st.l_whence = SEEK_SET;
      lock_st.l_len = 1;

      errno = 0;

      do {
            rc = fcntl(accept_fd, F_SETLKW, &lock_st);
      } while (rc != 0 && errno == EINTR);

      if (rc != 0) {
            rc = errno;
            logger(L_ERR, L_FUNC, "could not acquire accept lock");
            logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
            return -1;
      }

      if (flags & VERBOSE)
            logger(L_DEBUG, L_FUNC, "acquired accept lock");

      return 0;
}


/*************************************************************
 * Attempt to release the write lock on the accept lock file.
 * Return 0 if everything went ok, return -1 if something bad
 * happened.
 **************************************************************/
int rel_accept_lock() {

      struct flock    lock_st;
      int             rc;


      if (!(flags & USE_ACCEPT_LOCK))
            return 0;

      lock_st.l_type = F_UNLCK;
      lock_st.l_start = 0;
      lock_st.l_whence = SEEK_SET;
      lock_st.l_len = 1;

      errno = 0;

      do {
            rc = fcntl(accept_fd, F_SETLKW, &lock_st);
      } while (rc != 0 && errno == EINTR);

      if (rc != 0) {
            rc = errno;
            logger(L_ERR, L_FUNC, "could not release accept lock");
            logger(L_ERR, L_FUNC, "fcntl: %s", strerror(rc));
            return -1;
      }

      if (flags & VERBOSE)
            logger(L_DEBUG, L_FUNC, "released accept lock");

      return 0;
}



#endif /* USE_UNIX_IPC */

Generated by  Doxygen 1.6.0   Back to index