Commit 3bcb05ab authored by Vangelis Koukis's avatar Vangelis Koukis
Browse files

Add support for Windows XP / Server 2003 Images

Introduce support for Windows XP / Server 2003 Images.

To do this:
    * Extend common.sh and 40InstallUnattend so they can also detect
      Windows XP / Server 2003 SYSPREP.INF answer files.
    * Extend 50AssignHostname so it can set the hostname inside
      SYSPREP.INF, based on a small handle-ini-file.py utility.
    * Remove the seemingly unnecessary addition of /LOGONPASSWORDCHG:NO,
      which is unsupported under XP / Server 2003. More on this below.
    * Warn the user about Windows XP / Server 2003 not supporting
      online NTFS resize, and the need to use OFFLINE_NTFSRESIZE
      instead.

Regarding the use of /LOGONPASSWORDCHG:NO while using NET USER
to change a user password:
    * This argument is unsupported under Windows XP / Server 2003,
      see http://blog.johnmuellerbooks.com/2011/04/12/working-with-net-user/
    * Its default value is "NO" anyway, so it shouldn't make a
      difference whether it is explicitly specified in the command line
      or not:
      https://answers.microsoft.com/en-us/windows/forum/windows_vista-security/setting-up-passwords-for-new-users/1704349b-31a3-4340-ae9e-1473c5adb919
    * Even if the security policies of a specific Image were set up
      in such way that users *were* required to change their passwords
      immediately upon their first logon, it is not snf-image's job
      to modify this behavior by specifying /LOGONPASSWORDCHG:NO.
      The password policy is Image-specific, and snf-image shouldn't
      mess with it.
