Commit 8c98fc4b authored by Russ Fish's avatar Russ Fish

Work over the accounts and mounts part of the CygWinXP port.

Use cygrunsrv -i on sshd to "allow the service to interact with the desktop."
Now that the sshd daemon has a desktop session context that is inherited by
the client shell, remote home directories can work.  They start with a blank
Windows mount context, but once a single Samba connection is made during
login, it enables all UNC //machine/path mounts to work.  Hence the home
directories are now CygWin mount points (no longer symlinks) to UNC paths, set
up by rc.mounts and then shared through CygWin to all of the user logins.

Get rid of the previous horrible (and fragile) hack to set up an auto-login by
the swapin user which then automatically started a user sshd on port 2222.

tmcd.c - Arrange for tmcd to provide the public key data when a special argument is
given as "tmcc accounts pubkeys".

rc.accounts - Due to permissions problems with remote-mounted authorized_keys
files, sshd_config now uses "AuthorizedKeysFile /sshkeys/%u/authorized_keys",
which is where rc.accounts puts the public key data.

Since root, Administrator, and even SYSTEM can be locked out by permissions on
NT, WINDOWS() variant logic to set ownership and modes on authorized_keys
files had to be added to rc.accounts.  There is also a bug in the sshd
"privilege separation" setreuid() dance that requires the authorized_keys
files to be owned by SYSTEM (or be mode 644, which is slightly worse.)

