Package org.knopflerfish.bundle.consoletcp

Source Code of org.knopflerfish.bundle.consoletcp.ConsoleTcp$AcceptThread$LoginResult

/*
* Copyright (c) 2003, KNOPFLERFISH project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* - Redistributions of source code must retain the above copyright
*   notice, this list of conditions and the following disclaimer.
*
* - 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.
*
* - Neither the name of the KNOPFLERFISH project nor the names of its
*   contributors may be used to endorse or promote products derived
*   from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE
* COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/

package org.knopflerfish.bundle.consoletcp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.Hashtable;

import org.knopflerfish.service.console.ConsoleService;
import org.knopflerfish.service.console.Session;
import org.knopflerfish.service.console.SessionListener;
import org.knopflerfish.service.um.useradmin.ContextualAuthorization;
import org.knopflerfish.service.um.useradmin.PasswdAuthenticator;
import org.knopflerfish.service.um.useradmin.PasswdSession;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.log.LogService;
import org.osgi.service.useradmin.Authorization;

// ******************** ConsoleTcp ********************
/**
* * Bundle activator implementation. * *
*
* @author Jan Stein *
* @version $Revision: 1.1.1.1 $
*/
public class ConsoleTcp implements BundleActivator, ManagedService {
    ServiceReference srefConsole;

    private static final String PORT_PROPERTY = "port";

    private static final String ADDRESS_PROPERTY = "address";

    private static final String LOGIN_PROPERTY = "login";

    private static final String UM_PROPERTY = "um";

    private static final String EXIT_ON_LOGOUT_PROPERTY = "exitOnLogout";

    private static final String REQUIRED_GROUP_PROPERTY = "requiredGroup";

    private static final String FORBIDDEN_GROUP_PROPERTY = "forbiddenGroup";

    private static final String USE_TIMEOUT_PROPERTY = "useTimeout";

    private static final String DEFAULT_USER_NAME = "admin";

    private static final String DEFAULT_PASSWORD = "admin";

    static private final String logServiceName = LogService.class.getName();

    static private final String consoleServiceName = ConsoleService.class
            .getName();

    static private final String TELNET_INTERRUPT = "\377\364";

    private ConsoleService sConsole = null;

    private AcceptThread tAccept = null;

    BundleContext bc;

    boolean quit = false;

    Integer port;

    InetAddress address;

    Boolean login;

    Boolean requireUM;

    Boolean exitOnLogout;

    Boolean useTimeout;

    String requiredGroup;

    String forbiddenGroup;

    /*---------------------------------------------------------------------------*
     *        BundleActivator implementation
     *---------------------------------------------------------------------------*/

    // ==================== start ====================
    /**
     * * Called by the framework when this bundle is started. * *
     *
     * @param bc
     *            Bundle context. *
     */
    public void start(BundleContext bc) {
        this.bc = bc;

        // Get config
        Hashtable p = new Hashtable();
        p.put(Constants.SERVICE_PID, getClass().getName());
        bc.registerService(ManagedService.class.getName(), this, p);
    }

    // ==================== stop ====================
    /**
     * * Called by the framework when this bundle is stopped. * *
     *
     * @param bc
     *            Bundle context.
     */
    synchronized public void stop(BundleContext bc) {
        stopAccept();
    }

    /*---------------------------------------------------------------------------*
     *        ManagedService implementation
     *---------------------------------------------------------------------------*/

