Back to beaubozarth.com

server.c

/*
 * Authentication Server file
 * copyright beaubozarth.com 2008
 */

#include "includes.h"

static int DAEMON_MAX = 5;

/*
 * the last message the was processed
 */
int last_msg = -1;
int proc_max = 0;
const char *listen_port = "135";

/*
 * a useful macro to debug the last message processed
 */
#define LAST_MESSAGE() cifs_function( last_msg )

extern pstring usr_socket_opts;
extern SIG_ATOMIC_T sigterm;
extern SIG_ATOMIC_T sighup;

static int server_fd = -1;

int daemon_fd(void)
{
    return server_fd;
}

static void daemon_fd_set(int fd)
{
    server_fd = fd;
    client_setfd(fd);
}

/*
 * Signal termination.
 */

static void sig_term(void)
{
    sigterm = 1;
    sys_sel_signal();
}

/*
 * Process a sighup.
 */

static void sig_hup(int sig)
{
    sighup = 1;
    sys_sel_signal();
}

/*
 * SAM sync message
 */

static void SAM_sync_msg(int UNUSED(msg_type), pid_t UNUSED(pid),
             void *UNUSED(buf), size_t UNUSED(len))
{
        DEBUG( 10, ("** sam sync message received, ignoring\n"));
}

/*
 * SAM sync replicate message
 */

static void SAM_replicate_msg( int msg_type, pid_t pid, void *buf, size_t len )
{
        uint32 lserial;

        if (len != sizeof( uint32))
                return;

        lserial = *((uint32 *) buf);

        DEBUG( 3, ("SAM_replicate_message: received msg, serial = 0x%04x\n",
                  lserial));
}

/*
 * Begin socket connection
 */

static BOOL socket_open(void)
{

    /*
         * close standard file descriptors
         */
    daemon_fd_set(dup(0));

    /*
     * Don't close stderr.
     */
    close_fds(FALSE);
    
    set_socket_opts(daemon_fd(), "SO_KEEPALIVE");
    set_socket_opts(daemon_fd(), usr_socket_opts);

    return TRUE;
}

static void msg_exit_server(int msg_type, pid_t src, void *buf, size_t len)
{
    exit_server("Got a exit");
}

/*
 * Have we maxed the process limit ?
 */

static BOOL max_daemon_proc(void)
{
    if (proc_max <= DAEMON_MAX)
        proc_max++;
        return TRUE;
    else
    {
        if (debug)
        {
            DEBUG( 0,("max_daemon_proc: can't start daemon, maxed out.\n"));
        }
    }
    return FALSE;
}

/*
 * Open a socket.
 */

