usr/src/lib/pyrad/util.py
author devjani.ray@oracle.com <devjani.ray@oracle.com>
Thu, 02 Feb 2012 11:25:01 -0500
changeset 798 a7deccd6492f
parent 771 d8e492629de3
child 804 806e7738af00
permissions -rw-r--r--
CR 7121230 Rad unix transport paths should be created under /system/volatile/rad/

#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
#

import socket
import ssl
import getpass
import subprocess
import os
import rad.client as rad
import rad.adaptor as adapt

#
# Default path for authenticated AF_UNIX socket
#
_RAD_PATH_AFUNIX_AUTH = "/system/volatile/rad/radsocket"

#
# Make pipes quack enough like a socket to satisfy RecordMarkingSocket
#
class SocketLike(object):
	def __init__(self, process):
		self._process = process
		self._in = process.stdin
		self._out = process.stdout

	def recv(self, n):
		#
		# If the file is unbuffered, it reads one byte at a time.
		# If the file is buffered, it waits until all n bytes are
		# read before returning.
		# Neither is acceptable, so we use os.read.
		#
		return os.read(self._out.fileno(), n)

	def sendall(self, buf):
		r = self._in.write(buf)
		self._in.flush()
		return r

	def close(self):
		#
		# We would prefer to simply close our end of the pipe
		# and let rad die on its own, but reaching in and doing
		# that to a pipe inside a subprocess might defy its
		# expectations.  Instead we terminate the subprocess.
		#
		self._process.terminate()


#
# Map a path given the specified root directory
#
def map_path(root, path):
	if root != None and path[0] == '/':
		return root + path;
	return path

#
# Connect to a privately spawned copy of rad
# Defaults to loading all system modules if a list of modules isn't provided
#
def connect_private(modules = None, debug = False, env = None, root = None,
    auxargs = None, locale = None):
	args = [ map_path(root, "/usr/lib/rad/rad"),
	    "-M", map_path(root, "/usr/lib/rad/transport/mod_xport_pipe.so"),
	    "-M", map_path(root, "/usr/lib/rad/protocol/mod_proto_rad.so"),
	    "-t", "stdin:exit" ]

	if modules:
		for m in modules:
			args.extend([ "-M", map_path(root, m) ])
	else:
		args.extend([ "-m", map_path(root, "/usr/lib/rad/module") ])

	if debug:
		args.extend([ "-d" ])

	if auxargs:
		args.extend(auxargs)

	p = subprocess.Popen(args, 0, None, subprocess.PIPE, subprocess.PIPE,
	    env = env)
	return rad.RadConnection(SocketLike(p), locale = locale)
	
#
# Connect to a remote rad daemon using SSH
# The remote system must have netcat (/usr/bin/nc) installed
#
def connect_ssh(host, user = None, locale = None):
	if user:
		host = "%s@%s" % (user, host)
	p = subprocess.Popen(["/usr/bin/ssh", "-q", "-e", "none",
	    "-o", "BatchMode yes", host, "/usr/lib/rad/radpipe" ],
	    0, None, subprocess.PIPE, subprocess.PIPE)
	return rad.RadConnection(SocketLike(p), locale = locale)

#
# Connect to the local rad daemon via the standard unix domain socket
#
def connect_local(path = _RAD_PATH_AFUNIX_AUTH, locale = None):
	s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM);
	s.connect(path)
	return rad.RadConnection(s, locale = locale)

#
# Connect to a remote rad daemon at the standard port
#
def connect_ssl(host, port = 12302, locale = None):
	s = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
	s.connect((host, port))
	s = ssl.wrap_socket(s)
	return rad.RadConnection(s, locale = locale)

# 
# Fetches and caches a handle to the authentication object.
# Supplies some convenience routines built on top of it.
#
class RadAuth(object):
	AUTH_NAME = rad.Name("org.opensolaris.os.rad",
	    [("type", "authentication")])

	def __init__(self, rc):
		self._auth = adapt.Adaptor(rc.get_object(self.AUTH_NAME))

	def handle(self):
		return self._auth

	#
	# Perform a terminal-interactive PAM login
	# Currently not recommended due to bug in Python's getpass.getpass()
	#
	def pam_login(self, user = None):
		if not user:
			user = raw_input("Username: ")
		blk = self._auth.login("C", user);
		types = self._auth._object.types
		BlockType = types.BlockType
		MsgType = types.MsgType
		while True:
			resp = []
			if blk.type == BlockType.success:
				break
			if blk.type == BlockType.error:
				raise Exception("Authentication Failed")
			for m in blk.messages:
				if m.style == MsgType.prompt_echo_off:
					r = getpass.getpass(m.message)
					resp.append(r)
				elif m.style == MsgType.prompt_echo_on:
					r = raw_input(m.message)
					resp.append(r)
				elif m.style == MsgType.text_info:
					print m.message
				elif m.style == MsgType.error_msg:
					print "ERROR: %s" % m.message
			blk = self._auth.submit(resp)

		self._auth.complete()