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$
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		14
53#define FTP_GROUP	"ftp"
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
63typedef 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
72static 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
80static 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
88static 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 (leave empty for none)",
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    LAYOUT_END,
119};
120
121static int
122createFtpUser(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("ftp", "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 */
186static int
187anonftpOpenDialog(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
237int
238configAnonFTP(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		 "If you want the server to be read-only you should leave the upload\n"
252		 "directory option empty and add the -r command-line option to ftpd(8)\n"
253                 "in inetd.conf(5)\n\n"
254		 "Do you wish to continue configuring anonymous FTP?")) {
255	return DITEM_FAILURE;
256    }
257
258    /* Be optimistic */
259    i = DITEM_SUCCESS;
260
261    i = anonftpOpenDialog();
262    if (DITEM_STATUS(i) != DITEM_SUCCESS) {
263	msgConfirm("Configuration of Anonymous FTP cancelled per user request.");
264	return i;
265    }
266
267    /*** Use defaults for any invalid values ***/
268    if (atoi(tconf.uid) <= 0)
269	sprintf(tconf.uid, "%d", FTP_UID);
270
271    if (!tconf.group[0])
272	SAFE_STRCPY(tconf.group, FTP_GROUP);
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/etc && chmod 555 %s/etc", tconf.homedir, tconf.homedir);
291	vsystem("mkdir -p %s/pub", tconf.homedir);
292	if (tconf.upload[0]) {
293	    vsystem("mkdir -p %s/%s", tconf.homedir, tconf.upload);
294	    vsystem("chmod 1777 %s/%s", tconf.homedir, tconf.upload);
295	}
296
297	if (DITEM_STATUS(createFtpUser()) == DITEM_SUCCESS) {
298	    msgNotify("Copying password information for anon FTP.");
299	    vsystem("awk -F: '{if ((substr($1, 1, 1) != \"+\") && (substr($1, 1, 1) != \"-\") && ($3 < 10 || $1 == \"ftp\")) print $0}' /etc/master.passwd > %s/etc/master.passwd", tconf.homedir);
300	    vsystem("/usr/sbin/pwd_mkdb -d %s/etc %s/etc/master.passwd && chmod 444 %s/etc/pwd.db", tconf.homedir, tconf.homedir, tconf.homedir);
301	    vsystem("rm -f %s/etc/master.passwd %s/etc/spwd.db", tconf.homedir, tconf.homedir);
302	    vsystem("awk -F: '!/^#/ {if ((substr($1, 1, 1) != \"+\") && (substr($1, 1, 1) != \"-\") && ($3 < 100)) printf \"%%s:*:%%s:\\n\", $1, $3}' /etc/group > %s/etc/group && chmod 444 %s/etc/group", tconf.homedir, tconf.homedir);
303	    vsystem("chown -R root:%s %s/pub", tconf.group, tconf.homedir);
304	}
305	else {
306	    msgConfirm("Unable to create FTP user!  Anonymous FTP setup failed.");
307	    i = DITEM_FAILURE;
308	}
309
310	if (!msgYesNo("Create a welcome message file for anonymous FTP users?")) {
311	    char cmd[256];
312	    vsystem("echo Your welcome message here. > %s/etc/%s", tconf.homedir, MOTD_FILE);
313	    sprintf(cmd, "%s %s/etc/%s", variable_get(VAR_EDITOR), tconf.homedir, MOTD_FILE);
314	    if (!systemExecute(cmd))
315		i = DITEM_SUCCESS;
316	    else
317		i = DITEM_FAILURE;
318	}
319    }
320    else {
321	msgConfirm("Invalid Directory: %s\n"
322		   "Anonymous FTP will not be set up.", tconf.homedir);
323	i = DITEM_FAILURE;
324    }
325    if (DITEM_STATUS(i) == DITEM_SUCCESS)
326	variable_set2("anon_ftp", "YES", 0);
327    return i | DITEM_RESTORE;
328}
329