18722 - Add tests for authentication and PAM conversation processing.
--- a/usr/src/Makefile.env Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/Makefile.env Fri Jul 29 17:37:59 2011 -0400
@@ -43,6 +43,7 @@
PROTO_AUTOSTART = $(ROOT)/usr/share/gnome/autostart
PROTO_BIN = $(ROOT)/usr/bin
PROTO_DOC = $(ROOT)/usr/share/lib/java/javadoc
+PROTO_ETC_UATTR.D = $(ROOT)/etc/user_attr.d
PROTO_ETC_VPANELS = $(ROOT)/etc/vpanels
PROTO_INCLUDE = $(ROOT)/usr/include
PROTO_LIB = $(ROOT)/usr/lib
--- a/usr/src/Makefile.targ Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/Makefile.targ Fri Jul 29 17:37:59 2011 -0400
@@ -67,6 +67,7 @@
# Proto files, remove CDDL
$(PROTO_BIN)/% $(PROTO_RAD)/% := FILEMODE = 0555
$(PROTO_BIN)/% \
+ $(PROTO_ETC_UATTR.D)/% \
$(PROTO_ETC_VPANELS)/% \
$(PROTO_MFS_NETWORK)/% \
$(PROTO_MFS_SYSTEM)/% \
--- a/usr/src/cmd/conv_test/Makefile Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/cmd/conv_test/Makefile Fri Jul 29 17:37:59 2011 -0400
@@ -28,6 +28,7 @@
$(PROTO_MTD)/rad_conv_test := FILEMODE = 0555
install: $(PROTO_MTD)/rad_conv_test \
- $(PROTO_MFS_SYSTEM)/rad-conv-test.xml
+ $(PROTO_MFS_SYSTEM)/rad-conv-test.xml \
+ $(PROTO_ETC_UATTR.D)/rad-test
include $(SRC)/Makefile.targ
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/cmd/conv_test/rad-test Fri Jul 29 17:37:59 2011 -0400
@@ -0,0 +1,27 @@
+#
+# 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) 2011, Oracle and/or its affiliates. All rights reserved.
+#
+
+radtest_rolepass::::type=role
+radtest_rolenopass::::type=role
+radtest_hasroles::::type=normal;roles=radtest_rolepass,radtest_rolenopass
--- a/usr/src/cmd/rad/daemon/rad-test.xml Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/cmd/rad/daemon/rad-test.xml Fri Jul 29 17:37:59 2011 -0400
@@ -89,7 +89,7 @@
</astring_list>
</property>
<propval name='debug' type='boolean' value='false' />
- <propval name='timeout' type='integer' value='180' />
+ <propval name='timeout' type='integer' value='3' />
<propval name='pam_service' type='astring' value='radtest' />
</property_group>
--- a/usr/src/lib/conv_test/common/conv_test.c Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/lib/conv_test/common/conv_test.c Fri Jul 29 17:37:59 2011 -0400
@@ -40,10 +40,14 @@
#define USER_TRAILING "radtest_trailing"
#define USER_SERIAL "radtest_serial"
#define USER_PARALLEL "radtest_parallel"
+#define USER_HASROLES "radtest_hasroles"
+
+#define ROLE_PASS "radtest_rolepass"
+#define ROLE_NOPASS "radtest_rolenopass"
#define PROMPT_SIMPLE "Password:"
#define PROMPT_TRAILING "Thank you!"
-#define PROMPT_NEW "New password:"
+#define PROMPT_NEW "New Password:"
#define PROMPT_A "Password A:"
#define PROMPT_B "Password B:"
@@ -53,13 +57,16 @@
#define PASS_LAME "lamepass"
#define PASS_A "Apass"
#define PASS_B "Bpass"
+#define PASS_ROLE "rolepass"
+/*ARGSUSED*/
int
pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
return (PAM_IGNORE);
}
+/*ARGSUSED*/
int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
@@ -73,11 +80,25 @@
pam_get_item(pamh, PAM_USER, (void **)&username) != PAM_SUCCESS)
return (PAM_AUTH_ERR);
- if (strcmp(username, USER_NOPASS) == 0) {
+ if (strcmp(username, USER_NOPASS) == 0 ||
+ strcmp(username, ROLE_NOPASS) == 0) {
return (PAM_SUCCESS);
+ } else if (strcmp(username, ROLE_PASS) == 0) {
+ msg[0].msg_style = PAM_PROMPT_ECHO_ON;
+ msg[0].msg = PROMPT_SIMPLE;
+ int err = conv->conv(1, &pmsg, &res, conv->appdata_ptr);
+ if (res != NULL) {
+ if (err == PAM_SUCCESS && res->resp) {
+ if (strcmp(res->resp, PASS_ROLE) == 0)
+ result = PAM_SUCCESS;
+ free(res->resp);
+ }
+ free(res);
+ }
} else if (strcmp(username, USER_SIMPLE) == 0 ||
strcmp(username, USER_STALE) == 0 ||
- strcmp(username, USER_EXPIRED) == 0) {
+ strcmp(username, USER_EXPIRED) == 0 ||
+ strcmp(username, USER_HASROLES) == 0) {
msg[0].msg_style = PAM_PROMPT_ECHO_ON;
msg[0].msg = PROMPT_SIMPLE;
int err = conv->conv(1, &pmsg, &res, conv->appdata_ptr);
@@ -102,12 +123,12 @@
free(res);
}
if (result == PAM_SUCCESS) {
- result == PAM_AUTH_ERR;
+ result = PAM_AUTH_ERR;
msg[0].msg_style = PAM_TEXT_INFO;
msg[0].msg = PROMPT_TRAILING;
err = conv->conv(1, &pmsg, &res, conv->appdata_ptr);
if (err == PAM_SUCCESS)
- result == PAM_SUCCESS;
+ result = PAM_SUCCESS;
}
} else if (strcmp(username, USER_SERIAL) == 0) {
msg[0].msg_style = PAM_PROMPT_ECHO_ON;
@@ -122,7 +143,7 @@
free(res);
}
if (result == PAM_SUCCESS) {
- result == PAM_AUTH_ERR;
+ result = PAM_AUTH_ERR;
msg[0].msg_style = PAM_PROMPT_ECHO_ON;
msg[0].msg = PROMPT_B;
err = conv->conv(1, &pmsg, &res, conv->appdata_ptr);
@@ -156,6 +177,7 @@
return (result);
}
+/*ARGSUSED*/
int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
@@ -170,6 +192,7 @@
return (PAM_SUCCESS);
}
+/*ARGSUSED*/
int
pam_sm_chauthtok(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
@@ -184,7 +207,7 @@
return (PAM_AUTH_ERR);
msg[0].msg_style = PAM_PROMPT_ECHO_ON;
- msg[0].msg = PASS_NEW;
+ msg[0].msg = PROMPT_NEW;
int err = conv->conv(1, &pmsg, &res, conv->appdata_ptr);
if (res != NULL) {
if (err == PAM_SUCCESS && res->resp) {
--- a/usr/src/pkg/manifests_dev/system-management-rad-test.p5m Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/pkg/manifests_dev/system-management-rad-test.p5m Fri Jul 29 17:37:59 2011 -0400
@@ -37,6 +37,13 @@
user username=radtest_trailing ftpuser=false gcos-field="rad test" group=other
user username=radtest_serial ftpuser=false gcos-field="rad test" group=other
user username=radtest_parallel ftpuser=false gcos-field="rad test" group=other
+user username=radtest_rolepass ftpuser=false gcos-field="rad test" group=other
+user username=radtest_rolenopass ftpuser=false gcos-field="rad test" group=other
+user username=radtest_hasroles ftpuser=false gcos-field="rad test" group=other
+
+dir path=/etc group=sys
+dir path=/etc/user_attr.d group=sys
+file path=/etc/user_attr.d/rad-test group=sys mode=0644
dir path=lib
dir path=lib/svc
--- a/usr/src/test/java/Makefile Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/test/java/Makefile Fri Jul 29 17:37:59 2011 -0400
@@ -32,6 +32,7 @@
DEPENDENCY_JARS = $(PROTO_RAD_JAVA)/rad.jar
DEPENDENCY_JARS += $(PROTO_RAD_JAVA)/adr.jar
DEPENDENCY_JARS += $(PROTO_RAD_JAVA)/afunix.jar
+DEPENDENCY_JARS += $(PROTO_VP_DIR)/scf-common.jar
DEPENDENCY_JARS += /usr/share/lib/java/junit.jar
JAVA_OPTS = -g -Xlint -Xlint:-serial
--- a/usr/src/test/java/build.xml Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/test/java/build.xml Fri Jul 29 17:37:59 2011 -0400
@@ -37,7 +37,8 @@
<property name="path.adr" location="${path.rad.java}/adr.jar" />
<property name="path.rad" location="${path.rad.java}/rad.jar" />
<property name="path.util" location="${path.rad.java}/afunix.jar" />
-
+ <property name="path.scf"
+ location="${path.proto}/usr/share/vpanels/scf-common.jar" />
<path id="srcroots">
<pathelement location="${path.src}" />
<pathelement location="${path.gen}" />
@@ -47,6 +48,7 @@
<pathelement location="${path.adr}" />
<pathelement location="${path.rad}" />
<pathelement location="${path.util}" />
+ <pathelement location="${path.scf}" />
<pathelement location="/usr/share/lib/java/junit.jar" />
</path>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/java/src/client/PAMAuthTest.java Fri Jul 29 17:37:59 2011 -0400
@@ -0,0 +1,51 @@
+/*
+ * 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) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package client;
+
+import org.junit.Before;
+import org.junit.Test;
+import testutil.Desc;
+
+import static org.junit.Assert.*;
+
+public class PAMAuthTest extends PAMTestBase {
+ @Before
+ public void setUp() throws Exception {
+ assertTrue(isRadReady(true)); // auth
+ }
+
+ @Test
+ @Desc("Connecting to the container with authentication correctly" +
+ " reports the user you authenticated as.")
+ public void authUserCorrect() throws Exception {
+ String username = System.getProperty("user.name");
+ String serverUserName = bean_.getuser();
+
+ assertNotNull(username);
+ assertNotNull(serverUserName);
+ assertEquals(username, serverUserName);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/java/src/client/PAMConvTest.java Fri Jul 29 17:37:59 2011 -0400
@@ -0,0 +1,639 @@
+/*
+ * 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) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package client;
+
+import java.io.IOException;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.opensolaris.os.rad.ObjectException;
+import org.opensolaris.os.rad.test.RadRequestMXBean;
+import org.opensolaris.os.rad.api.pam.Block;
+import org.opensolaris.os.rad.api.pam.BlockType;
+import org.opensolaris.os.rad.api.pam.Message;
+import org.opensolaris.os.rad.api.pam.MsgType;
+import testutil.Desc;
+
+import static org.junit.Assert.*;
+
+public class PAMConvTest extends PAMTestBase {
+ static final String USCE_CLASSNAME =
+ "com.oracle.solaris.afunix.UnixSocketClosedException";
+ static final int CONN_TO = 3;
+
+ @Before
+ public void setUp() throws Exception {
+ assertTrue(isRadReady(false)); // unauth
+ }
+
+ @Test
+ @Desc("Connecting to the container without authentication correctly" +
+ " reports that you aren't authenticated.")
+ public void authUserUserNone() throws Exception {
+ String serverUserName = bean_.getuser();
+
+ assertNull(serverUserName);
+ }
+
+ @Test
+ @Desc("Connecting to the container without authentication correcctly" +
+ " reports you have no roles availables.")
+ public void authRolesNone() throws Exception {
+ List<String>roles = bean_.getroles();
+
+ assertNotNull(roles);
+ assertEquals(0, roles.size());
+ }
+
+ @Test
+ @Desc("Connecting to the container with authentication correctly" +
+ " resports the roles available to the authenticated user.")
+ public void authRolesCorrect() throws Exception {
+ String username = "radtest_hasroles";
+ String prompt = "Password:";
+ String password = "simplepass";
+ String myroles[] =
+ new String[] {"radtest_rolepass", "radtest_rolenopass"};
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message>messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(prompt, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(password.toCharArray());
+
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(BlockType.success, block.getType());
+
+ List<String>roles = bean_.getroles();
+ assertNotNull(roles);
+ assertEquals(myroles.length, roles.size());
+
+ // the rolenames returned are the expected ones.
+ assertTrue(roles.contains(myroles[0]) && roles.contains(myroles[1]));
+ }
+
+ @Test
+ @Desc("Authentication as a bogus user failes immediately.")
+ public void authLoginBogusUser() throws Exception {
+ String username = "bogususer";
+ Block block = bean_.login(locale_, username);
+
+ assertNotNull(block);
+ assertEquals(BlockType.error, block.getType());
+ assertNull(bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user that doesn't have password completes" +
+ " successfully.")
+ public void authLoginNoPassword() throws Exception {
+ String username = "radtest_nopass";
+ Block block = bean_.login(locale_, username);
+
+ assertNotNull(block);
+ assertEquals(BlockType.success, block.getType());
+ assertEquals(username, bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user that has password completes successfully")
+ public void authLoginPassword() throws Exception {
+ String username = "radtest_simple";
+ String prompt = "Password:";
+ String password = "simplepass";
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message> messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(prompt, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(password.toCharArray());
+
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(BlockType.success, block.getType());
+
+ assertEquals(username, bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authntication as user that has password fails when bad password" +
+ " is provided")
+ public void authLoginPasswordFail() throws Exception {
+ String username = "radtest_simple";
+ String prompt = "Password:";
+ String password = "badpassword";
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message> messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(prompt, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(password.toCharArray());
+
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(BlockType.error, block.getType());
+ assertNull(bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user that has password completes successfully," +
+ " with trailing message")
+ public void authLoginPasswordTrailing() throws Exception {
+ String username = "radtest_trailing";
+ String prompt = "Password:";
+ String password = "simplepass";
+ String trailingPrompt = "Thank you!";
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message> messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(prompt, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(password.toCharArray());
+
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+
+ assertEquals(MsgType.text_info, messages.get(0).getStyle());
+ assertEquals(trailingPrompt, messages.get(0).getMessage());
+
+ resp = new ArrayList<char []>();
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(BlockType.success, block.getType());
+ assertEquals(username, bean_.getuser());
+ }
+
+ /**
+ * authenticate a user using multiple prompts in serial.
+ * @param aPass - password for the first prompt.
+ * @param bPass - password for the second prompt.
+ * @return boolean - true if successully authenticated, otherwise false.
+ */
+ private boolean authLoginMultipleSerial(String aPass, String bPass)
+ throws Exception {
+ String username = "radtest_serial";
+ String promptA = "Password A:";
+ String promptB = "Password B:";
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message> messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(promptA, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(aPass.toCharArray());
+
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ if (block.getType() != BlockType.conv)
+ return false;
+
+ messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(promptB, messages.get(0).getMessage());
+
+ resp = new ArrayList<char[]>();
+ resp.add(bPass.toCharArray());
+ block = bean_.submit(resp);
+ assertNotNull(block);
+
+ return (block.getType() == BlockType.success);
+ }
+
+ @Test
+ @Desc("Authentication as user using mulitple prompts in serial completes" +
+ " successfully.")
+ public void authLoginMultipleSerial() throws Exception {
+ assertTrue(authLoginMultipleSerial("Apass", "Bpass"));
+ assertEquals("radtest_serial", bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user using multiple prompts in serial fails" +
+ " when the first password is wrong")
+ public void authLoginMutipleSerialA() throws Exception {
+ assertFalse(authLoginMultipleSerial("ABadpass", "Bpass"));
+ assertNull(bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user using multiple prompts in serial fails" +
+ " when the second password is wrong")
+ public void authLoginMultipleSerialB() throws Exception {
+ assertFalse(authLoginMultipleSerial("Apass", "BBadpass"));
+ assertNull(bean_.getuser());
+ }
+
+ /**
+ * authenticate a user using multiple prompts in parallel.
+ * @param passowrd - a list a passwords to used as response.
+ * @return boolean - true if successful, otherwise false.
+ */
+ private boolean authLoginMultipleParallel(List<char[]>passList)
+ throws Exception {
+ String username = "radtest_parallel";
+ String [] prompts = new String[] {"Password A:", "Password B:"};
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message>messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(2, messages.size());
+ assertEquals(prompts[0], messages.get(0).getMessage());
+ assertEquals(prompts[1], messages.get(1).getMessage());
+
+ block = bean_.submit(passList);
+ assertNotNull(block);
+
+ return (block.getType() == BlockType.success);
+ }
+
+ @Test
+ @Desc("Authnetication as user using multiple prompts simultaneously" +
+ " completes successfully.")
+ public void authLoginMultipleParallel() throws Exception {
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add("Apass".toCharArray());
+ resp.add("Bpass".toCharArray());
+
+ assertTrue(authLoginMultipleParallel(resp));
+ assertEquals("radtest_parallel", bean_.getuser());
+ }
+
+ @Test
+ @Desc("Autentication as user using multiple prompts simultaneously" +
+ " fails when passwords are wrong.")
+ public void authLoginMultipleParallelFail() throws Exception {
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add("ABadPass".toCharArray());
+ resp.add("BBadPass".toCharArray());
+
+ assertFalse(authLoginMultipleParallel(resp));
+ assertNull(bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user using multiple prompts simultaneously" +
+ " fails when only one password is provided.")
+ public void authLoginMultipleParallelFailShort() throws Exception {
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add("Apass".toCharArray());
+
+ boolean successful = false;
+ try {
+ authLoginMultipleParallel(resp);
+ } catch (ObjectException oe) {
+ // We expect an ObjectException to be thrown
+ successful = true;
+ }
+
+ assertNull(bean_.getuser());
+ assertTrue(successful);
+ }
+
+ @Test
+ @Desc("Authentication as user using multiple prompts simultaneously" +
+ " fails when too many passwords are provided.")
+ public void authLoginMultipleParallelFailLong() throws Exception {
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add("Apass".toCharArray());
+ resp.add("Bpass".toCharArray());
+ resp.add("OneTooManyPass".toCharArray());
+
+ boolean successful = false;
+ try {
+ authLoginMultipleParallel(resp);
+ } catch (ObjectException oe) {
+ // We expect an ObjectException to be thrown
+ successful = true;
+ }
+
+ assertNull(bean_.getuser());
+ assertTrue(successful);
+ }
+
+ @Test
+ @Desc("Authentication as expired user fails.")
+ public void authLoginExpired() throws Exception {
+ String username = "radtest_expired";
+ String prompt = "Password:";
+ String password = "simplepss";
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message>messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(prompt, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(password.toCharArray());
+ block = bean_.submit(resp);
+
+ assertEquals(BlockType.error, block.getType());
+ assertNull(bean_.getuser());
+ }
+
+ /**
+ * common method used by the following three 'change password' tests.
+ *
+ * @param prompt [] - exptected prompts from the server.
+ * @param response [] - proper responses to the prompts above.
+ * @param BlockType [] - expected server responses to the responses above.
+ */
+ private void authLoginChangePasswordBase(String [] prompt,
+ String [] response, BlockType [] blockType) throws Exception {
+
+ String username = "radtest_stale";
+
+ Block block = bean_.login(locale_, username);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+ for (int i = 0; i < prompt.length; i++) {
+ List<Message>messages = block.getMessages();
+ assertNotNull(messages);
+ assertEquals(1, messages.size());
+ assertEquals(prompt[i], messages.get(0).getMessage());
+
+ List<char []>resp = new ArrayList<char []>();
+ if (response[i] != null) {
+ resp.add(response[i].toCharArray());
+ }
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(blockType[i], block.getType());
+ }
+ }
+
+ @Test
+ @Desc("Authentication as user with stale password is able to provide" +
+ " a new one.")
+ public void authLoginChangePassword() throws Exception {
+ String [] prompt = new String [] {"Password:",
+ "Your password has expired.",
+ "New Password:",
+ "New Password:"};
+ String [] response = new String [] {"simplepass",
+ null,
+ "newpass",
+ "newpass"};
+ BlockType [] blockType = new BlockType [] {BlockType.conv,
+ BlockType.conv,
+ BlockType.conv,
+ BlockType.success};
+
+ authLoginChangePasswordBase(prompt, response, blockType);
+ assertEquals("radtest_stale", bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user with statel password is prompted for new" +
+ " one until successful.")
+ public void authLoginChangePasswordRetry() throws Exception {
+ String [] prompt = new String [] {"Password:",
+ "Your password has expired.",
+ "New Password:",
+ "New Password:",
+ "New Password:"};
+ String [] response = new String [] {"simplepass",
+ null,
+ "lamepass",
+ "newpass",
+ "newpass"};
+ BlockType [] blockType = new BlockType [] {BlockType.conv,
+ BlockType.conv,
+ BlockType.conv,
+ BlockType.conv,
+ BlockType.success};
+
+ authLoginChangePasswordBase(prompt, response, blockType);
+ assertEquals("radtest_stale", bean_.getuser());
+ }
+
+ @Test
+ @Desc("Authentication as user with stale password is prompted for new" +
+ " one but fails")
+ public void authLoginChangePasswordFail() throws Exception {
+ String [] prompt = new String [] {"Password:",
+ "Your password has expired.",
+ "New Password:",
+ "New Password:",
+ "New Password:"};
+ String [] response = new String [] {"simplepass",
+ null,
+ "firstbadpass",
+ "secondbadpass",
+ "thirdbadpass"};
+ BlockType [] blockType = new BlockType [] {BlockType.conv,
+ BlockType.conv,
+ BlockType.conv,
+ BlockType.conv,
+ BlockType.error};
+
+ authLoginChangePasswordBase(prompt, response, blockType);
+ assertNull(bean_.getuser());
+ }
+
+ @Test
+ @Desc("User is able to assume role without password")
+ public void authAssumeNoPassword() throws Exception {
+ // login as 'radtest_hasroles'
+ authRolesCorrect();
+
+ String rolename = "radtest_rolenopass";
+ Block block = bean_.assume(locale_, rolename);
+ assertNotNull(block);
+ assertEquals(BlockType.success, block.getType());
+ assertEquals(rolename, bean_.getuser());
+ }
+
+ @Test
+ @Desc("User is able to assume a role with a password")
+ public void authAssumePassword() throws Exception {
+ String rolename = "radtest_rolepass";
+ String prompt = "Password:";
+ String rolepass = "rolepass";
+
+ // login as 'radtest_hasroles'
+ authRolesCorrect();
+ Block block = bean_.assume(locale_, rolename);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message>messages = block.getMessages();
+ assertEquals(1, messages.size());
+ assertEquals(prompt, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(rolepass.toCharArray());
+
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(BlockType.success, block.getType());
+ assertEquals(rolename, bean_.getuser());
+ }
+
+ @Test
+ @Desc("Attempt to assume a bad role fails.")
+ public void authAssumeBadRole() throws Exception {
+ String rolename = "badrole";
+
+ // login as 'radtest_hasroles'
+ authRolesCorrect();
+ Block block = bean_.assume(locale_, rolename);
+ assertNotNull(block);
+ assertEquals(BlockType.error, block.getType());
+ assertEquals("radtest_hasroles", bean_.getuser());
+ }
+
+ @Test
+ @Desc("Attempt to assume a role fails due to bad password.")
+ public void authAssumeBadPassword() throws Throwable {
+ String rolename = "radtest_rolepass";
+ String prompt = "Password:";
+ String password = "badrolepass";
+
+ // login as 'radtest_hasroles'
+ authRolesCorrect();
+ Block block = bean_.assume(locale_, rolename);
+ assertNotNull(block);
+ assertEquals(BlockType.conv, block.getType());
+
+ List<Message>messages = block.getMessages();
+ assertEquals(1, messages.size());
+ assertEquals(prompt, messages.get(0).getMessage());
+
+ List<char[]>resp = new ArrayList<char[]>();
+ resp.add(password.toCharArray());
+
+ block = bean_.submit(resp);
+ assertNotNull(block);
+ assertEquals(BlockType.error, block.getType());
+ assertEquals("radtest_hasroles", bean_.getuser());
+ }
+
+ @Test
+ @Desc("Connection times out if no requests are made following connection.")
+ public void authTimeoutExpireInitial() throws Throwable {
+ // pause a second logger to force a timeout.
+ Thread.sleep((CONN_TO + 1) * 1000);
+
+ try {
+ int to = bean_.getconnectionTimeout();
+ assertEquals(CONN_TO, to);
+ } catch (UndeclaredThrowableException ute) {
+ Throwable t = ute.getUndeclaredThrowable();
+ if (USCE_CLASSNAME.equals(t.getClass().getName()))
+ return;
+
+ throw t;
+ }
+
+ fail("Connection did not timeout as expected.");
+ }
+
+ @Test
+ @Desc("Connection times out after a request is made.")
+ public void authTimeoutExpireResponse() throws Throwable {
+ int to = bean_.getconnectionTimeout();
+ assertEquals(CONN_TO, to);
+
+ // pause to force a timeout
+ Thread.sleep((CONN_TO + 1) * 1000);
+ try {
+ to = bean_.getconnectionTimeout();
+ assertEquals(CONN_TO, to);
+ } catch (UndeclaredThrowableException ute) {
+ Throwable t = ute.getUndeclaredThrowable();
+ if (USCE_CLASSNAME.equals(t.getClass().getName()))
+ return;
+
+ throw t;
+ }
+ fail("Connection did not timeout as expected.");
+ }
+
+ @Test
+ @Desc("Connection about to timeout is correctly extended following" +
+ " response")
+ public void authTimeoutExtension() throws Exception {
+ Thread.sleep((CONN_TO - 1) * 1000);
+ int to = bean_.getconnectionTimeout();
+ assertEquals(CONN_TO, to);
+
+ // start login process with 1 second left
+ Block block = bean_.login(locale_, "radtest_nopass");
+
+ // pause past the would have been remaining 1 second
+ Thread.sleep(1500);
+
+ // should have got another 'CONN_TO' worth of time. It should now
+ // be CONN_TO - 1.5 seconds before the next timeout.
+ to = bean_.getconnectionTimeout();
+ assertEquals(CONN_TO, to);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/test/java/src/client/PAMTestBase.java Fri Jul 29 17:37:59 2011 -0400
@@ -0,0 +1,128 @@
+/*
+ * 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) 2011, Oracle and/or its affiliates. All rights reserved.
+ */
+
+package client;
+
+import com.oracle.solaris.smf.Instance;
+import com.oracle.solaris.smf.MasterMXBean;
+import com.oracle.solaris.smf.SMFState;
+import common.MBeanTestCommon;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import javax.management.MBeanServerConnection;
+import javax.management.ObjectName;
+import javax.management.remote.JMXConnector;
+import javax.management.remote.JMXConnectorFactory;
+import javax.management.remote.JMXServiceURL;
+import org.junit.After;
+import org.junit.Before;
+import org.opensolaris.os.adr.Stability;
+import org.opensolaris.os.rad.jmx.RadJMX;
+import org.opensolaris.os.rad.api.pam.AuthenticatorMXBean;
+import org.opensolaris.os.rad.api.pam.Block;
+import org.opensolaris.os.rad.api.pam.Message;
+
+import static org.junit.Assert.*;
+
+public abstract class PAMTestBase extends MBeanTestCommon {
+ public static final String RADTEST_SVC = "svc:/system/rad-test";
+ public static final String RADCONVTEST_SVC = "svc:/system/rad-conv-test";
+ public static final String PATH_AUTH = "/var/run/test-radsocket";
+ public static final String PATH_UNAUTH = "/var/run/test-radsocket-unauth";
+
+ private JMXConnector conn_;
+ protected AuthenticatorMXBean bean_;
+ protected String locale_ = Locale.getDefault().getLanguage();
+
+ /**
+ * determine if the rad-test and rad-conv-test services are runnning.
+ */
+ protected void checkRequiredServices() throws Exception {
+ setUpCommon("/usr/lib/rad/module/mod_smf.so");
+
+ final String name = "com.oracle.solaris.smf:type=master";
+ MasterMXBean smfBean = RadJMX.newMXBeanProxy(getMBSC(), strToON(name),
+ MasterMXBean.class, Stability.PRIVATE);
+ if (smfBean == null)
+ throw new Exception("Unable to create SMF Master MX Bean.");
+
+ List<Instance> instances = smfBean.getinstances();
+ boolean radtest = false;
+ boolean radconvtest = false;
+
+ for (Instance instance : instances) {
+ if (instance.getFmri().startsWith(RADTEST_SVC))
+ radtest = instance.getState() == SMFState.ONLINE;
+ if (instance.getFmri().startsWith(RADCONVTEST_SVC))
+ radconvtest = instance.getState() == SMFState.ONLINE;
+ }
+
+ if (!radtest)
+ throw new Exception("The Service '" + RADTEST_SVC +
+ "' Required by this test is not running.");
+ if (!radconvtest)
+ throw new Exception("The Service '" + RADCONVTEST_SVC +
+ "' Required by this test is not running.");
+
+ super.tearDown();
+ }
+
+ /**
+ * connect to the rad-test instance.
+ */
+ protected boolean isRadReady(boolean auth) throws Exception {
+ checkRequiredServices();
+
+ String path = auth ? PATH_AUTH : PATH_UNAUTH;
+
+ final String url = "service:jmx:radunix://" + path;
+ final String name = "org.opensolaris.os.rad:type=authentication";
+
+ conn_ = JMXConnectorFactory.connect(new JMXServiceURL(url));
+ MBeanServerConnection mbsc = conn_.getMBeanServerConnection();
+ if (mbsc == null) {
+ conn_.close();
+ return false;
+ }
+
+ bean_ = RadJMX.newMXBeanProxy(mbsc, new ObjectName(name),
+ AuthenticatorMXBean.class, Stability.PRIVATE);
+ if (bean_ == null) {
+ conn_.close();
+ return false;
+ }
+
+ return true;
+ }
+
+ @After
+ public void tearDown() throws IOException {
+ if (conn_ != null)
+ conn_.close();
+ }
+}
--- a/usr/src/test/java/src/client/RadRequestBase.java Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/test/java/src/client/RadRequestBase.java Fri Jul 29 17:37:59 2011 -0400
@@ -108,7 +108,8 @@
@After
@Override
public void tearDown() throws IOException {
- connector_.close();
+ if (connector_ != null)
+ connector_.close();
super.tearDown();
}
--- a/usr/src/test/radtest/java.py Thu Jul 28 15:54:26 2011 -0700
+++ b/usr/src/test/radtest/java.py Fri Jul 29 17:37:59 2011 -0400
@@ -34,6 +34,7 @@
_VP_DIR = "usr/lib/rad/java"
_JUNIT_JAR = "/usr/share/lib/java/junit.jar"
+_SCF_JAR = "usr/share/vpanels/scf-common.jar"
class JavaExecutor(fw.ExtTestFinder):
""" Finds tests matching a particular pattern and executes them """
@@ -47,9 +48,11 @@
def make_classpath(self, root, dir):
vproot = os.path.join(root, _VP_DIR)
vpjars = [ "rad.jar", "adr.jar", "afunix.jar" ]
+ scfjar = os.path.join(root, _SCF_JAR)
classpathents = map(lambda x: os.path.join(vproot, x), vpjars)
classpathents.append(_JUNIT_JAR)
+ classpathents.append(scfjar)
classpathents.append(dir)
return ":".join(classpathents)