Initial import from FreeBSD RELENG_4:
[games.git] / release / sysinstall / anonFTP.c
1 /*
2  * The new sysinstall program.
3  *
4  * This is probably the last program in the `sysinstall' line - the next
5  * generation being essentially a complete rewrite.
6  *
7  * $FreeBSD: src/release/sysinstall/anonFTP.c,v 1.29.2.4 2002/08/09 20:34:23 schweikh Exp $
8  *
9  * Copyright (c) 1995
10  *      Coranth Gryphon.  All rights reserved.
11  * Copyright (c) 1996
12  *      Jordan K. Hubbard.  All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer,
19  *    verbatim and that no modifications are made prior to this
20  *    point in the file.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR THEIR PETS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  */
38
39 #include "sysinstall.h"
40 #include <sys/param.h>
41 #include <pwd.h>
42 #include <grp.h>
43
44 /* This doesn't change until FTP itself changes */
45
46 #define FTP_NAME        "ftp"
47 #define MOTD_FILE       "ftpmotd"
48
49 /* These change if we want to use different defaults */
50
51 #define FTP_UID         14
52 #define FTP_GID         5
53 #define FTP_GROUP       "operator"
54 #define FTP_UPLOAD      "incoming"
55 #define FTP_COMMENT     "Anonymous FTP Admin"
56 #define FTP_HOMEDIR     "/var/ftp"
57
58 #define ANONFTP_HELPFILE "anonftp"
59
60 /* Set up the structure to hold configuration information */
61 /* Note that this is only what we could fit onto the one screen */
62
63 typedef struct
64 {
65     char homedir[64];             /* Home Dir for Anon FTP */
66     char group[32];               /* Group */
67     char uid[8];                  /* UID */
68     char comment[64];             /* PWD Comment */
69     char upload[32];              /* Upload Dir */
70 } FTPConf;
71
72 static FTPConf tconf;
73
74 #define ANONFTP_HOMEDIR_LEN       64
75 #define ANONFTP_COMMENT_LEN       64
76 #define ANONFTP_UPLOAD_LEN        32
77 #define ANONFTP_GROUP_LEN         32
78 #define ANONFTP_UID_LEN           8
79
80 static int      okbutton, cancelbutton;
81
82 /* What the screen size is meant to be */
83 #define ANONFTP_DIALOG_Y         0
84 #define ANONFTP_DIALOG_X         8
85 #define ANONFTP_DIALOG_WIDTH     COLS - 16
86 #define ANONFTP_DIALOG_HEIGHT    LINES - 2
87
88 static Layout layout[] = {
89 #define LAYOUT_UID              0
90     { 2, 3, 8, ANONFTP_UID_LEN - 1,
91       "UID:", "What user ID to assign to FTP Admin",
92       tconf.uid, STRINGOBJ, NULL },
93 #define LAYOUT_GROUP            1
94     { 2, 15, 15, ANONFTP_GROUP_LEN - 1,
95       "Group:",  "Group name that ftp process belongs to",
96       tconf.group, STRINGOBJ, NULL },
97 #define LAYOUT_COMMENT          2
98     { 2, 35, 24, ANONFTP_COMMENT_LEN - 1,
99       "Comment:", "Password file comment for FTP Admin",
100       tconf.comment, STRINGOBJ, NULL },
101 #define LAYOUT_HOMEDIR          3
102     { 9, 10, 43, ANONFTP_HOMEDIR_LEN - 1,
103       "FTP Root Directory:",
104       "The top directory to chroot to when doing anonymous ftp",
105       tconf.homedir, STRINGOBJ, NULL },
106 #define LAYOUT_UPLOAD           4
107     { 14, 20, 22, ANONFTP_UPLOAD_LEN - 1,
108       "Upload Subdirectory:", "Designated sub-directory that holds uploads",
109       tconf.upload, STRINGOBJ, NULL },
110 #define LAYOUT_OKBUTTON         5
111     { 19, 15, 0, 0,
112       "OK", "Select this if you are happy with these settings",
113       &okbutton, BUTTONOBJ, NULL },
114 #define LAYOUT_CANCELBUTTON     6
115     { 19, 35, 0, 0,
116       "CANCEL", "Select this if you wish to cancel this screen",
117       &cancelbutton, BUTTONOBJ, NULL },
118     { NULL },
119 };
120
121 static int
122 createFtpUser(void)
123 {
124     struct passwd *tpw;
125     struct group  *tgrp;
126     char pwline[256];
127     char *tptr;
128     int gid;
129     FILE *fptr;
130     
131     if ((gid = atoi(tconf.group)) <= 0) {
132         if (!(tgrp = getgrnam(tconf.group))) {
133             /* group does not exist, create it by name */
134             
135             tptr = msgGetInput("14", "What group ID to use for group %s ?", tconf.group);
136             if (tptr && *tptr && ((gid = atoi(tptr)) > 0)) {
137                 if ((fptr = fopen(_PATH_GROUP,"a"))) {
138                     fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
139                     fclose(fptr);
140                 }
141             }
142             else
143                 gid = FTP_GID;
144         }
145         else
146             gid = tgrp->gr_gid;
147     }
148     else if (!getgrgid(gid)) {
149         /* group does not exist, create it by number */
150         
151         tptr = msgGetInput("14", "What group name to use for gid %d ?", gid);
152         if (tptr && *tptr) {
153             SAFE_STRCPY(tconf.group, tptr);
154             if ((tgrp = getgrnam(tconf.group))) {
155                 gid = tgrp->gr_gid;
156             }
157             else if ((fptr = fopen(_PATH_GROUP,"a"))) {
158                 fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
159                 fclose(fptr);
160             }
161         }
162     }
163     
164     if ((tpw = getpwnam(FTP_NAME))) {
165         if (tpw->pw_uid != FTP_UID)
166             msgConfirm("FTP user already exists with a different uid.");
167         
168         return DITEM_SUCCESS;   /* succeeds if already exists */
169     }
170     
171     sprintf(pwline, "%s:*:%s:%d::0:0:%s:%s:/nonexistent\n", FTP_NAME, tconf.uid, gid, tconf.comment, tconf.homedir);
172     
173     fptr = fopen(_PATH_MASTERPASSWD,"a");
174     if (! fptr) {
175         msgConfirm("Could not open master password file.");
176         return DITEM_FAILURE;
177     }
178     fprintf(fptr, "%s", pwline);
179     fclose(fptr);
180     msgNotify("Remaking password file: %s", _PATH_MASTERPASSWD);
181     vsystem("pwd_mkdb -p %s", _PATH_MASTERPASSWD);
182     return DITEM_SUCCESS | DITEM_RESTORE;
183 }
184
185 /* This is it - how to get the setup values */
186 static int
187 anonftpOpenDialog(void)
188 {
189     WINDOW              *ds_win;
190     ComposeObj          *obj = NULL;
191     int                 n = 0, cancel = FALSE;
192     int                 max;
193     char                title[80];
194     WINDOW              *w = savescr();
195
196     /* We need a curses window */
197     if (!(ds_win = openLayoutDialog(ANONFTP_HELPFILE, " Anonymous FTP Configuration ",
198                               ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, ANONFTP_DIALOG_WIDTH, ANONFTP_DIALOG_HEIGHT))) {
199         beep();
200         msgConfirm("Cannot open anonymous ftp dialog window!!");
201         restorescr(w);
202         return DITEM_FAILURE;
203     }
204     
205     /* Draw a sub-box for the path configuration */
206     draw_box(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 8,
207              ANONFTP_DIALOG_HEIGHT - 11, ANONFTP_DIALOG_WIDTH - 17,
208              dialog_attr, border_attr);
209     wattrset(ds_win, dialog_attr);
210     sprintf(title, " Path Configuration ");
211     mvwaddstr(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 22, title);
212     
213     /** Initialize the config Data Structure **/
214     bzero(&tconf, sizeof(tconf));
215     
216     SAFE_STRCPY(tconf.group, FTP_GROUP);
217     SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
218     SAFE_STRCPY(tconf.comment, FTP_COMMENT);
219     SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
220     sprintf(tconf.uid, "%d", FTP_UID);
221     
222     /* Some more initialisation before we go into the main input loop */
223     obj = initLayoutDialog(ds_win, layout, ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, &max);
224
225     cancelbutton = okbutton = 0;
226     while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel));
227
228     /* Clear this crap off the screen */
229     delwin(ds_win);
230     use_helpfile(NULL);
231     restorescr(w);
232     if (cancel)
233         return DITEM_FAILURE;
234     return DITEM_SUCCESS;
235 }
236
237 int
238 configAnonFTP(dialogMenuItem *self __unused)
239 {
240     int i;
241
242
243     if (msgYesNo("Anonymous FTP permits un-authenticated users to connect to the system\n"
244                  "FTP server, if FTP service is enabled.  Anonymous users are\n"
245                  "restricted to a specific subset of the file system, and the default\n"
246                  "configuration provides a drop-box incoming directory to which uploads\n"
247                  "are permitted.  You must separately enable both inetd(8), and enable\n"
248                  "ftpd(8) in inetd.conf(5) for FTP services to be available.  If you\n"
249                  "did not do so earlier, you will have the opportunity to enable inetd(8)\n"
250                  "again later.\n\n"
251                  "Do you wish to continue configuring anonymous FTP?")) {
252         return DITEM_FAILURE;
253     }
254     
255     /* Be optimistic */
256     i = DITEM_SUCCESS;
257     
258     i = anonftpOpenDialog();
259     if (DITEM_STATUS(i) != DITEM_SUCCESS) {
260         msgConfirm("Configuration of Anonymous FTP cancelled per user request.");
261         return i;
262     }
263     
264     /*** Use defaults for any invalid values ***/
265     if (atoi(tconf.uid) <= 0)
266         sprintf(tconf.uid, "%d", FTP_UID);
267     
268     if (!tconf.group[0])
269         SAFE_STRCPY(tconf.group, FTP_GROUP);
270     
271     if (!tconf.upload[0])
272         SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
273     
274     /*** If the user did not specify a directory, use default ***/
275     
276     if (tconf.homedir[strlen(tconf.homedir) - 1] == '/')
277         tconf.homedir[strlen(tconf.homedir) - 1] = '\0';
278     
279     if (!tconf.homedir[0])
280         SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
281     
282     /*** If HomeDir does not exist, create it ***/
283     
284     if (!directory_exists(tconf.homedir))
285         vsystem("mkdir -p %s", tconf.homedir);
286     
287     if (directory_exists(tconf.homedir)) {
288         msgNotify("Configuring %s for use by anon FTP.", tconf.homedir);
289         vsystem("chmod 555 %s && chown root.%s %s", tconf.homedir, tconf.group, tconf.homedir);
290         vsystem("mkdir %s/bin && chmod 555 %s/bin", tconf.homedir, tconf.homedir);
291         vsystem("cp /bin/ls %s/bin && chmod 111 %s/bin/ls", tconf.homedir, tconf.homedir);
292         vsystem("cp /bin/date %s/bin && chmod 111 %s/bin/date", tconf.homedir, tconf.homedir);
293         vsystem("mkdir %s/etc && chmod 555 %s/etc", tconf.homedir, tconf.homedir);
294         vsystem("mkdir -p %s/pub", tconf.homedir);
295         vsystem("mkdir -p %s/%s", tconf.homedir, tconf.upload);
296         vsystem("chmod 1777 %s/%s", tconf.homedir, tconf.upload);
297         
298         if (DITEM_STATUS(createFtpUser()) == DITEM_SUCCESS) {
299             msgNotify("Copying password information for anon FTP.");
300             vsystem("awk -F: '{if ($3 < 10 || $1 == \"ftp\") print $0}' /etc/passwd > %s/etc/passwd && chmod 444 %s/etc/passwd", tconf.homedir, tconf.homedir);
301             vsystem("awk -F: '{if ($3 < 100) print $0}' /etc/group > %s/etc/group && chmod 444 %s/etc/group", tconf.homedir, tconf.homedir);
302             vsystem("chown -R root.%s %s/pub", tconf.group, tconf.homedir);
303         }
304         else {
305             msgConfirm("Unable to create FTP user!  Anonymous FTP setup failed.");
306             i = DITEM_FAILURE;
307         }
308         
309         if (!msgYesNo("Create a welcome message file for anonymous FTP users?")) {
310             char cmd[256];
311             vsystem("echo Your welcome message here. > %s/etc/%s", tconf.homedir, MOTD_FILE);
312             sprintf(cmd, "%s %s/etc/%s", variable_get(VAR_EDITOR), tconf.homedir, MOTD_FILE);
313             if (!systemExecute(cmd))
314                 i = DITEM_SUCCESS;
315             else
316                 i = DITEM_FAILURE;
317         }
318     }
319     else {
320         msgConfirm("Invalid Directory: %s\n"
321                    "Anonymous FTP will not be set up.", tconf.homedir);
322         i = DITEM_FAILURE;
323     }
324     if (DITEM_STATUS(i) == DITEM_SUCCESS)
325         variable_set2("anon_ftp", "YES", 0);
326     return i | DITEM_RESTORE;
327 }