static BOOL open_socket(BOOL daemonize, BOOL nodaemon)
{
    int num_if = iface_count();
    int num_sockets = 0;
    int lset_fd[FD_SETSIZE];
    fd_set l_set;
    int s;
    int i;

    if (!daemonize)
    {
        return socket_open();
    }
        
    /*
     * Kill the zombies
     */
    signalcatch(SIGCLD, sigcld);
                
    FD_ZERO(&l_set);

    /*
     * Default port 135
     */
    s = lset_fd[num_sockets] = open_socket_in(SOCK_STREAM,
                          listen_port, 0,
                          ifip->s_addr,
                          TRUE);
    if (s == -1)
        return FALSE;

    /*
     * start listening
     */
    set_socket_opts(s,"SO_KEEPALIVE");
    set_socket_opts(s,usr_socket_opts);
     
    /*
         * Set daemon socket non-blocking for the accept.
     */
    set_blocking(s,FALSE);
 
    if (listen(s, LISTEN_BACKLOG ) == -1)
    {
        DEBUG(0, ("listen: %s\n", strerror( errno)));
        close(s);
        return FALSE;
    }
    FD_SET(s, &l_set);

    num_sockets++;
    if (num_sockets >= FD_SETSIZE)
    {
        DEBUG(0, ("open_socket: Too many sockets to bind to\n"));
        return FALSE;
    }
    else
    {
        /*
         * Just bind to 0.0.0.0 - accept  all connections
         */
        fstring tok;
        const char *ptr;

        num_if = 1;
        
        /*
         * open a socket
         */
        s = open_socket_in(SOCK_STREAM,
                   listen_port, 0,
                   interpret_addr(lp_socket_address()),
                   TRUE);
        if (s == -1)
            return(FALSE);
        
        /*  
         * listen
         */
        set_socket_opts(s, "SO_KEEPALIVE");
        set_socket_opts(s, user_socket_options);
            
        /*
         * Set socket to non-blocking
         */
        set_blocking(s, FALSE);
 
        if (listen( s, LISTEN_BACKLOG ) == -1)
        {
            DEBUG(0, ("open_socket: listen: %s\n",
                      strerror( errno)));
            close(s);
            return FALSE;
        }

        lset_fd[num_sockets] = s;
        FD_SET(s, &l_set);

        num_sockets++;

        if (num_sockets >= FD_SETSIZE)
        {
            DEBUG(0, ( "open_socket: Too many sockets to bind to\n"));
            return FALSE;
        }
    }

    SAFE_FREE(listen_port);

        /*
     * messages
     */

        reg_msg(MSG_SAM_SYNC, SAM_sync_msg);
        reg_msg(MSG_SAM_REPL, SAM_replicate_msg);
        reg_msg(MSG_SHUTDOWN, msg_exit_server);

    /*
     * accept incoming connections  
     * fork each new process
     */
    DEBUG(2, ("listening for a connection\n"));
    while (1)
    {
        fd_set lfds;
        int num;
        
        /*
         * Free temp memory from the parent.
         */
        talloc_free();

        /*
         * respond to PING and DEBUG messages from the parent.
         */
        dispatch_msg();

        memcpy( (char * ) &lfds,
            (char * ) &l_set,
                 sizeof( l_set));
        
        num = sys_select(FD_SETSIZE,
                 &lfds,
                 NULL,
                 NULL,
                 NULL);
        
        if (num == -1 && errno == EINTR)
        {
            if (sigterm)
            {
                exit_server("Caught SIGTERM");
            }

            /*
             * check for sighup
             */
            if (sighup)
            {
                DEBUG(1, ("Reloading services after SIGHUP\n"));
                services_reload(FALSE);
                sighup = 0;
            }

            continue;
        }
        
        /*
         * check for need to reload services
         */
        check_reload(time(NULL));

        /*
         * accept on read-only sockets.
         */
        for( ; num > 0; num--)
        {
            struct sockaddr addr;
            socklen_t in_addrlen = sizeof(addr);

            s = -1;
            for(i = 0; i < num_sockets; i++)
            {
                if(FD_ISSET(lset_fd[i], &lfds))
                {
                    s = lset_fd[i];
                    /*
                     * Clear this it's done.
                     */
                    FD_CLR(lset_fd[i], &lfds);
                    break;
                }
            }

            daemon_fd_set(accept( s, &addr, &in_addrlen));
            
            if (daemon_fd() == -1 && errno == EINTR)
                continue;
            
            if (daemon_fd() == -1)
            {
                DEBUG(0, ("open_socket: accept: %s\n",
                        strerror(errno)));
                continue;
            }

            /*
             * Ensure the child is in blocking mode
             */
            set_blocking(daemon_fd(), TRUE);

            if (daemon_fd() != -1 && nodaemon)
                return TRUE;
            
            if (max_daemon_proc() && daemon_fd() != -1 && sys_fork()==0)
            {
                /*
                 * We are now the child
                 * close any listening sockets
                 */
                for(i = 0; i < num_sockets; i++)
                    close(lset_fd[i]);
                
                /*
                 * close the file descriptors
                 */
                close_fds(FALSE);
                
                set_socket_opts(daemon_fd(), "SO_KEEPALIVE");
                set_socket_opts(daemon_fd(), usr_socket_opts);
                
                set_remote_machine_name(get_peer_addr( daemon_fd()),
                             FALSE);
                
                /*
                 * Reset the random number generator
                 * keeps children from getting the
                 * same random numbers.
                 */

                flag_random_reseed();
                return TRUE;
            }
            /*
             * Parent doesn't need this
             */
            close(daemon_fd());
            daemon_fd_set(-1);
        }
    } /* end while loop */

/*
 * NOTREACHED    
 */
}

/*
 * Reload services file.
 */

BOOL services_reload(BOOL test)
{
    BOOL ret;
    
    reopen_logs();

    if (daemon_fd() != -1)
    {      
        set_socket_opts(daemon_fd(), "SO_KEEPALIVE");
        set_socket_opts(daemon_fd(), usr_socket_opts);
    }

    /*
     * flush service parameters
     */
    set_current_service(NULL, 0, TRUE);

    return(ret);
}


/*
 * The server's done, stick a fork in it
 */
void exit_server(const char *reason)
{
    static int firsttime = 1;
    extern char *last_inbuf;
    extern struct auth_context *negprot_global_auth_context;

    if (!firsttime)
        exit(0);
    firsttime = 0;

    DEBUG( 2,("Closing connections\n"));

    if (negprot_global_auth_context)
    {
        (negprot_global_auth_context->free ) ( &negprot_global_auth_context);
    }

    conn_close_all();

    invalidate_all_vuids();

    print_notify_send_messages(3);

    /*
     * Handle exit events
     */
    do_exit_events();

    /*
     * delete the connections.
     */
    conn_give(NULL, "");

    resp_rem_msg();
    dec_proc_cnt();

    if (!reason)
    {   
        int oldlevel = DEBUGLEVEL;
        DEBUGLEVEL = 10;
        DEBUG( 0, ("Last message was %s\n",fn_name( last_msg)));
        if (last_inbuf)
            show_msg(last_inbuf);
        DEBUGLEVEL = oldlevel;
    }    

    locking_end();

    DEBUG(3, ("Server exit (%s)\n", (reason ? reason : "")));
    exit(0);
}