cygwinxp/liblocsetup.pm - Pay attention to the users' shell preferences in
generating /etc/passwd.  Make warnings more uniform.
parent 0e27c632
......@@ -189,6 +189,7 @@ sub doboot()
# On CygWin, just make a batch of Windows accounts and generate
# /etc/passwd and /etc/group from the Windows accounts.
# (The process isn't finished until os_accounts_end is called.)
goto bad
if (WINDOWS() && os_accounts_start());
......@@ -453,8 +454,63 @@ sub doboot()
if (! MFS() && ! WINDOWS()) {
dbmclose(%PWDDB);
}
os_accounts_end()
if (WINDOWS());
if (WINDOWS()) {
# Make the /etc/group and passwd files.
os_accounts_end();
# We couldn't do chown'ing before new users were in the /etc/passwd file.
print "Filling in ssh authorized_keys files.\n";
while (($login, $info) = each %newaccounts) {
if ($info =~ /$pat/) {
$pswd = $2;
$uid = $3;
$gid = $4;
$root = $5;
$name = $6;
$hdir = $7;
# We have to put the authorized_keys files somewhere that they
# won't be covered up by the Samba homedir mounts.
#
# The SMB protocol (whether served by Samba or Microsoft)
# doesn't provide fine-grained control over file and directory
# permissions, just coarse control over the whole share mount.
# Hence sshd will ignore the keys files if they're remote.
#
# This matches a line like this in /etc/sshd_config:
# AuthorizedKeysFile /sshkeys/%u/authorized_keys
my $sshdir = "/sshkeys/$login";
# Open up an existing key dir to the root user. Even though root
# is in the Administrators group, it's locked out by permissions.
$cmd = "$CHOWN -R root $sshdir";
if (-e $sshdir && system($cmd) != 0) {
warning("Failed $cmd: $!");
}
# Create .ssh dir and populate it with an authkeys file.
TBNewsshKeyfile($sshdir, $uid, $gid, 1, @{$pubkeys1{$login}});
TBNewsshKeyfile($sshdir, $uid, $gid, 2, @{$pubkeys2{$login}});
# Set the ownership of the home directory / mount point / keys.
# Use the system chown by name, not UID. Can't use chown -R, it goes
# top-down, chowning the directory away and then trying to read it.
#
# Okay, this is really wrong. The authkeys files can't be mode 600
# and owned by the user. They can be 644/root, or 600/SYSTEM.
# This is a bug in sshd's use of setreuid(). We prefer 600/SYSTEM.
my $cmd = "$CHOWN SYSTEM $sshdir/*";
if (system($cmd) != 0) {
warning("Failed $cmd: $!");
}
$cmd = "$CHOWN $login $sshdir";
if (system($cmd) != 0) {
warning("Failed $cmd: $!");
}
}
}
}
#
# Create sfs_users file and populate it with public SFS keys
......@@ -590,15 +646,21 @@ sub TBNewsshKeyfile($$$$@)
my $keyfile = "$sshdir/authorized_keys";
if (! -e $sshdir) {
if (! mkdir($sshdir, 0700)) {
warning("Could not mkdir $sshdir: $!");
if (! os_mkdir($sshdir, "0700")) {
warning("TBNewsshKeyfile: Could not os_mkdir $sshdir: $!");
return -1;
}
}
if (! WINDOWS()) {
# On Windows we'll do the chown'ing at the end.
# Right now it would lock out root.
if (!chown($uid, $gid, $sshdir)) {
warning("Could not chown $sshdir: $!");
return -1;
}
}
if ($protocol == 2) {
$keyfile .= "2";
}
......@@ -620,7 +682,7 @@ sub TBNewsshKeyfile($$$$@)
}
close(AUTHKEYS);
if (!chown($uid, $gid, "${keyfile}.new")) {
if (! WINDOWS() && !chown($uid, $gid, "${keyfile}.new")) {
warning("Could not chown ${keyfile}.new: $!");
return -1;
}
......@@ -633,7 +695,7 @@ sub TBNewsshKeyfile($$$$@)
warning("Could not save off ${keyfile}: $!");
return -1;
}
if (!chown($uid, $gid, "${keyfile}.old")) {
if (! WINDOWS() && !chown($uid, $gid, "${keyfile}.old")) {
warning("Could not chown ${keyfile}.old: $!");
}
if (!chmod(0600, "${keyfile}.old")) {
......@@ -641,7 +703,7 @@ sub TBNewsshKeyfile($$$$@)
}
}
if (system("mv -f ${keyfile}.new ${keyfile}")) {
warning("Could not mv ${keyfile} to ${keyfile}.new");
warning("Could not mv ${keyfile}.new to ${keyfile}");
}
return 0;
}
......@@ -168,11 +168,11 @@ sub doboot()
# On CygWin, the Samba mount is set up per-user by rc.cygwinxp-user, and what is
# available through the mount is controlled by the smb.conf file on the file
# server. All that's left is to wrap the //fs names in symlinks.
# server. All that's left is to CygWin mount the //fs directory paths.
if (WINDOWS()) {
while (($remote, $local) = each %mounts) {
my $host = "fs.bsd-fs.testbed.emulab.net"; ###"fs";
print "Link $local to //$host\n";
print "Mount $local from //$host\n";
os_samba_mount($local, $host, 0);
}
return;
......
......@@ -6,13 +6,13 @@
#
#
# Linux specific routines and constants for the client bootime setup stuff.
# CygWin specific routines and constants for the client bootime setup stuff.
#
package liblocsetup;
use Exporter;
@ISA = "Exporter";
@EXPORT =
qw ( $CP $LN $RM $CHOWN $EGREP
qw ( $CP $LN $RM $CHOWN $CHMOD $TOUCH $MOUNT $EGREP
$NFSMOUNT $UMOUNT $SFCMOUNT $SFCUMOUNT $NTS $NET $HOSTSFILE
$TMPASSWD $SFSSD $SFSCD $RPMCMD
os_account_cleanup os_accounts_start os_accounts_end
......@@ -45,6 +45,8 @@ BEGIN
}
}
use librc;
#
# Various programs and things specific to CygWin on XP and that we want to export.
#
......@@ -52,6 +54,9 @@ $CP = "/bin/cp";
$LN = "/bin/ln";
$RM = "/bin/rm";
$CHOWN = "/bin/chown";
$CHMOD = "/bin/chmod";
$TOUCH = "/bin/touch";
$MOUNT = "/bin/mount";
$EGREP = "/bin/egrep -q";
# Emulab wrappers for Windows.
......@@ -94,6 +99,8 @@ my $GATED = "/usr/sbin/gated";
my $ROUTE = "/sbin/route";
my $SHELLS = "/etc/shells";
my $DEFSHELL = "/bin/tcsh";
my $winusersfile = "$BOOTDIR/winusers";
my $usershellsfile = "$BOOTDIR/usershells";
#
# OS dependent part of cleanup node state.
......@@ -103,15 +110,21 @@ sub os_account_cleanup()
unlink @LOCKFILES;
# Generate the CygWin password and group files from the registry users.
# Note that the group membership is not reported into the CygWin files.
print "Resetting the CygWin passwd and group files.\n";
my $cmd = "$MKPASSWD -l | $AWK -F: '" . 'BEGIN{ OFS=":" } ';
my $cmd = "$MKPASSWD -l | $AWK -F: '";
$cmd .= 'BEGIN{ OFS=":" } ';
# Make root's UID zero. Put non-root homedirs under /users, not /home.
$cmd .= '{ if ($1=="root") $3="0"; else sub("/home/", "/users/"); print }';
$cmd .= "' > /etc/passwd";
$cmd .= '{ if ($1=="root") $3="0"; else sub("/home/", "/users/"); print }';
$cmd .= "'";
# Apply the users' shell preferences.
$cmd .= " | sed -f $usershellsfile"
if (-e $usershellsfile);
$cmd .= " > /etc/passwd";
##print "$cmd\n";
if (system("$cmd") != 0) {
print STDERR "Could not generate /etc/password file: $!\n";
warning("Could not generate /etc/password file: $!\n");
return -1;
}
......@@ -121,7 +134,7 @@ sub os_account_cleanup()
$cmd .= "' > /etc/group";
##print "$cmd\n";
if (system("$cmd") != 0) {
print STDERR "Could not generate /etc/group file: $!\n";
warning("Could not generate /etc/group file: $!\n");
return -1;
}
......@@ -179,7 +192,6 @@ sub os_etchosts_line($$$)
# [Global] or [Local]
# Group Name,Comment,UserName,...
#
my $winusersfile = "$BOOTDIR/winusers";
my @groupNames;
my %groupsByGid;
my %groupMembers;
......@@ -191,12 +203,17 @@ sub os_accounts_start()
%groupMembers = ();
if (! open(WINUSERS, "> $winusersfile")) {
print STDERR "os_accounts_start: Cannot create $winusersfile .\n";
warning("os_accounts_start: Cannot create $winusersfile .\n");
return -1;
}
if (! open(USERSHELLS, "> $usershellsfile")) {
warning("os_accounts_start: Cannot create $usershellsfile .\n");
return -1;
}
# Users come before groups in the addusers.exe account stream.
# Notice the <CR>'s. It's a Windows file.
# Notice the <CR><LF>'s! It's a Windows file.
print WINUSERS "[Users]\r\n";
return 0;
......@@ -280,7 +297,7 @@ sub os_useradd($$$$$$$$$)
# Groups have to be created before we can add members.
my $gname = $groupsByGid{$gid};
warn "*** Missing group name for gid $gid.\n"
warning("Missing group name for gid $gid.\n")
if (!$gname);
$groupMembers{$gname} .= "$login ";
$groupMembers{'Administrators'} .= "$login "
......@@ -291,12 +308,15 @@ sub os_useradd($$$$$$$$$)
$groupMembers{$gname} .= "$login ";
}
else {
warn "*** Missing group name for gid $gid.\n";
warning("Missing group name for gid $gid.\n");
}
}
# Map the shell into a full path.
$shell = MapShell($shell);
# Change the ones that are different from the default from mkpasswd, /bin/bash.
print USERSHELLS "/^$login:/s|/bin/bash\$|$shell|\n"
if ($shell !~ "/bin/bash");
# Use the leading 8 chars of the Unix MD5 passwd hash as a known random
# password, both here and in Samba. Skip over a "$1$" prefix.
......@@ -320,6 +340,7 @@ sub os_accounts_end()
print WINUSERS "$grp,Emulab $grp group,\r\n";
}
close WINUSERS;
close USERSHELLS;
# Create the whole batch of groups and accounts listed in the file.
# /p options: Users don't have to change passwords, and they never expire.
......@@ -329,7 +350,7 @@ sub os_accounts_end()
my $cmd = "$ADDUSERS /c '$winfile' /p:le";
##print " $cmd\n";
if (system($cmd) != 0) {
warn "*** WARNING: AddUsers error ($cmd)\n";
warning("AddUsers error ($cmd)\n");
return -1;
}
......@@ -341,12 +362,13 @@ sub os_accounts_end()
my $cmd = "$NET localgroup $grp $mbr /add > /dev/null";
##print " $cmd\n";
if (system($cmd) != 0) {
warn "*** WARNING: net localgroup error ($cmd)\n";
warning("net localgroup error ($cmd)\n");
}
}
}
# Make the CygWin /etc/passwd and /etc/group files match Windows.
# Note that the group membership is not reported into the CygWin files.
return os_account_cleanup();
}
......@@ -428,7 +450,7 @@ sub os_routing_add_manual($$$$$;$)
} elsif ($routetype eq "default") {
$cmd = "$ROUTE add default gw $gate";
} else {
warn "*** WARNING: bad routing entry type: $routetype\n";
warning("Bad routing entry type: $routetype\n");
$cmd = "";
}
......@@ -447,7 +469,7 @@ sub os_routing_del_manual($$$$$;$)
} elsif ($routetype eq "default") {
$cmd = "$ROUTE delete default";
} else {
warn "*** WARNING: bad routing entry type: $routetype\n";
warning("Bad routing entry type: $routetype\n");
$cmd = "";
}
......@@ -483,39 +505,29 @@ sub os_samba_mount($$$)
{
my ($local, $host, $verbose) = @_;
# Make the CygWin symlink from the local path to the driveletter automount point.
my $localdir = $sambapath = $local;
$localdir =~ s|(.*)/.*|$1|;
# Make the CygWin mount from the Samba path to the local mount point directory.
my $sambapath = $local;
$sambapath =~ s|.*/(.*)|//$host/$1|;
if (length($localdir) && ! -e $localdir) {
print "os_samba_mount: Making CygWin '$localdir' directory for symlinks.\n"
if (! -e $local) {
print "os_samba_mount: Making CygWin '$local' mount point directory.\n"
if ($verbose);
if (! os_mkdir($localdir, "0777")) { # Writable so anybody can make symlinks.
print STDERR "os_samba_mount: Failed CygWin mkdir, $cmd.\n";
exit(1);
if (! os_mkdir($local, "0755")) { # Will make whole path if necessary.
warning("os_samba_mount: Could not make mount point $local.\n");
}
}
if (-e $local) {
print "Removing previous CygWin symlink '$local'.\n"
if ($verbose);
$cmd = "$CHOWN `id -un` $local";
if (system($cmd) != 0) {
print STDERR
"os_samba_mount: Failed to take ownership of symlink, $cmd.\n";
}
$cmd = "$RM -f $local";
if (system($cmd) != 0) {
print STDERR
"os_samba_mount: Failed to remove previous CygWin symlink, $cmd.\n";
exit(1);
}
elsif (! -d $local) {
warning("os_samba_mount: Mount point $local is not a directory.\n");
}
print "Making CygWin symlink '$local' to '$sambapath'.\n"
print "Mounting '$local' from '$sambapath'.\n"
if ($verbose);
$cmd = "$LN -f -s $sambapath $local";
# If we don't turn on the -E/--no-executable flag, CygWin mount warns us:
# mount: defaulting to '--no-executable' flag for speed since native path
# references a remote share. Use '-f' option to override.
# Even with -E, exe's and scripts still work properly, so put it in.
$cmd = "$MOUNT -E $sambapath $local";
if (system($cmd) != 0) {
print STDERR "os_samba_mount: Failed CygWin symlink, $cmd.\n";
exit(1);
warning("os_samba_mount: Failed, $cmd.\n");
}
}
......@@ -610,8 +622,7 @@ sub os_fwconfig_line($@)
{
my ($fwinfo, @fwrules) = @_;
my ($upline, $downline);
my $errstr = "*** WARNING: Linux firewall not implemented\n";
my $errstr = "*** WARNING: Windows firewall not implemented\n";
warn $errstr;
$upline = "echo $errstr; exit 1";
......
......@@ -39,9 +39,12 @@ export USER
# 3) from HOMEDRIVE/HOMEPATH
# 4) / (root)
### Use a local dir under sshd for now.
### Try to get a connection to the Samba host.
/usr/local/etc/emulab/rc/rc.cygwinxp-user
### Use a local dir under sshd if the mount failed.
if [ ! -d "$HOME" ]; then
HOME=/home/$USER
HOME=/home/$USER
fi
# If the home directory doesn't exist, create it.
if [ ]; then
......
......@@ -18,32 +18,10 @@ uname -r > $iscygwin
chmod g+w $iscygwin
chmod -f g-w /etc/emulab
# Set up for autologin as the swapin user after reboot.
# Also run rc.cygwinxp-user as part of all logins.
tmcc=/usr/local/etc/emulab/tmcc.bin
swapper=`$tmcc creator | sed 's|.*SWAPPER=\([^ ]*\).*|\1|'`
echo "`date`: Set to autologin as $swapper." >> $logfile
alogdir=/var/emulab/boot
autologin=$alogdir/autologin.reg
chmod -f g+w $alogdir
rm -f $autologin
# Note the double-doubled backslashes below. Bash and regedit each eat a level.
cat <<EOF > $autologin
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]
"AutoAdminLogon"="1"
"DefaultUserName"="$swapper"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
"EmulabLogin"="C:\\\\cygwin\\\\bin\\\\bash /usr/local/etc/emulab/rc/rc.cygwinxp-user"
EOF
chmod g+rw $autologin
chmod -f g-w $alogdir
winautologin=`cygpath -w $autologin`
regedit=/cygdrive/c/WINDOWS/regedit
if ! $regedit -s "$winautologin" ; then
echo "`date`: $regedit -s $winautologin failed with code $?." >> $logfile
fi
# Set up to run rc.cygwinxp-user as part of all logins.
regtool -s set \
/HKEY_LOCAL_MACHINE/SOFTWARE/Microsoft/Windows/CurrentVersion/Run/EmulabLogin \
'C:\cygwin\bin\bash /usr/local/etc/emulab/rc/rc.cygwinxp-user'
# Make sure the computer name is right, reboots to change it if necessary.
nodeid=`$tmcc nodeid`
......
......@@ -8,46 +8,43 @@
# HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run\EmulabLogin
# with value:
# C:\cygwin\bin\bash /usr/local/etc/emulab/rc/rc.cygwinxp-user
#
# We also arrange for this to be run by login shells under ssh.
PATH=/usr/local/etc/emulab:/bin:$PATH
export PATH
# Root has a local home directory, and is handled by the SYSTEM sshd on port 22.
# There is no need to do any of the following for root.
user=`id -un`
if [ $user = root ]; then
exit 0
fi
# Samba server.
###host=fs
host=fs.bsd-fs.testbed.emulab.net
# Shares are local to the Win32 login session context.
# Explanation: Shares are local to the Win32 login session context.
# We must process a user name and password for the first one to be opened, and it
# has to go onto a drive letter. After that, we can use UNC //host paths freely.
###host=fs
host=fs.bsd-fs.testbed.emulab.net
pswd=`tmcc accounts | awk '/LOGIN='$user' /{print substr($0,index($0," PSWD=")+9,8)}'`
echo "Connecting to //$host as $user."
while ! net use Z: '\\'$host'\'$user "$pswd" /user:$user /persistent:no
do
# Read one char (or none on <Enter>), add a time-out for autologins after boot.
read -p "Connection failed, try again? [y]: " -n 1 -t 5 chr
echo ""
if [[ ! -z "$chr" && "$chr" != [yY] ]]; then break; fi
echo ""
echo "Re-trying connection to //$host as $user."
done
# Show network mount status.
net use
# Set the homedir for ssh in case we don't get through rc.mounts .
ln -f -s //$host/$user /users/$user
# Nothing to do if the mount has already been done.
if (net use | grep -q Z:); then
##echo "Remote directories have already been enabled by a Z: mount."
##sleep 5
exit 0
fi
# Run the SSH daemon in the Win32 login session context, so ssh client sessions
# will see the same set of mounts when they come in AS THIS SAME USER.
echo ""
startsshd
# Get the password for the user.
user=`id -un`
pswd=`tmcc accounts | awk '/^ADDUSER LOGIN='$user' /{print substr($0,index($0," PSWD=")+9,8)}'`
if [ -z "$pswd" ]; then
echo "No account for user $user, so no remote file access."
sleep 5
exit 1
fi
echo ""
echo "Hit <Enter> to stop the sshd when you're ready to log out."
read
echo "Connecting Z: to //$host as $user."
if ! net use Z: '\\'$host'\'$user "$pswd" /user:$user /persistent:no; then
echo "Connection failed."
else
# Show network mount status.
net use
fi
stopsshd
sleep 5
exit 0
......@@ -1868,9 +1868,10 @@ COMMAND_PROTOTYPE(doaccounts)
/*
* Locally, everything is NFS mounted so no point in
* sending back pubkey stuff; its never used.
* sending back pubkey stuff; it's never used except on CygWin.
* Add an argument of "pubkeys" to get the PUBKEY data on CygWin.
*/
if (reqp->islocal)
if (reqp->islocal && strncmp(rdata, "pubkeys", 7) != 0)
goto skipsshkeys;
/*
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment