1 #!/usr/bin/ksh
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 #
  23 # Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  24 # Use is subject to license terms.
  25 #
  26 #
  27 # This script sets up anonymous FTP on the current host.
  28 #
  29 # Usage:
  30 #       ftpconfig [ftpdir]
  31 #       ftpconfig -d ftpdir
  32 #
  33 # ftpconfig without any arguments updates the files required by anonymous FTP
  34 # in an existing ftp users home directory.
  35 #
  36 # If ftpdir (which must be an absolute pathname) is supplied, ftpconfig
  37 # creates an ftp user account with a home directory of ftpdir, or updates
  38 # an existing ftp user account to have a home directory of ftpdir.
  39 #
  40 # If ftpdir already exists, the files it contains which are required by
  41 # anonymous FTP are updated, otherwise ftpdir is created containing the files
  42 # required by anonymous FTP.
  43 #
  44 # The -d (directory only) option just creates a new or updates an existing
  45 # ftpdir without creating or updating the ftp user account. This is useful
  46 # when creating guest FTP user accounts.
  47 #
  48 # Exit codes:   0 - success
  49 #               1 - usage
  50 #               2 - command failure
  51 #
  52 
  53 usage()
  54 {
  55         fmt=`gettext "Usage: %s [ftpdir]\n       %s -d ftpdir"`
  56         printf "$fmt\n" "$cmd" "$cmd" >&2
  57         exit 1
  58 }
  59 
  60 verify_root()
  61 {
  62         # Verify caller has a real user ID of 0.
  63         set `id`
  64         if [ "$1" != "uid=0(root)" ]
  65         then
  66                 fmt=`gettext "%s: Error: Only root can run %s"`
  67                 printf "$fmt\n" "$cmd" "$cmd" >&2
  68                 exit 1
  69         fi
  70 }
  71 
  72 # Make directory $1 under $home_dir with mode $2 and ownership $3.
  73 make_dir()
  74 {
  75         # Make a special case of creating $home_dir itself
  76         if [ -z "$1" ]
  77         then
  78                 dir="$home_dir"
  79         else
  80                 dir="$home_dir/$1"
  81         fi
  82         if [ ! -d "$dir" ]
  83         then
  84                 mkdir "$dir" || exit 2
  85         fi
  86         chmod "$2" "$dir"
  87         chown "$3" "$dir"
  88 }
  89 
  90 # Copy file $1 to under $home_dir with mode $2 and ownership $3.
  91 copy_file()
  92 {
  93         if [ -f "$1" ]
  94         then
  95                 file="$home_dir$1"
  96                 rm -f "$file"
  97                 cp "$1" "$file" || exit 2
  98                 chmod "$2" "$file"
  99                 chown "$3" "$file"
 100         fi
 101 }
 102 
 103 add_user()
 104 {
 105         pwent=`grep "^$username:" /etc/passwd`
 106         if [ -z "$pwent" ]
 107         then
 108                 # No existing ftp account.
 109                 if [ -z "$home_dir" ]
 110                 then
 111                         fmt=`gettext "%s: Error: No directory specified and no existing ftp account to update"`
 112                         printf "$fmt\n" "$cmd" >&2
 113                         exit 1
 114                 fi
 115 
 116                 # Create a new ftp account.
 117                 comment="Anonymous FTP"
 118                 fmt=`gettext "Creating user %s"`
 119                 printf "$fmt\n" "$username"
 120                 /usr/sbin/useradd -c "$comment" -d "$home_dir" -s "$login_shell" -g other "$username" || exit 2
 121         else
 122                 # ftp account already exists.
 123                 if [ -z "$home_dir" ]
 124                 then
 125                         home_dir=`echo "$pwent" | cut -d: -f6`
 126                         if [ -z "$home_dir" ]
 127                         then
 128                                 fmt=`gettext "%s: Error: Existing ftp account has no home directory"`
 129                                 printf "$fmt\n" "$cmd" >&2
 130                                 exit 2
 131                         fi
 132                 else
 133                         # Update an existing ftp account.
 134                         old_dir=`echo "$pwent" | cut -d: -f6`
 135                         if [ "$old_dir" != "$home_dir" ]
 136                         then
 137                                 fmt=`gettext "Updating user %s"`
 138                                 printf "$fmt\n" "$username"
 139                                 /usr/sbin/usermod -d "$home_dir" "$username" || exit 2
 140                         fi
 141                 fi
 142         fi
 143 }
 144 
 145 list_pam_session()
 146 {
 147         # Produce a list of the PAM session management modules.
 148         if [ -f /etc/pam.conf ]
 149         then
 150                 awk '($1 == "ftp" || $1 == "other") && $2 == "session" {
 151                         if ($4 !~ /^\//) printf "/usr/lib/security/"
 152                         print $4
 153                 }' </etc/pam.conf | sed 's/$ISA\///'
 154         fi
 155 }
 156 
 157 list_conv_cmds()
 158 {
 159         # Produce a list of the commands specified in the conversions file.
 160         if [ -f /etc/ftpd/ftpconversions ]
 161         then
 162                 sed 's/#.*$//' /etc/ftpd/ftpconversions | cut -d: -f5 |
 163                         awk '$1 !~ /^$/ { print $1 }' | sort -u
 164         fi
 165 }
 166 
 167 list_dyn_libs()
 168 {
 169         # Produce a list of the required dynamic libraries.
 170         for file in $* /usr/sbin/in.ftpd /usr/bin/ls `list_conv_cmds`
 171         do
 172                 ldd "$file" 2>/dev/null | cut -d'>' -f2
 173         done | sort -u
 174 }
 175 
 176 create_home_dir()
 177 {
 178         if [ "$home_dir" = "/" -o "$home_dir" = "/usr" ]
 179         then
 180                 fmt=`gettext "%s: Error: Installing FTP in %s is not permitted"`
 181                 printf "$fmt\n" "$cmd" "$home_dir" >&2
 182                 exit 1
 183         fi
 184 
 185         if [ ! -d "$home_dir" ]
 186         then
 187                 if [ -e "$home_dir" ]
 188                 then
 189                         fmt=`gettext "%s: Error: %s already exists but is not a directory"`
 190                         printf "$fmt\n" "$cmd" "$home_dir" >&2
 191                         exit 2
 192                 else
 193                         fmt=`gettext "Creating directory %s"`
 194                         printf "$fmt\n" "$home_dir"
 195                         make_dir "" 755 root:sys
 196                 fi
 197         fi
 198 }
 199 
 200 install_slash_etc()
 201 {
 202         # Preserve an existing etc directory.
 203         make_dir etc 111 root:sys
 204         make_dir etc/ftpd 111 root:sys
 205 
 206         # Create a stripped down password file.
 207         rm -f "$home_dir/etc/passwd"
 208         awk -F: '$1 ~ /^root$|^bin$|^sys$|^ftpadm$|^ftp$/ { print $1":x:"$3":"$4":::" }' </etc/passwd >"$home_dir/etc/passwd"
 209         chmod 444 "$home_dir/etc/passwd"
 210         chown root:sys "$home_dir/etc/passwd"
 211 
 212         # Create a stripped down group file.
 213         rm -f "$home_dir/etc/group"
 214         awk -F: '$1 ~ /^root$|^other$|^bin$|^sys$|^ftpadm$/ { print $1"::"$3":" }' </etc/group >"$home_dir/etc/group"
 215         chmod 444 "$home_dir/etc/group"
 216         chown root:sys "$home_dir/etc/group"
 217 
 218         # Copy in /etc/default/init, needed for timezone.
 219         if [ -f /etc/default/init ]
 220         then
 221                 make_dir etc/default 111 root:sys
 222                 copy_file /etc/default/init 444 root:sys
 223         fi
 224 
 225         # Copy in files used for hostname resolution
 226         copy_file /etc/hosts 444 root:sys
 227         copy_file /etc/resolv.conf 444 root:sys
 228         make_dir etc/inet 111 root:sys
 229         copy_file /etc/inet/ipnodes 444 root:sys
 230 }
 231 
 232 install_slash_usr()
 233 {
 234         # Preserve an existing usr directory.
 235         make_dir usr 111 root:sys
 236         make_dir usr/bin 111 root:bin
 237 
 238         if [ -h "$home_dir/bin" ]
 239         then
 240                 rm -f "$home_dir/bin"
 241         fi
 242         if [ ! -e "$home_dir/bin" ]
 243         then
 244                 ln -s ./usr/bin "$home_dir/bin" || exit 2
 245                 chown -h root:bin "$home_dir/bin"
 246         fi
 247 
 248         # Copy required dynamic libraries and PAM session management modules.
 249         libs="/lib/nss_files.so.1 /lib/nss_dns.so.1 /lib/libresolv.so.2"
 250         for lib in /lib/ld.so.1 $libs `list_dyn_libs $libs` `list_pam_session`
 251         do
 252                 if [ -f "$lib" ]
 253                 then
 254                         dir=`dirname "$home_dir$lib"`
 255                         if [ ! -d "$dir" ]
 256                         then
 257                                 mkdir -p "$dir" || exit 2
 258                         fi
 259                         copy_file "$lib" 555 root:bin
 260                 fi
 261         done
 262 
 263         # Copy required commands.
 264         for prog in /usr/bin/ls `list_conv_cmds`
 265         do
 266                 if [ -f "$prog" ]
 267                 then
 268                         dir=`dirname "$home_dir$prog"`
 269                         if [ ! -d "$dir" ]
 270                         then
 271                                 mkdir -p "$dir" || exit 2
 272                         fi
 273                         copy_file "$prog" 111 root:bin
 274                 fi
 275         done
 276 
 277         # Copy timezone files.
 278         if [ -d /usr/share/lib/zoneinfo ]
 279         then
 280                 rm -rf "$home_dir/usr/share/lib/zoneinfo"
 281                 find /usr/share/lib/zoneinfo | cpio -pduL "$home_dir" >/dev/null 2>&1
 282                 (cd "$home_dir/usr/share/lib"; find zoneinfo -type f |
 283                         xargs chmod 444)
 284                 rm -rf "$home_dir/usr/share/lib/zoneinfo/src"
 285         fi
 286 
 287         for dir in usr lib platform
 288         do
 289                 if [ -d "$home_dir/$dir" ]
 290                 then
 291                         (cd "$home_dir"; find $dir -type d | xargs chmod 111)
 292                         (cd "$home_dir"; find $dir -type d | xargs chown root:bin)
 293                         [ $dir != "lib" ] && chown root:sys "$home_dir/$dir"
 294                 fi
 295         done
 296 }
 297 
 298 install_slash_dev()
 299 {
 300         # Preserve an existing dev directory.
 301         make_dir dev 111 root:sys
 302 
 303         # Copy devices.
 304         for devname in conslog null udp udp6 zero
 305         do
 306                 rm -f "$home_dir/dev/$devname"
 307         done
 308         cpio -pduL "$home_dir" >/dev/null 2>&1 <<-EOF
 309         /dev/conslog
 310         /dev/null
 311         /dev/udp
 312         /dev/udp6
 313         /dev/zero
 314         EOF
 315         if [ $? -ne 0 ]
 316         then
 317                 fmt=`gettext "%s: Error: Creation of devices in %s failed"`
 318                 printf "$fmt\n" "$cmd" "$home_dir/dev" >&2
 319                 exit 2
 320         fi
 321 }
 322 
 323 install_slash_pub()
 324 {
 325         # Preserve an existing pub directory.
 326         make_dir pub 755 root:sys
 327 }
 328 
 329 update_home_dir()
 330 {
 331         fmt=`gettext "Updating directory %s"`
 332         printf "$fmt\n" "$home_dir"
 333         install_slash_dev
 334         install_slash_etc
 335         install_slash_usr
 336         install_slash_pub
 337 }
 338 
 339 # Execution starts here.
 340 
 341 IFS="   
 342 "
 343 SHELL=/usr/bin/ksh
 344 PATH=/usr/bin
 345 TEXTDOMAIN=SUNW_OST_OSCMD
 346 export SHELL PATH IFS TEXTDOMAIN
 347 
 348 cmd=`basename "$0"`
 349 username=ftp
 350 login_shell=/bin/true
 351 
 352 verify_root
 353 
 354 while getopts d arg
 355 do
 356         case $arg in
 357         d)      directory_only=1;;
 358         \?)     usage;;
 359         esac
 360 done
 361 shift `expr $OPTIND - 1`
 362 
 363 # Check arguments.
 364 [ $# -gt 1 ] && usage
 365 
 366 home_dir="$1"
 367 if [ -n "$directory_only" -a -z "$home_dir" ]
 368 then
 369         fmt=`gettext "%s: Error: ftpdir required with -d option"`
 370         printf "$fmt\n" "$cmd" >&2
 371         usage
 372 fi
 373 
 374 if [ -n "$home_dir" ]
 375 then
 376         echo "$home_dir" | grep "^/" >/dev/null 2>&1
 377         if [ $? -ne 0 ]
 378         then
 379                 fmt=`gettext "%s: Error: ftpdir must be an absolute pathname"`
 380                 printf "$fmt\n" "$cmd" >&2
 381                 usage
 382         fi
 383 fi
 384 
 385 # Ignore certain signals.
 386 trap '' 1 2 3 15
 387 
 388 umask 022
 389 [ -z "$directory_only" ] && add_user
 390 
 391 create_home_dir
 392 update_home_dir
 393 
 394 exit 0