/*
 * Init the connect, service and file structs.
 */
static BOOL init_structs(void)
{
    /*
     * Set the machine NETBIOS name if not already
     * set from the config file.
     */

    if (!init_names())
        return FALSE;

    conn_init();

    file_init();

    /*
     * RPC pipes
     */
    init_rpc_pipe_hnd();

    init_dptrs();

    secrets_init();

    return TRUE;
}

/*
 * main
 */

 int main(int argc, const char *argv[])
{
    /*
     * daemon or not
     */
    static BOOL daemonize = FALSE;
    static BOOL nodaemon = FALSE;
    static BOOL fork = TRUE;
    static BOOL to_stdout = FALSE;
    static char *port = NULL;
    int opt;

    /*
     * Set UID/GID to ensure we're root.
     */
    uid = getuid();
    if (uid != 0)
        setuid(0, 0, 0);
    gid = getegid();
    if (gid != 0)
        setgid(0, 0, 0);

    /*
     * Parse the args here
     */
    while( (opt = getopt(argc, argv, "i:d:")) !=EOF)
    {
        switch(opt)
        {
        case 'i':
            nodaemon = TRUE;
            break;
        case 'd':
            DEBUG = TRUE;
            break;
        default:
            break;
        }
    }

    if (nodaemon)
    {
        fork = FALSE;
        to_stdout = TRUE;
    }

    if (to_stdout && fork)
    {
        DEBUG(0, ("DAEMON: Can't log to stdout unless daemon is nodaemon (-i)\n"));
        exit(-1);
    }

    start_log(to_stdout);

    setup_sigfault( (void (*) (void *)) exit_server);
    signalcatch(SIGTERM, SIGNAL_CAST sigterm);
    signalcatch(SIGHUP, SIGNAL_CAST sighup);
    
    /*
     * Let's block some boring signals
     */
    signalblock(TRUE, SIGPIPE);
    signalblock(TRUE, SIGFPE);
    signalblock(TRUE, SIGUSR2);

    /* In POSIX signals are inherited. If the following signals
     * are masked, we'll die as we won't recieve them.
     */
    signalblock(FALSE, SIGHUP);
    signalblock(FALSE, SIGUSR1);
    signalblock(FALSE, SIGTERM);

    /*
     * Set total control on permissions
     */
    umask(0);

    /*
     * Re-open the log file if it's closed
         * for some reason.
     */
    reopen_log();

    /*
     * The following displays upon daemon startup
     */
    DEBUG(0, ("tc auth server started.\n"));

    DEBUG(2,("uid=%d gid=%d euid=%d egid=%d\n",
         (int) getuid(), (int) getgid(),
         (int) geteuid(),(int) getegid()));

    if (!daemonize && !socket_yes(0))
    {
        if (!nodaemon)
            DEBUG(0, ("DAEMON: not a socket, assuming -d option\n"));

        /*
         * By setting daemonize open_socket() doesn't
         * get called later.
         */
        daemonize = TRUE;
    }

    if (daemonize && !nodaemon)
    {
        DEBUG(3, ("fork the daemon.\n"));
        fork_daemon(fork);
    }

    /*
     * Set process group for signal management.
     */
    if (nodaemon)
        setpgid((pid_t) 0, (pid_t) 0);

    if (daemonize)
        pidfile_create("authserv");

    /*
     * Setup the tdb's
     */
    if (!msg_init())
        exit(1);

    if (!sess_init())
        exit(1);

    if (conn_init() == NULL)
        exit(1);

    if (!lock_init(0))
        exit(1);

    if (!info_db_init())
        exit(1);

    if (!init_reg())
        exit(1);

    /*
     * Setup so we can get messages.
     */

    conn_entry();

    if (!open_socket( daemonize, nodaemon))
        exit(1);

    /*
     * after the fork()
     */

    /*
     * Try to set up a SID using PAM
     */
    if(!passdb_init( FALSE))
        exit(1);

    if(!gsam_sid_get())
    {
        DEBUG(0, ("DAEMON: cannot create SAM SID.\n"));
        exit(1);
    }

    mod_init();

    /*
     * reload the services file.
     */
    services_reload(TRUE);

    if (!acct_policy_init())
    {
        DEBUG(0, ("Could not open account policy tdb.\n"));
        exit(1);
    }

    /*
     * Setup oplocks
     */
    if (!oplock_init())
        exit(1);
    
    /*
     * Setup change notify
     */
    if (!nchange_init())
        exit(1);

    /*
     * re-init timezone
     */
    init_time();

    /*
     * register msg handlers
     */
    reg_msg(MSG_FORCE_TDIS, msg_force_tdis);

    /*
     * Process client requests here
     */
    serv_process();
    
    exit_server("normal exit");
    return(0);
}