/* * ==================== * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved. * * The contents of this file are subject to the terms of the Common Development * and Distribution License("CDDL") (the "License"). You may not use this file * except in compliance with the License. * * You can obtain a copy of the License at * http://opensource.org/licenses/cddl1.php * See the License for the specific language governing permissions and limitations * under the License. * * When distributing the Covered Code, include this CDDL Header Notice in each file * and include the License file at http://opensource.org/licenses/cddl1.php. * If applicable, add the following below this CDDL Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyrighted [year] [name of copyright owner]" * ==================== */ package org.identityconnectors.solaris.operation.nis; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import org.identityconnectors.common.CollectionUtil; import org.identityconnectors.common.Pair; import org.identityconnectors.common.StringUtil; import org.identityconnectors.common.logging.Log; import org.identityconnectors.common.security.GuardedString; import org.identityconnectors.framework.common.objects.Attribute; import org.identityconnectors.framework.common.objects.AttributeUtil; import org.identityconnectors.solaris.SolarisConfiguration; import org.identityconnectors.solaris.SolarisConnection; import org.identityconnectors.solaris.attr.NativeAttribute; import org.identityconnectors.solaris.operation.search.SolarisEntry; public class UpdateNISUser extends AbstractNISOp { private static final Log logger = Log.getLog(UpdateNISUser.class); private static final String NO_PRIMARY_GROUP = "No primary group"; private static final String UID_NOT_UNIQUE = "uid is not unique."; public static void updateUser(SolarisEntry userEntry, final GuardedString passwd, SolarisConnection connection) { final String accountName = userEntry.getName(); String pwddir = connection.getConfiguration().getNisPwdDir(); String pwdfile = pwddir + "/passwd"; boolean recordUpdate = false; boolean isRename = false; String shell = null; String uid = null; String gid = null; String gecos = null; String homedir = null; final String cpCmd = connection.buildCommand(false, "cp"); final String chownCmd = connection.buildCommand(true, "chown"); final String diffCmd = connection.buildCommand(false, "diff"); final String grepCmd = connection.buildCommand(false, "grep"); // Test for a rename operation Attribute nameAttr = userEntry.searchForAttribute(NativeAttribute.NAME); String newName = (nameAttr != null) ? AttributeUtil.getStringValue(nameAttr) : accountName; if (newName != null && !newName.equals(userEntry.getName())) { // Make sure we update the entry recordUpdate = true; isRename = true; } String removeTmpFilesScript = AbstractNISOp.getRemovePwdTmpFiles(connection); // @formatter:off String getOwner = "OWNER=`ls -l " + pwdfile + " | awk '{ print $3 }'`; " + "GOWNER=`ls -l " + pwdfile + " | awk '{ print $4 }'`; " + "unset GRPERRMSG"; String updateUser = "if [ -n \"$ENTRYTEXT\" ]; then " + cpCmd + "-p " + pwdfile + " " + TMP_PWDFILE_1 + "; " + grepCmd + "-v \"^" + accountName + ":\" " + TMP_PWDFILE_1 + " > " + TMP_PWDFILE_2 + "; " + chownCmd + "$WHOIAM " + TMP_PWDFILE_2 + "\n " + "echo " + newName + ":$PASSWD:$NEWUID:$GROUP:$GECOS:$HOMEDIR:$SHELL >> " + TMP_PWDFILE_2 + "; " + diffCmd + pwdfile + " " + TMP_PWDFILE_1 + " 2>&1 >/dev/null; " + "RC=$?\n" + "if [ $RC -eq 0 ]; then " + cpCmd + "-f " + TMP_PWDFILE_2 + " " + pwdfile + "; " + chownCmd + "$OWNER:$GOWNER " + pwdfile + "; " + "else " + "GRPERRMSG=\"" + ERROR_MODIFYING + pwdfile + ", for entry " + newName + ".\"; " + "fi; " + "else\n" + "GRPERRMSG=\"" + accountName + " not found in " + pwdfile + ".\"; " + "fi"; // @formatter:on // Get specified user attributes Map<NativeAttribute, List<Object>> attributes = AbstractNISOp.constructNISUserAttributeParameters(userEntry, ALLOWED_NIS_ATTRIBUTES); for (Map.Entry<NativeAttribute, List<Object>> it : attributes.entrySet()) { NativeAttribute key = it.getKey(); String value = (String) it.getValue().get(0); boolean matched = true; switch (key) { case SHELL: shell = value; break; case GROUP_PRIM: gid = value; break; case DIR: homedir = value; break; case COMMENT: gecos = value; break; case ID: uid = value; break; default: matched = false; break; } if (matched) { logger.ok("{0} attribute '{1}' got value '{2}'", userEntry.getName(), key.toString(), value); } } connection.doSudoStart(); try { connection.executeCommand(AbstractNISOp.WHO_I_AM); try { connection.executeMutexAcquireScript(PWD_MUTEX_FILE, TMP_PWD_MUTEX_FILE, PWD_PID_FILE); final Pair<Boolean, String> response = initPasswordRecord1(accountName, uid, gid, homedir, recordUpdate); recordUpdate = response.first; final String passwordRecord1 = response.second; final String passwordRecord2; if (gecos != null) { passwordRecord2 = ("GECOS=\"" + gecos + "\"; "); recordUpdate = true; } else { passwordRecord2 = ("GECOS=`echo $ENTRYTEXT | cut -d: -f5`; "); } if (recordUpdate) { final String passwdVars = initPasswdVars(connection.getConfiguration()); connection.executeCommand(getOwner); connection.executeCommand(passwordRecord1); final String passwordCleanup1 = "echo \"$GRPERRMSG\"; unset GRPERRMSG; echo \"$DUPUIDERRMSG\"; unset DUPUIDERRMSG; unset dupuid; "; connection.executeCommand(passwordCleanup1, CollectionUtil.newSet(NO_PRIMARY_GROUP, UID_NOT_UNIQUE)); connection.executeCommand(passwordRecord2); connection.executeCommand(passwdVars); connection.executeCommand(removeTmpFilesScript); connection.executeCommand(updateUser); connection.executeCommand(removeTmpFilesScript); if (isRename && connection.getConfiguration().isNisShadowPasswordSupport()) { // Make sure we get the rename to the shadow file String shadowfile = pwddir + "/shadow"; // @formatter:off String shadowRename = "GRPENTRY=`grep '^" + accountName + ":' " + shadowfile + "`; " + "if [ -n \"$GRPENTRY\" ]; then " + cpCmd + "-p " + shadowfile + " " + TMP_PWDFILE_1 + "; " + grepCmd + "-v \"^" + accountName + ":\" " + TMP_PWDFILE_1 + " > " + TMP_PWDFILE_2 + "; " + chownCmd + "$WHOIAM " + TMP_PWDFILE_2 + "\n " + "echo $GRPENTRY | sed 's/^" + accountName + ":/" + newName + ":/g' >> " + TMP_PWDFILE_2 + "; " + diffCmd + shadowfile + " " + TMP_PWDFILE_1 + " 2>&1 >/dev/null; " + "RC=$?\n" + "if [ $RC -eq 0 ]; then " + cpCmd + "-f " + TMP_PWDFILE_2 + " " + shadowfile + "; " + chownCmd + "$OWNER:$GOWNER " + shadowfile + "; " + "else\n" + "GRPERRMSG=\"" + ERROR_MODIFYING + shadowfile + ", for entry " + newName + ".\"; " + "fi; " + "else\n" + "GRPERRMSG=\"" + ERROR_MODIFYING + shadowfile + ", " + accountName + " not found.\"; " + "fi"; // @formatter:on getOwner = initGetOwner(shadowfile); connection.executeCommand(getOwner); connection.executeCommand(shadowRename); connection.executeCommand(removeTmpFilesScript); } // Migrate the changes to the NIS database. // The changes to the NIS database have to be made before the // changes for shell and password. AbstractNISOp.addNISMake("passwd", connection); } if (shell != null) { addNISShellUpdate(accountName, shell, connection); } if (passwd != null) { addNISPasswordUpdate(accountName, passwd, connection); } AbstractNISOp.addNISMake("passwd", connection); } finally { connection.executeMutexReleaseScript(PWD_MUTEX_FILE); } } finally { connection.doSudoReset(); } } private static void addNISPasswordUpdate(String account, GuardedString password, SolarisConnection connection) { final String passwdCmd = connection.buildCommand(true, "yppasswd", account); connection.executeCommand(passwdCmd, Collections.<String>emptySet(), CollectionUtil.newSet(" password:")); connection.sendPassword(password, Collections.<String>emptySet(), CollectionUtil.newSet("new password:")); connection.sendPassword(password, CollectionUtil.newSet(" denied"), Collections.<String>emptySet()); } private static void addNISShellUpdate(String account, String shell, SolarisConnection connection) { final String passwdCmd = connection.buildCommand(true, "passwd", "-r nis -e", account); final Set<String> chshReject = CollectionUtil.newSet("password:", "passwd:" /* // passwd: User unknown: <id>\nPermission denied\n */); connection.executeCommand(passwdCmd, chshReject, CollectionUtil.newSet("new shell:")); connection.executeCommand(shell, CollectionUtil.newSet("unacceptable as a new shell")); } private static String initPasswdVars(SolarisConfiguration configuration) { StringBuilder builder = new StringBuilder(); builder.append(configuration.isNisShadowPasswordSupport() ? "PASSWD=\"x\"; " : // password held in shadow file "PASSWD=`echo $ENTRYTEXT | cut -d: -f2`; " // password in passwd file ); builder.append("SHELL=`echo $ENTRYTEXT | cut -d: -f7`"); return builder.toString(); } private static Pair<Boolean, String> initPasswordRecord1(String accountName, String uid, String gid, String homedir, final boolean recordUpdate) { boolean recordUpdateTmp = recordUpdate; StringBuffer passwordRecord1 = new StringBuffer( // The connection to the resource is pooled. Clear the environment // variables that will be used. "unset GRPERRMSG; unset dupuid; unset DUPUIDERRMSG; " ); passwordRecord1.append("ENTRYTEXT=`ypmatch " + accountName + " passwd`; "); if (StringUtil.isNotBlank(gid)) { // "" gid does not make sense passwordRecord1.append("GROUP=`ypmatch " + gid + " group | cut -d: -f3`; "); passwordRecord1.append("if [ -z \"$GROUP\" ]; then\n"); passwordRecord1.append("GRPERRMSG=\"" + NO_PRIMARY_GROUP + " matches in ypmatch " + gid + " group.\"; "); passwordRecord1.append("fi; "); recordUpdateTmp = true; } else { passwordRecord1.append("GROUP=`echo $ENTRYTEXT | cut -d: -f4`; "); } if (homedir != null) { passwordRecord1.append("HOMEDIR=" + homedir +"; "); recordUpdateTmp = true; } else { passwordRecord1.append("HOMEDIR=`echo $ENTRYTEXT | cut -d: -f6`; "); } if (StringUtil.isNotBlank(uid)) { // "" uid does not make sense passwordRecord1.append("NEWUID=" + uid + "; \\\n"); //check whether newuid is duplicate or not. passwordRecord1.append("dupuid=`ypmatch \"$NEWUID\" passwd.byuid | cut -d: -f3`; "); passwordRecord1.append("if [ \"$dupuid\" ]; then\n"); passwordRecord1.append("DUPUIDERRMSG=\"" + UID_NOT_UNIQUE + " change uid " + uid + " to some other unique value." + "\"; "); passwordRecord1.append("fi; "); recordUpdateTmp = true; } else { passwordRecord1.append("NEWUID=`echo $ENTRYTEXT | cut -d: -f3`; "); } return new Pair<Boolean, String>(recordUpdateTmp, passwordRecord1.toString()); } }