2N/A#
2N/A# CDDL HEADER START
2N/A#
2N/A# The contents of this file are subject to the terms of the
2N/A# Common Development and Distribution License (the "License").
2N/A# You may not use this file except in compliance with the License.
2N/A#
2N/A# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A# or http://www.opensolaris.org/os/licensing.
2N/A# See the License for the specific language governing permissions
2N/A# and limitations under the License.
2N/A#
2N/A# When distributing Covered Code, include this CDDL HEADER in each
2N/A# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A# If applicable, add the following below this CDDL HEADER, with the
2N/A# fields enclosed by brackets "[]" replaced with your own identifying
2N/A# information: Portions Copyright [yyyy] [name of copyright owner]
2N/A#
2N/A# CDDL HEADER END
2N/A#
2N/A
2N/A#
2N/A# Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
2N/A#
2N/A
2N/A"""util - RAD connection oriented utilities
2N/A
2N/AThis module facilitates communication with RAD instances via a number of
2N/Aalternative transports.
2N/A
2N/A"""
2N/Aimport socket
2N/Aimport ssl
2N/Aimport getpass
2N/Aimport subprocess
2N/Aimport os
2N/Aimport rad.client as rad
2N/A
2N/A#
2N/A# Paths for authenticated/unauthenticated AF_UNIX socket
2N/A#
2N/ARAD_PATH_AFUNIX_AUTH = "/system/volatile/rad/radsocket"
2N/ARAD_PATH_AFUNIX_UNAUTH = "/system/volatile/rad/radsocket-unauth"
2N/ARAD_PORT_TLS = 12302
2N/A
2N/A#
2N/A# Make pipes quack enough like a socket to satisfy RecordMarkingSocket
2N/A#
2N/Aclass ProcessPseudoSocket(object):
2N/A """ A class which implements a sufficient amount of the socket.socket
2N/A interface to allow a pipe to communicate like a
2N/A rad.recordmarking.RecordMarkingSocket. """
2N/A def __init__(self, process):
2N/A self._process = process
2N/A self._in = process.stdin
2N/A self._out = process.stdout
2N/A
2N/A def __enter__(self):
2N/A return self
2N/A
2N/A def __exit__(self, type, value, tb):
2N/A self.close()
2N/A return False
2N/A
2N/A def recv(self, n):
2N/A #
2N/A # If the file is unbuffered, it reads one byte at a time.
2N/A # If the file is buffered, it waits until all n bytes are
2N/A # read before returning.
2N/A # Neither is acceptable, so we use os.read.
2N/A #
2N/A return os.read(self._out.fileno(), n)
2N/A
2N/A def sendall(self, buf):
2N/A r = self._in.write(buf)
2N/A self._in.flush()
2N/A return r
2N/A
2N/A def close(self):
2N/A #
2N/A # We would prefer to simply close our end of the pipe
2N/A # and let rad die on its own, but reaching in and doing
2N/A # that to a pipe inside a subprocess might defy its
2N/A # expectations. Instead we terminate the subprocess.
2N/A #
2N/A self._process.terminate()
2N/A
2N/A#
2N/A# Make a zonesbridge.IO object quack enough like a socket to satisfy
2N/A# RecordMarkingSocket
2N/A#
2N/Aclass _ZonesBridgePseudoSocket(object):
2N/A """ A class which implements a sufficient amount of the socket.socket
2N/A interface to allow a zonesbridge.IO object communicate like a
2N/A rad.recordmarking.RecordMarkingSocket. """
2N/A def __init__(self, zio, zone, user = None):
2N/A self._zio = zio
2N/A self._token = zio.openRad(zone, user)
2N/A
2N/A def __enter__(self):
2N/A return self
2N/A
2N/A def __exit__(self, type, value, tb):
2N/A self.close()
2N/A return False
2N/A
2N/A def recv(self, n):
2N/A return self._zio.read(self._token, n)
2N/A
2N/A def sendall(self, buf):
2N/A self._zio.write(self._token, buf)
2N/A
2N/A def close(self):
2N/A self._zio.close(self._token)
2N/A
2N/A#
2N/A# Map a path given the specified root directory
2N/A#
2N/Adef _map_path(root, path):
2N/A if root is not None and path[0] == '/':
2N/A return root + path;
2N/A return path
2N/A
2N/A#
2N/A# build the rad commandline command with the given options.
2N/A#
2N/Adef build_rad_cmd(modules = None, debug = False, root = None, auxargs = None):
2N/A """
2N/A Get a List[string] suitable to use when spawning a RAD process
2N/A via subprocess.Popen().
2N/A
2N/A Arguments:
2N/A
2N/A modules List[string], a list of modules to load
2N/A debug boolean, run the spawned RAD process in debug mode
2N/A root string, root directory to use for the spawned RAD
2N/A process
2N/A auxargs List[string], additional argument to supply to the
2N/A spawned RAD process
2N/A
2N/A Returns:
2N/A
2N/A List[string]: A list containing a command plus arguments
2N/A """
2N/A cmd = [_map_path(root, "/usr/lib/rad/rad"),
2N/A "-M", _map_path(root, "/usr/lib/rad/transport/mod_pipe.so"),
2N/A "-M", _map_path(root, "/usr/lib/rad/protocol/mod_proto_rad.so")]
2N/A
2N/A if modules is not None:
2N/A for module in modules:
2N/A cmd.extend(["-M", _map_path(root, module)])
2N/A else:
2N/A cmd.extend(["-m", _map_path(root, "/usr/lib/rad/module"),
2N/A "-m", _map_path(root, "/usr/lib/rad/site-modules")])
2N/A
2N/A if debug:
2N/A cmd.extend(["-d"])
2N/A
2N/A if auxargs is not None:
2N/A cmd.extend(auxargs)
2N/A
2N/A cmd.extend(["-t", "stdin:exit"])
2N/A return cmd
2N/A
2N/A
2N/A#
2N/A# Connect to a privately spawned copy of rad
2N/A# Defaults to loading all system modules if a list of modules isn't provided
2N/A#
2N/Adef _get_private_transport(modules = None, debug = False, env = None,
2N/A root = None, auxargs = None):
2N/A """
2N/A Get a transport suitable for connecting to a privately spawned
2N/A RAD process.
2N/A
2N/A Arguments:
2N/A
2N/A modules List[string], a list of modules to load
2N/A debug boolean, run the spawned RAD process in debug mode
2N/A env Dictionary[string, string], environment for the
2N/A spawned RAD process
2N/A root string, root directory to use for the spawned RAD
2N/A process
2N/A auxargs List[string], additional argument to supply to the
2N/A spawned RAD process
2N/A
2N/A Returns:
2N/A
2N/A ProcessPseudoSocket: a "socket" for a spawned RAD process
2N/A """
2N/A cmd = build_rad_cmd(modules = modules, debug = debug, root = root,
2N/A auxargs = auxargs)
2N/A p = subprocess.Popen(cmd, 0, None, subprocess.PIPE, subprocess.PIPE,
2N/A env = env)
2N/A return ProcessPseudoSocket(p)
2N/A
2N/A#
2N/A# Use _get_private_transport() to return a RADConnection
2N/A#
2N/Adef connect_private(modules = None, debug = False, env = None, root = None,
2N/A auxargs = None, locale = None):
2N/A """
2N/A Connect to a privately spawned instance of RAD using a pipe.
2N/A
2N/A Arguments:
2N/A
2N/A modules List[string], a list of modules to load
2N/A debug boolean, run the spawned RAD process in debug mode
2N/A env Dictionary[string, string], environment for the
2N/A spawned RAD process
2N/A root string, root directory to use for the spawned RAD
2N/A process
2N/A auxargs List[string], additional argument to supply to the
2N/A spawned RAD process
2N/A locale string, locale
2N/A
2N/A Returns:
2N/A
2N/A rad.client.RadConnection: a connection to RAD
2N/A """
2N/A p = _get_private_transport(modules, debug, env, root, auxargs)
2N/A return rad.RadConnection(p, locale = locale)
2N/A
2N/A#
2N/A# Connect to the local rad daemon via the standard unix domain socket
2N/A#
2N/Adef _get_unix_transport(path = RAD_PATH_AFUNIX_AUTH):
2N/A """
2N/A Get a transport suitable for connecting to a local RAD instance.
2N/A
2N/A Arguments:
2N/A
2N/A path string, path to the socket
2N/A
2N/A Returns:
2N/A
2N/A socket.socket: a Unix Domain(AF_UNIX) socket
2N/A """
2N/A s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM);
2N/A s.connect(path)
2N/A return s
2N/A
2N/A#
2N/A# Use _get_unix_transport() to return a RADConnection
2N/A#
2N/Adef connect_unix(path = RAD_PATH_AFUNIX_AUTH, locale = None):
2N/A """
2N/A Connect to a RAD instance over a Unix Domain Socket.
2N/A
2N/A Arguments:
2N/A
2N/A path string, path to the socket
2N/A locale string, locale
2N/A
2N/A Returns:
2N/A
2N/A rad.client.RadConnection: a connection to RAD
2N/A """
2N/A """ Get a UnixDomainSocket based RadConnection to RAD on
2N/A the local host at path. """
2N/A s = _get_unix_transport(path)
2N/A return rad.RadConnection(s, locale = locale)
2N/A
2N/A#
2N/A# Connect to a remote rad daemon at the standard port
2N/A#
2N/Adef _get_tls_transport(host, port = RAD_PORT_TLS):
2N/A """
2N/A Get a TLS transport suitable for connecting to a remote host.
2N/A
2N/A Arguments:
2N/A
2N/A host string, target host
2N/A port int, target port
2N/A
2N/A Returns:
2N/A
2N/A ssl.SSLSocket: a TLS based socket
2N/A """
2N/A s = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
2N/A s.connect((host, port))
2N/A return ssl.wrap_socket(s)
2N/A
2N/A#
2N/A# Use _get_tls_transport() to return a RADConnection
2N/A#
2N/Adef connect_tls(host, port = RAD_PORT_TLS, locale = None):
2N/A """
2N/A Connect to a RAD instance over TLS.
2N/A
2N/A Arguments:
2N/A
2N/A host string, target host
2N/A port int, target port
2N/A locale string, locale
2N/A
2N/A Returns:
2N/A
2N/A rad.client.RadConnection: a connection to RAD
2N/A """
2N/A s = _get_tls_transport(host, port)
2N/A return rad.RadConnection(s, locale = locale)
2N/A
2N/A#
2N/A# Connect to a non-global zone's local (unix domain socket) rad daemon, through
2N/A# an established rad connection to its global zone.
2N/A#
2N/Adef _get_zone_transport(rc, zone, user = None):
2N/A """
2N/A Get a transport suitable for connecting to a local zone.
2N/A
2N/A Arguments:
2N/A
2N/A rc rad.client.RadConnection, Connection to a global zone
2N/A zone string, Name of a local zone
2N/A user string, user name
2N/A
2N/A Returns:
2N/A
2N/A _ZonesBridgePseudoSocket: a "socket" for a local zone
2N/A """
2N/A # Retrieve the zonesbridge.IO object
2N/A zio_name = rad.Name("com.oracle.solaris.rad.zonesbridge",
2N/A [("type", "IO")])
2N/A # Get a native-looking python object that throws RAD exceptions
2N/A zio = rc.get_object(zio_name)
2N/A
2N/A return _ZonesBridgePseudoSocket(zio, zone, user)
2N/A
2N/A#
2N/A# Use _get_zone_transport() to return a RADConnection
2N/A#
2N/Adef connect_zone(rc, zone, user = None, locale = None):
2N/A """
2N/A Connect to a RAD instance in a local zone.
2N/A
2N/A Arguments:
2N/A
2N/A rc rad.client.RadConnection, Connection to a global zone
2N/A zone string, Name of a local zone
2N/A user string, user name
2N/A locale string, locale
2N/A
2N/A Returns:
2N/A
2N/A rad.client.RadConnection: a connection to RAD
2N/A """
2N/A s = _get_zone_transport(rc, zone, user)
2N/A return rad.RadConnection(s, locale = locale)
2N/A
2N/A#
2N/A# Fetches and caches a handle to the authentication object.
2N/A# Supplies some convenience routines built on top of it.
2N/A#
2N/Aclass RadAuth(object):
2N/A """ Authentication utility class. """
2N/A AUTH_NAME = rad.Name("com.oracle.solaris.rad.pam",
2N/A [("type", "Authentication")])
2N/A
2N/A def __init__(self, rc):
2N/A self._auth = rc.get_object(self.AUTH_NAME)
2N/A
2N/A def handle(self):
2N/A return self._auth
2N/A
2N/A #
2N/A # Perform a terminal-interactive PAM login
2N/A # Currently not recommended due to bug in Python's getpass.getpass()
2N/A #
2N/A def pam_login(self, user = None):
2N/A """
2N/A Perform a terminal-interactive PAM login.
2N/A
2N/A Arguments:
2N/A
2N/A user string, user name
2N/A
2N/A Returns:
2N/A
2N/A """
2N/A if not user:
2N/A user = raw_input("Username: ")
2N/A blk = self._auth.login("C", user);
2N/A types = rad.get_types(self._auth)
2N/A BlockType = types.BlockType
2N/A MsgType = types.MsgType
2N/A while True:
2N/A resp = []
2N/A if blk.type == BlockType.SUCCESS:
2N/A break
2N/A if blk.type == BlockType.ERROR:
2N/A raise Exception("Authentication Failed")
2N/A for m in blk.messages:
2N/A if m.style == MsgType.PROMPT_ECHO_OFF:
2N/A r = getpass.getpass(m.message)
2N/A resp.append(r)
2N/A elif m.style == MsgType.PROMPT_ECHO_ON:
2N/A r = raw_input(m.message)
2N/A resp.append(r)
2N/A elif m.style == MsgType.TEXT_INFO:
2N/A print m.message
2N/A elif m.style == MsgType.ERROR_MSG:
2N/A print "ERROR: %s" % m.message
2N/A blk = self._auth.submit(resp)
2N/A
2N/A self._auth.complete()