    // ==================== updated ====================
    /**
     * * Called by CM when it got this bundles configuration. * *
     *
     * @param bc
     *            Bundle context.
     */
    public synchronized void updated(Dictionary cfg)
            throws ConfigurationException, IllegalArgumentException {

        if (cfg == null) {
            stopAccept();
            cfg = new Hashtable();
        }
        Integer oldport = port;
        InetAddress oldAddress = address;
        Boolean oldUseTimeout = useTimeout;
        try {
            port = (Integer) cfg.get(PORT_PROPERTY);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for "
                    + PORT_PROPERTY);
        }
        String addressStr = null;
        try {
            addressStr = (String) cfg.get(ADDRESS_PROPERTY);
            if (addressStr != null) {
                addressStr = addressStr.trim();
                if ("".equals(addressStr))
                    address = null;
                else
                    address = InetAddress.getByName(addressStr);
            } else {
                address = null;
            }
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for "
                    + ADDRESS_PROPERTY);
        } catch (UnknownHostException e) {
            throw new IllegalArgumentException("Cannot resolve "
                    + ADDRESS_PROPERTY + ": " + addressStr);
        }
        try {
            login = (Boolean) cfg.get(LOGIN_PROPERTY);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for "
                    + LOGIN_PROPERTY);
        }
        try {
            requireUM = (Boolean) cfg.get(UM_PROPERTY);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for " + UM_PROPERTY);
        }
        try {
            exitOnLogout = (Boolean) cfg.get(EXIT_ON_LOGOUT_PROPERTY);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for "
                    + EXIT_ON_LOGOUT_PROPERTY);
        }
        try {
            requiredGroup = (String) cfg.get(REQUIRED_GROUP_PROPERTY);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for "
                    + REQUIRED_GROUP_PROPERTY);
        }
        try {
            forbiddenGroup = (String) cfg.get(FORBIDDEN_GROUP_PROPERTY);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for "
                    + FORBIDDEN_GROUP_PROPERTY);
        }
        try {
            useTimeout = (Boolean) cfg.get(USE_TIMEOUT_PROPERTY);
        } catch (ClassCastException e) {
            throw new IllegalArgumentException("Wrong type for "
                    + USE_TIMEOUT_PROPERTY);
        }
        if (port == null) {
            port = new Integer(8999);
        }
        if (port.intValue() < 0 || port.intValue() > 65535) {
            log(LogService.LOG_ERROR, "Illegal port number");
            throw new IllegalArgumentException("Illegal port number");
        }
        if (login == null) {
            login = new Boolean(true);
        }
        if (requireUM == null) {
            requireUM = new Boolean(false);
        }
        if (exitOnLogout == null) {
            exitOnLogout = new Boolean(false);
        }
        if ("".equals(requiredGroup)) {
            requiredGroup = null;
        }
        if ("".equals(forbiddenGroup)) {
            forbiddenGroup = null;
        }
        if (useTimeout == null) {
            useTimeout = new Boolean(true);
        }

        boolean addressChanged = (oldAddress == null && address != null)
                || (oldAddress != null && !oldAddress.equals(address));

        boolean useTimeoutChanged = (oldUseTimeout == null && !useTimeout
                .booleanValue())
                || (oldUseTimeout != null && !oldUseTimeout.equals(useTimeout));

        if (tAccept != null) {
            if (port.equals(oldport) && !addressChanged && !useTimeoutChanged) {
                // No need to restart
                return;
            }
            stopAccept();
        } else {
            // Get console service
            srefConsole = bc.getServiceReference(consoleServiceName);
            sConsole = (ConsoleService) bc.getService(srefConsole);
            if (sConsole == null) {
                log(LogService.LOG_ERROR, "Failed to get ConsoleService");
                throw new ConfigurationException(null,
                        "Failed to get ConsoleService");
            }
        }
        tAccept = new AcceptThread(sConsole);
        tAccept.setDaemon(false);
        quit = false;
        tAccept.start();
    }

    /**
     * * Utility to stop accept thread. *
     */

    private void stopAccept() {
        if (tAccept != null) {
            tAccept.quit();
            try {
                tAccept.join();
            } catch (InterruptedException ignore) {
            }
            tAccept = null;
        }
    }

    // ==================== log ====================
    /**
     * * Utility method used for logging. * *
     *
     * @param level
     *            Log level *
     * @param msg
     *            Log message
     */

    void log(int level, String msg) {
        try {
            ServiceReference srLog = bc.getServiceReference(logServiceName);
            if (srLog != null) {
                LogService sLog = (LogService) bc.getService(srLog);
                if (sLog != null) {
                    sLog.log(level, msg);
                }
                bc.ungetService(srLog);
            }
        } catch (IllegalStateException exp) {
            // if the thread has survied the stop of the bundle we get this
            // which we unfortunately has to ignore
        }
    }

    // ******************** AcceptThread ********************
    /**
     * * Waits for tcp connetions *
     */
    class AcceptThread extends Thread implements SessionListener {
        Hashtable sessions = new Hashtable();

        ConsoleService cs;

        ThreadGroup tg;

        AcceptThread(ConsoleService cs) {
            super(new ThreadGroup("Console TCP"), "Console TCP accept thread");
            this.cs = cs;
        }

        public void quit() {
            quit = true;
            log(LogService.LOG_INFO, "Stop listening for connections");
            for (Enumeration e = sessions.keys(); e.hasMoreElements();) {
                ((Session) e.nextElement()).close();
            }
            if (tg != null) {
                Thread[] threads = new Thread[tg.activeCount()];
                tg.enumerate(threads);
                for (int i = 0; i < threads.length; i++)
                    threads[i].interrupt();
                // BUG workaround! Connect so that socket is closed properly
                try {
                    (new Socket("localhost", port.intValue())).close();
                } catch (IOException ignore) {
                }
                try {
                    join();
                } catch (InterruptedException ignore) {
                }
            }
        }

        public void run() {
            ServerSocket serverSock = null;
            tg = getThreadGroup();
            try {
                log(LogService.LOG_INFO, "Listening for connections on port "
                        + port);
                serverSock = new ServerSocket(port.intValue(), 50, address);
                final SessionListener thisSessionListener = this; // used in
                                                                    // thread
                // below
                while (!quit) {
                    // Listen for incoming requests:
                    final Socket client = serverSock.accept();
                    if (quit) {
                        client.close();
                        break;
                    }

                    // Create new Thread to be able to handle simultaneous
                    // logins:
                    Thread startSessionThread = new Thread() {
                        public void run() {
                            String session = "console session "
                                    + client.getInetAddress().getHostName();
                            PrintWriter out = null;
                            try {
                                out = new PrintWriter(client.getOutputStream(),
                                        true);
                                InputStream is = client.getInputStream();
                                InputStreamReader isr = new InputStreamReader(
                                        is);
                                BufferedReader in = new BufferedReader(isr);

                                Authorization authorization = null;
                                if (login.booleanValue()) {
                                    LoginResult loginResult = doLogin(client);
                                    if (loginResult.loginOK) {
                                        authorization = loginResult.authorization;
                                    } else {
                                        return;
                                    }
                                }

                                // To avoid bug with hanging sockets we set
                                // SO_TIMEOUT
                                // addition: to avoid a bug in j9 it is now
                                // possible to
                                // configure the console to not use the timeout
                                if (useTimeout.booleanValue()) {
                                    client.setSoTimeout(3000);
                                }
                                Session s = cs.runSession(session, in, out);
                                Dictionary props = s.getProperties();
                                if (authorization != null) {
                                    props.put(Session.PROPERTY_AUTHORIZATION,
                                            authorization);
                                }
                                props.put(Session.PROPERTY_TCP, new Boolean(
                                        true));
                                if (exitOnLogout.booleanValue()) {
                                    props.put(Session.PROPERTY_EXIT_ON_LOGOUT,
                                            new Boolean(true));
                                    /*
                                     * Add properties to session to be able to
                                     * exit on login? if (requiredGroup != null) {
                                     * props.put(Session.PROPERTY_REQUIRED_GROUP,
                                     * requiredGroup); } if (forbiddenGroup !=
                                     * null) {
                                     * props.put(Session.PROPERTY_FORBIDDEN_GROUP,
                                     * forbiddenGroup); }
                                     */
                                }

                                sessions.put(s, client);
                                s.addSessionListener(thisSessionListener);
                                s.setInterruptString(TELNET_INTERRUPT);
                                log(LogService.LOG_INFO, "Started " + session);
                            } catch (IOException e) {
                                log(LogService.LOG_ERROR,
                                        "Failed to get input or output from socket to console session: "
                                                + e);
                                try {
                                    if (out != null
                                            && client.getSoTimeout() > 0) {
                                        out.println("\nSession timed out ("
                                                + client.getSoTimeout()
                                                + " ms).");
                                    }
                                } catch (SocketException ignore) {
                                }
                                try {
                                    client.close();
                                } catch (IOException e2) {
                                    log(LogService.LOG_ERROR,
                                            "Failed to close socket: " + e);
                                }
                            }
                        }
                    };
                    startSessionThread.start();

                } // while (!quit)
            } catch (Exception e) {
                if (!quit) {
                    log(LogService.LOG_ERROR,
                            "Communication error in ConsoleTcp: " + e);
                }
            }
            if (serverSock != null) {
                try {
                    serverSock.close();
                } catch (IOException ignore) {
                }
            }
        }

        // SessionListener

        public void sessionEnd(Session s) {
            try {
                ((Socket) sessions.remove(s)).close();
            } catch (IOException ignore) {
            }
        }

        LoginResult doLogin(Socket client) throws IOException {
            PrintWriter out = new PrintWriter(client.getOutputStream(), true);
            InputStream is = client.getInputStream();
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader in = new BufferedReader(isr);

            // To avoid bug with hanging sockets we set SO_TIMEOUT
            // 60 secs to login
            // addition: to avoid a bug in j9 it is now possible to configure
            // the console to not use the timeout
            if (useTimeout.booleanValue()) {
                client.setSoTimeout(60000);
            }

            out.print("login: ");
            out.flush();
            String userName = in.readLine();
            out.print("password: ");

            // Turn off echo:
            out.print((char) 255); // IAC
            out.print((char) 251); // WILL
            out.print((char) 1); // TELOPT_ECHO
            out.print((char) 0);
            out.flush();
            in.read();
            in.read();
            in.read(); // Read answer.

            // Read password:
            String password = in.readLine();

            // Turn echo back on:
            out.print((char) 255); // IAC
            out.print((char) 252); // WONT
            out.print((char) 1); // TELOPT_ECHO
            out.print((char) 0);
            out.flush();
            in.read();
            in.read();
            in.read(); // Read answer.
            out.println();

            Authorization authorization = null;
            boolean loginOK = false;
            ServiceReference sr = bc
                    .getServiceReference(PasswdAuthenticator.class.getName());
            if (sr == null) {
                if (requireUM.booleanValue()) {
                    log(LogService.LOG_WARNING,
                            "Failed to get PasswdAuthenticator reference. UM required but not present.");
                } else {
                    loginOK = (DEFAULT_USER_NAME.equals(userName) && DEFAULT_PASSWORD
                            .equals(password));
                }
            } else {
                PasswdAuthenticator pa = (PasswdAuthenticator) bc
                        .getService(sr);
                if (pa != null) {
                    PasswdSession ps = pa.createSession();
                    ps.setUsername(userName);
                    ps.setPassword(password);
                    ContextualAuthorization ca = null;
                    try {
                        ca = ps.getAuthorization();
                    } catch (IllegalStateException ex) {
                        log(LogService.LOG_WARNING,
                                "UserAdmin service not available.");
                    }
                    if (ca != null) {
                        if (requiredGroup != null && !ca.hasRole(requiredGroup)) {
                            loginOK = false;
                            log(
                                    LogService.LOG_INFO,
                                    userName
                                            + " tried to login, but did not have required role "
                                            + requiredGroup);
                        } else if (forbiddenGroup != null
                                && ca.hasRole(forbiddenGroup)) {
                            loginOK = false;
                            log(
                                    LogService.LOG_INFO,
                                    userName
                                            + " tried to login, but had forbidden role "
                                            + forbiddenGroup);
                        } else {
                            authorization = ca;
                            loginOK = true;
                            out.println("Logged in.");
                        }
                    }
                } else {
                    log(LogService.LOG_WARNING,
                            "Failed to get PasswdAuthenticator service.");
                }
                bc.ungetService(sr);
            }

            // Check if login successful:
            if (!loginOK) {
                out.println("Login failed!");
                client.close();
                log(LogService.LOG_INFO, "Login failed for " + userName);
                return new LoginResult();
            }

            // Set context:
            if (authorization instanceof ContextualAuthorization) {
                String authMethod = "passwd"; // TBD
                String inputPath = "tcp"; // TBD
                ((ContextualAuthorization) authorization).setIPAMContext(
                        inputPath, authMethod);
                Dictionary context = ((ContextualAuthorization) authorization)
                        .getContext();
                log(LogService.LOG_INFO, "User " + authorization.getName()
                        + " logged in, authentication context is " + context
                        + ".");
            } else if (authorization == null) {
                log(LogService.LOG_INFO, "Default user " + DEFAULT_USER_NAME
                        + " logged in.");
            } else {
                log(LogService.LOG_INFO, "User " + authorization.getName()
                        + " logged in.");
            }
            return new LoginResult(authorization);
        }

        class LoginResult {
            Authorization authorization;

            boolean loginOK;

            LoginResult() {
                loginOK = false;
            }

            LoginResult(Authorization authorization) {
                this.authorization = authorization;
                loginOK = true;
            }
        }

    }

}
TOP

Related Classes of org.knopflerfish.bundle.consoletcp.ConsoleTcp$AcceptThread$LoginResult

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.