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