parent 83cd6ea0
......@@ -8,7 +8,8 @@ SUBDIRS = tasks networking
dist_doc_DATA = COPYING AUTHORS CONTRIBUTORS
dist_bin_SCRIPTS = snf-image-helper
dist_scripts_SCRIPTS= hashpwd.py inject-files.py decode-properties.py disklabel.py
dist_scripts_SCRIPTS= hashpwd.py inject-files.py decode-properties.py \
disklabel.py handle-ini-file.py
dist_common_DATA = common.sh unattend.xml
edit = sed \
......
......@@ -504,6 +504,19 @@ get_last_free_sector() {
fi
}
get_sysprepinf() {
local target
target="$1"
# Return position of old-style (XP / Server 2003) answer file,
# C:\SYSPREP\SYSPREP.INF, if found.
sysprepinf="$target"/sysprep/sysprep.inf
if [ ! -f "$sysprepinf" ]; then
sysprepinf=""
fi
echo "$sysprepinf"
}
get_unattend() {
local target
target="$1"
......
#!/usr/bin/env python
#
# Copyright (C) 2015 GRNET S.A. and individual contributors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""Get or set a configuration variable inside an INI file.
Could have used Python's ConfigParser for this,
but it breaks with sloppily-formatted Windows SYSPREP.INF
answer files.
"""
import re
import sys
import os.path
import argparse
def main():
DESCRIPTION = "Get or set a configuration variable in a INI file"
parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument("path", metavar="FILE",
help="Path to INI file")
parser.add_argument("action", metavar="ACTION",
help="Action to perform: Must be `get' or `set'.")
parser.add_argument("section", metavar="SECTION",
help="The section containing the option. If the"
" section does not exist, it will be created.")
parser.add_argument("key", metavar="KEY",
help="The name of the variable. If it does not"
" exist it, will be added to the specified"
" section.")
parser.add_argument("value", metavar="VALUE", nargs="?",
help="The value of the variable to set.",
default="")
args = parser.parse_args()
if args.action != "get" and args.action != "set":
raise ValueError("ACTION must be `get' or `set'")
if args.action == "set" and args.value == "":
raise ValueError("VALUE is required if ACTION is `set'")
update = (args.action == "set")
section = args.section
key = args.key
value = args.value
section_re = re.compile("\s*\[\s*%s\s*\]\s*" % section, re.I)
section_start_re = re.compile("\s*\[.*", re.I)
key_re = re.compile("\s*(%s)\s*=(.*)$" % key, re.I)
section_found = False
key_found = False
with open(args.path, "r") as f:
lines = f.readlines()
with open(args.path if update else "/dev/null", "w") as f:
for line in lines:
if section_re.search(line):
# Found matching section
section_found = True
elif section_found and key_re.search(line):
# Found key inside matching section.
# Preserve case of key and write the value.
key_found = True
key, val = key_re.search(line).groups()
if not update:
print val.strip()
return 0
f.write("%s=%s\n" % (key, value))
continue
elif (not key_found and section_found and
section_start_re.match(line)):
# Found a new section after the matching section, but without
# finding the key. This means that this is a new key.
f.write("%s=%s\n" % (key, value))
f.write(line)
if section_found and not key_found:
f.write("%s=%s\n" % (key, value))
if not section_found:
f.write("[%s]\n" % section)
f.write("%s=%s\n" % (key, value))
return 0
if __name__ == "__main__":
sys.exit(main())
#! /bin/bash
# Copyright (C) 2011 GRNET S.A.
# Copyright (C) 2011, 2015 GRNET S.A. and individual contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -63,6 +63,10 @@ else
fi
fi
# FIXME: There is no guarantee the answer file is actually named
# C:\unattend.xml. These may have to be modified to use the actual
# name of the answer file, as returned by get_unattend().
echo "del /Q /F C:\unattend.xml" > \
"$target/Windows/Setup/Scripts/SetupComplete.cmd"
......@@ -86,6 +90,44 @@ echo "del /Q /F C:\Windows\SnfScripts\ChangeAdminPassword.cmd" >> \
echo "rmdir C:\Windows\SnfScripts" >> \
"$target/Windows/Setup/Scripts/SetupComplete.cmd"
# If using an old-style (XP / Server 2003) SYSPREP.INF answer file,
# ensure C:\SnfScripts\SetupComplete.cmd is executed via CmdLines.txt
# which must be installed in the InstalledfilesPath from SYSPREP.INF.
sysprepinf=$(get_sysprepinf "$target")
if [ -n "$sysprepinf" ]; then
h=@scriptsdir@/handle-ini-file.py
installfilespath=$($h "$sysprepinf" get Unattended InstallFilesPath)
if [ -z "$installfilespath" ]; then
# Set InstallFilesPath to C:\SYSPREP\i386 explicitly, if missing
$h "$sysprepinf" set Unattended InstallFilesPath 'C:\SYSPREP\i386'
installfilespath=$($h "$sysprepinf" get Unattended InstallFilesPath)
fi
if [ -z "$installfilespath" ]; then
log_error "Failed to get value of InstallFilesPath in SYSPREP.INF"
fi
installfilespath=$(tr [A-Z] [a-z] <<< "$installfilespath")
if [[ ! "$installfilespath" == 'c:\sysprep\'* ]]; then
log_error "InstallFilesPath from SYSPREP.INF not under C:\\SYSPREP\\"
fi
installfilespath=${installfilespath#'c:\sysprep\'}
installfilespath=$(echo "$installfilespath"|sed 's/\\/\//g')
# Ensure final location for InstallFilesPath is still under $target
oemdir="$target"/sysprep/"$installfilespath"/'$OEM$'
if ! readlink -f "$oemdir"|grep -q "^$target"; then
log_error "Invalid value for InstallFilesPath in SYSPREP.INF"
fi
mkdir -p "$oemdir"
cmdlinestxt="$oemdir"/CmdLines.txt
if [ ! -f "$cmdlinestxt" ]; then
echo "[Commands]" >"$cmdlinestxt"
fi
echo "\"C:\\Windows\\SnfScripts\\ChangeAdminPassword.cmd\"" >>"$cmdlinestxt"
echo "\"C:\\Windows\\Setup\\Scripts\\SetupComplete.cmd\"" >>"$cmdlinestxt"
fi
exit 0
# vim: set sta sts=4 shiftwidth=4 sw=4 et ai :
#! /bin/bash
# Copyright (C) 2011 GRNET S.A.
# Copyright (C) 2011, 2015 GRNET S.A. and individual contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -34,9 +34,9 @@ report_task_start
check_if_excluded
windows_hostname() {
local target password unattend tmp_unattend namespace
local target hostname unattend tmp_unattend namespace
target="$1"
password="$2"
hostname="$2"
tmp_unattend=$(mktemp)
add_cleanup rm "$tmp_unattend"
......@@ -45,14 +45,27 @@ windows_hostname() {
namespace="urn:schemas-microsoft-com:unattend"
# Windows XP / Server 2003:
# Set the hostname inside a SYSPREP.INF answer file, if found
sysprepinf=$(get_sysprepinf "$target")
# Windows Vista / Server 2008 and later:
# Use Unattend.xml or similar
unattend=$(get_unattend "$target")
if [ -z "$unattend" ]; then
log_error "Unattend.xml is missing."
if [ -z "$sysprepinf" -a -z "$unattend" ]; then
log_error "No C:\SYSPREP\SYSPREP.INF or Unattend.xml found in Image."
fi
if [ -n "$sysprepinf" ]; then
@scriptsdir@/handle-ini-file.py \
"$sysprepinf" set UserData ComputerName "$hostname"
fi
if [ -n "$unattend" ]; then
"$XMLSTARLET" ed -N x=$namespace -u "/x:unattend/x:settings/x:component/x:ComputerName" -v "$hostname" "$unattend" > "$tmp_unattend"
cat "$tmp_unattend" > "$unattend"
fi
"$XMLSTARLET" ed -N x=$namespace -u "/x:unattend/x:settings/x:component/x:ComputerName" -v "$password" "$unattend" > "$tmp_unattend"
cat "$tmp_unattend" > "$unattend"
echo done
}
......
#! /bin/bash
# Copyright (C) 2011 GRNET S.A.
# Copyright (C) 2011, 2015 GRNET S.A. and individual contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
......@@ -82,7 +82,7 @@ windows_password() {
for usr in $SNF_IMAGE_PROPERTY_USERS; do
echo -n "Installing new password for user \`$usr'..."
echo "net user $usr $password /ACTIVE:YES /LOGONPASSWORDCHG:NO /EXPIRES:NEVER /PASSWORDREQ:YES" >> \
echo "net user $usr $password /ACTIVE:YES /EXPIRES:NEVER /PASSWORDREQ:YES" >> \
"$target/Windows/SnfScripts/ChangeAdminPassword.cmd"
echo done
done
......
......@@ -57,6 +57,14 @@ if [ "$ptype" = "ntfs" -a "$SNF_IMAGE_PROPERTY_OSFAMILY" = "windows" ]; then
warn "Task ${PROGNAME:2} will not run. Offline NTFS resize requested."
exit 0
fi
# Windows XP / Server 2003
# No online resize is supported, emit a warning
sysprepinf=$(get_sysprepinf "$target")
if [ -n "$sysprepinf" ]; then
warn "Found SYSPREP.INF. Cannot perform online resize on XP / Server 2003."
warn "Set the OFFLINE_NTFSRESIZE property to request an offline resize."
fi
# Write a diskpart script to %SystemDrive%\Windows\SnfScripts. Sysprep will
# try to execute this script during the specialize pass.
cat > "$SNF_IMAGE_TARGET/Windows/SnfScripts/ExtendFilesystem" <<EOF
......
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