112156Sjkh/*
212156Sjkh * The new sysinstall program.
312156Sjkh *
412156Sjkh * This is probably the last program in the `sysinstall' line - the next
512156Sjkh * generation being essentially a complete rewrite.
612156Sjkh *
750479Speter * $FreeBSD$
812156Sjkh *
912156Sjkh * Copyright (c) 1995
1012156Sjkh *	Coranth Gryphon.  All rights reserved.
1117985Sjkh * Copyright (c) 1996
1217985Sjkh *	Jordan K. Hubbard.  All rights reserved.
1312156Sjkh *
1412156Sjkh * Redistribution and use in source and binary forms, with or without
1512156Sjkh * modification, are permitted provided that the following conditions
1612156Sjkh * are met:
1712156Sjkh * 1. Redistributions of source code must retain the above copyright
1812156Sjkh *    notice, this list of conditions and the following disclaimer,
1912156Sjkh *    verbatim and that no modifications are made prior to this
2012156Sjkh *    point in the file.
2112156Sjkh * 2. Redistributions in binary form must reproduce the above copyright
2212156Sjkh *    notice, this list of conditions and the following disclaimer in the
2312156Sjkh *    documentation and/or other materials provided with the distribution.
2412156Sjkh *
2517985Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
2612156Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2712156Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2817985Sjkh * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR THEIR PETS BE LIABLE
2912156Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3012156Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3112156Sjkh * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
3212156Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3312156Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3412156Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3512156Sjkh * SUCH DAMAGE.
3612156Sjkh *
3712156Sjkh */
3812156Sjkh
3921243Sjkh#include "sysinstall.h"
4012156Sjkh#include <sys/param.h>
4112156Sjkh#include <pwd.h>
4212156Sjkh#include <grp.h>
4312156Sjkh
4412156Sjkh/* This doesn't change until FTP itself changes */
4512156Sjkh
4612156Sjkh#define FTP_NAME	"ftp"
4712182Sjkh#define MOTD_FILE	"ftpmotd"
4812156Sjkh
4912156Sjkh/* These change if we want to use different defaults */
5012156Sjkh
5112156Sjkh#define FTP_UID		14
52170566Sceri#define FTP_GID		14
53170566Sceri#define FTP_GROUP	"ftp"
5412156Sjkh#define FTP_UPLOAD	"incoming"
5512156Sjkh#define FTP_COMMENT	"Anonymous FTP Admin"
5614321Sjkh#define FTP_HOMEDIR	"/var/ftp"
5712156Sjkh
5812156Sjkh#define ANONFTP_HELPFILE "anonftp"
5912156Sjkh
6012156Sjkh/* Set up the structure to hold configuration information */
6112156Sjkh/* Note that this is only what we could fit onto the one screen */
6212156Sjkh
6312156Sjkhtypedef struct
6412156Sjkh{
6514321Sjkh    char homedir[64];             /* Home Dir for Anon FTP */
6612156Sjkh    char group[32];               /* Group */
6712156Sjkh    char uid[8];                  /* UID */
6812156Sjkh    char comment[64];             /* PWD Comment */
6912156Sjkh    char upload[32];              /* Upload Dir */
7012156Sjkh} FTPConf;
7112156Sjkh
7212156Sjkhstatic FTPConf tconf;
7312156Sjkh
7412156Sjkh#define ANONFTP_HOMEDIR_LEN       64
7512156Sjkh#define ANONFTP_COMMENT_LEN       64
7612156Sjkh#define ANONFTP_UPLOAD_LEN        32
7712156Sjkh#define ANONFTP_GROUP_LEN         32
7812156Sjkh#define ANONFTP_UID_LEN           8
7912156Sjkh
8012156Sjkhstatic int      okbutton, cancelbutton;
8112156Sjkh
8212156Sjkh/* What the screen size is meant to be */
8312156Sjkh#define ANONFTP_DIALOG_Y         0
8412156Sjkh#define ANONFTP_DIALOG_X         8
8512156Sjkh#define ANONFTP_DIALOG_WIDTH     COLS - 16
8612156Sjkh#define ANONFTP_DIALOG_HEIGHT    LINES - 2
8712156Sjkh
8812156Sjkhstatic Layout layout[] = {
8921243Sjkh#define LAYOUT_UID		0
9014321Sjkh    { 2, 3, 8, ANONFTP_UID_LEN - 1,
9112156Sjkh      "UID:", "What user ID to assign to FTP Admin",
9212156Sjkh      tconf.uid, STRINGOBJ, NULL },
9321243Sjkh#define LAYOUT_GROUP		1
9414321Sjkh    { 2, 15, 15, ANONFTP_GROUP_LEN - 1,
9512156Sjkh      "Group:",  "Group name that ftp process belongs to",
9612156Sjkh      tconf.group, STRINGOBJ, NULL },
9721243Sjkh#define LAYOUT_COMMENT		2
9814321Sjkh    { 2, 35, 24, ANONFTP_COMMENT_LEN - 1,
9912156Sjkh      "Comment:", "Password file comment for FTP Admin",
10012156Sjkh      tconf.comment, STRINGOBJ, NULL },
10121243Sjkh#define LAYOUT_HOMEDIR		3
10214321Sjkh    { 9, 10, 43, ANONFTP_HOMEDIR_LEN - 1,
10312156Sjkh      "FTP Root Directory:",
10412156Sjkh      "The top directory to chroot to when doing anonymous ftp",
10512156Sjkh      tconf.homedir, STRINGOBJ, NULL },
10621243Sjkh#define LAYOUT_UPLOAD		4
10714321Sjkh    { 14, 20, 22, ANONFTP_UPLOAD_LEN - 1,
108137781Sjhb      "Upload Subdirectory:", "Designated sub-directory that holds uploads (leave empty for none)",
10912156Sjkh      tconf.upload, STRINGOBJ, NULL },
11021243Sjkh#define LAYOUT_OKBUTTON		5
11114321Sjkh    { 19, 15, 0, 0,
11212156Sjkh      "OK", "Select this if you are happy with these settings",
11312156Sjkh      &okbutton, BUTTONOBJ, NULL },
11421243Sjkh#define LAYOUT_CANCELBUTTON	6
11514321Sjkh    { 19, 35, 0, 0,
11612156Sjkh      "CANCEL", "Select this if you wish to cancel this screen",
11712156Sjkh      &cancelbutton, BUTTONOBJ, NULL },
118156123Sjhb    LAYOUT_END,
11912156Sjkh};
12012156Sjkh
12183842Smurraystatic int
12214670SjkhcreateFtpUser(void)
12312156Sjkh{
12414321Sjkh    struct passwd *tpw;
12514321Sjkh    struct group  *tgrp;
12614321Sjkh    char pwline[256];
12714321Sjkh    char *tptr;
12814321Sjkh    int gid;
12914321Sjkh    FILE *fptr;
13014321Sjkh
13114321Sjkh    if ((gid = atoi(tconf.group)) <= 0) {
13214321Sjkh	if (!(tgrp = getgrnam(tconf.group))) {
13314321Sjkh	    /* group does not exist, create it by name */
13414321Sjkh
13514321Sjkh	    tptr = msgGetInput("14", "What group ID to use for group %s ?", tconf.group);
13614321Sjkh	    if (tptr && *tptr && ((gid = atoi(tptr)) > 0)) {
13714321Sjkh		if ((fptr = fopen(_PATH_GROUP,"a"))) {
13814321Sjkh		    fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
13914321Sjkh		    fclose(fptr);
14014321Sjkh		}
14114321Sjkh	    }
14214321Sjkh	    else
14314321Sjkh		gid = FTP_GID;
14414321Sjkh	}
14512156Sjkh	else
14614321Sjkh	    gid = tgrp->gr_gid;
14712156Sjkh    }
14814321Sjkh    else if (!getgrgid(gid)) {
14912156Sjkh	/* group does not exist, create it by number */
15014321Sjkh
151170566Sceri	tptr = msgGetInput("ftp", "What group name to use for gid %d ?", gid);
15212214Sjkh	if (tptr && *tptr) {
15320247Sjkh	    SAFE_STRCPY(tconf.group, tptr);
15414321Sjkh	    if ((tgrp = getgrnam(tconf.group))) {
15514321Sjkh		gid = tgrp->gr_gid;
15614321Sjkh	    }
15714321Sjkh	    else if ((fptr = fopen(_PATH_GROUP,"a"))) {
15814321Sjkh		fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
15914321Sjkh		fclose(fptr);
16014321Sjkh	    }
16112214Sjkh	}
16212156Sjkh    }
16314321Sjkh
16414321Sjkh    if ((tpw = getpwnam(FTP_NAME))) {
16514321Sjkh	if (tpw->pw_uid != FTP_UID)
16614321Sjkh	    msgConfirm("FTP user already exists with a different uid.");
16714321Sjkh
16821243Sjkh	return DITEM_SUCCESS; 	/* succeeds if already exists */
16912156Sjkh    }
17014321Sjkh
17124548Sjkh    sprintf(pwline, "%s:*:%s:%d::0:0:%s:%s:/nonexistent\n", FTP_NAME, tconf.uid, gid, tconf.comment, tconf.homedir);
17214321Sjkh
17314321Sjkh    fptr = fopen(_PATH_MASTERPASSWD,"a");
17414321Sjkh    if (! fptr) {
17514321Sjkh	msgConfirm("Could not open master password file.");
17621243Sjkh	return DITEM_FAILURE;
17714321Sjkh    }
17879304Skris    fprintf(fptr, "%s", pwline);
17914321Sjkh    fclose(fptr);
18014321Sjkh    msgNotify("Remaking password file: %s", _PATH_MASTERPASSWD);
18114321Sjkh    vsystem("pwd_mkdb -p %s", _PATH_MASTERPASSWD);
18254722Sjkh    return DITEM_SUCCESS | DITEM_RESTORE;
18312156Sjkh}
18412156Sjkh
18512156Sjkh/* This is it - how to get the setup values */
18615355Sjkhstatic int
18714670SjkhanonftpOpenDialog(void)
18812156Sjkh{
18912156Sjkh    WINDOW              *ds_win;
19021243Sjkh    ComposeObj		*obj = NULL;
19121243Sjkh    int                 n = 0, cancel = FALSE;
19212156Sjkh    int                 max;
19312156Sjkh    char                title[80];
19454587Sjkh    WINDOW		*w = savescr();
19554587Sjkh
19612156Sjkh    /* We need a curses window */
19721243Sjkh    if (!(ds_win = openLayoutDialog(ANONFTP_HELPFILE, " Anonymous FTP Configuration ",
19821243Sjkh			      ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, ANONFTP_DIALOG_WIDTH, ANONFTP_DIALOG_HEIGHT))) {
19914670Sjkh	beep();
20014670Sjkh	msgConfirm("Cannot open anonymous ftp dialog window!!");
20154587Sjkh	restorescr(w);
20221243Sjkh	return DITEM_FAILURE;
20314670Sjkh    }
20412156Sjkh
20521243Sjkh    /* Draw a sub-box for the path configuration */
20621243Sjkh    draw_box(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 8,
20721243Sjkh	     ANONFTP_DIALOG_HEIGHT - 11, ANONFTP_DIALOG_WIDTH - 17,
20814321Sjkh	     dialog_attr, border_attr);
20912156Sjkh    wattrset(ds_win, dialog_attr);
21012156Sjkh    sprintf(title, " Path Configuration ");
21112156Sjkh    mvwaddstr(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 22, title);
21212156Sjkh
21312156Sjkh    /** Initialize the config Data Structure **/
21412156Sjkh    bzero(&tconf, sizeof(tconf));
21512156Sjkh
21620247Sjkh    SAFE_STRCPY(tconf.group, FTP_GROUP);
21720247Sjkh    SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
21820247Sjkh    SAFE_STRCPY(tconf.comment, FTP_COMMENT);
21920247Sjkh    SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
22012156Sjkh    sprintf(tconf.uid, "%d", FTP_UID);
22112156Sjkh
22212156Sjkh    /* Some more initialisation before we go into the main input loop */
22321243Sjkh    obj = initLayoutDialog(ds_win, layout, ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, &max);
22421243Sjkh
22521243Sjkh    cancelbutton = okbutton = 0;
22621243Sjkh    while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel));
22721243Sjkh
22812156Sjkh    /* Clear this crap off the screen */
22923588Sjkh    delwin(ds_win);
23012156Sjkh    use_helpfile(NULL);
23154587Sjkh    restorescr(w);
23212156Sjkh    if (cancel)
23315242Sjkh	return DITEM_FAILURE;
23415242Sjkh    return DITEM_SUCCESS;
23512156Sjkh}
23612156Sjkh
23712156Sjkhint
23883842SmurrayconfigAnonFTP(dialogMenuItem *self __unused)
23912156Sjkh{
24012156Sjkh    int i;
24181023Srwatson
24281023Srwatson
24381023Srwatson    if (msgYesNo("Anonymous FTP permits un-authenticated users to connect to the system\n"
24481023Srwatson		 "FTP server, if FTP service is enabled.  Anonymous users are\n"
24581023Srwatson		 "restricted to a specific subset of the file system, and the default\n"
24681023Srwatson		 "configuration provides a drop-box incoming directory to which uploads\n"
247101441Sschweikh		 "are permitted.  You must separately enable both inetd(8), and enable\n"
24881023Srwatson		 "ftpd(8) in inetd.conf(5) for FTP services to be available.  If you\n"
24981023Srwatson		 "did not do so earlier, you will have the opportunity to enable inetd(8)\n"
25081023Srwatson		 "again later.\n\n"
251137781Sjhb		 "If you want the server to be read-only you should leave the upload\n"
252137781Sjhb		 "directory option empty and add the -r command-line option to ftpd(8)\n"
253137781Sjhb                 "in inetd.conf(5)\n\n"
25481023Srwatson		 "Do you wish to continue configuring anonymous FTP?")) {
25581023Srwatson	return DITEM_FAILURE;
25681023Srwatson    }
25714321Sjkh
25812156Sjkh    /* Be optimistic */
25915242Sjkh    i = DITEM_SUCCESS;
26014321Sjkh
26112156Sjkh    i = anonftpOpenDialog();
26215419Sjkh    if (DITEM_STATUS(i) != DITEM_SUCCESS) {
26312214Sjkh	msgConfirm("Configuration of Anonymous FTP cancelled per user request.");
26454587Sjkh	return i;
26512156Sjkh    }
26614321Sjkh
26712214Sjkh    /*** Use defaults for any invalid values ***/
26812156Sjkh    if (atoi(tconf.uid) <= 0)
26912156Sjkh	sprintf(tconf.uid, "%d", FTP_UID);
27012156Sjkh
27112214Sjkh    if (!tconf.group[0])
27220247Sjkh	SAFE_STRCPY(tconf.group, FTP_GROUP);
27312156Sjkh
27412156Sjkh    /*** If the user did not specify a directory, use default ***/
27512156Sjkh
27615419Sjkh    if (tconf.homedir[strlen(tconf.homedir) - 1] == '/')
27715419Sjkh	tconf.homedir[strlen(tconf.homedir) - 1] = '\0';
27812156Sjkh
27912156Sjkh    if (!tconf.homedir[0])
28020247Sjkh	SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
28112156Sjkh
28212156Sjkh    /*** If HomeDir does not exist, create it ***/
28312156Sjkh
28415417Sjkh    if (!directory_exists(tconf.homedir))
28515355Sjkh	vsystem("mkdir -p %s", tconf.homedir);
28614321Sjkh
28714670Sjkh    if (directory_exists(tconf.homedir)) {
28814321Sjkh	msgNotify("Configuring %s for use by anon FTP.", tconf.homedir);
289154409Sceri	vsystem("chmod 555 %s && chown root:%s %s", tconf.homedir, tconf.group, tconf.homedir);
29014321Sjkh	vsystem("mkdir %s/etc && chmod 555 %s/etc", tconf.homedir, tconf.homedir);
29114321Sjkh	vsystem("mkdir -p %s/pub", tconf.homedir);
292137781Sjhb	if (tconf.upload[0]) {
293137781Sjhb	    vsystem("mkdir -p %s/%s", tconf.homedir, tconf.upload);
294137781Sjhb	    vsystem("chmod 1777 %s/%s", tconf.homedir, tconf.upload);
295137781Sjhb	}
29614321Sjkh
29715419Sjkh	if (DITEM_STATUS(createFtpUser()) == DITEM_SUCCESS) {
29814321Sjkh	    msgNotify("Copying password information for anon FTP.");
299137781Sjhb	    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);
300137781Sjhb	    vsystem("/usr/sbin/pwd_mkdb -d %s/etc %s/etc/master.passwd && chmod 444 %s/etc/pwd.db", tconf.homedir, tconf.homedir, tconf.homedir);
301137781Sjhb	    vsystem("rm -f %s/etc/master.passwd %s/etc/spwd.db", tconf.homedir, tconf.homedir);
302154410Sceri	    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);
303154409Sceri	    vsystem("chown -R root:%s %s/pub", tconf.group, tconf.homedir);
30414321Sjkh	}
30514321Sjkh	else {
30614321Sjkh	    msgConfirm("Unable to create FTP user!  Anonymous FTP setup failed.");
30715242Sjkh	    i = DITEM_FAILURE;
30814321Sjkh	}
30914321Sjkh
31014321Sjkh	if (!msgYesNo("Create a welcome message file for anonymous FTP users?")) {
31114321Sjkh	    char cmd[256];
31214321Sjkh	    vsystem("echo Your welcome message here. > %s/etc/%s", tconf.homedir, MOTD_FILE);
31314763Sjkh	    sprintf(cmd, "%s %s/etc/%s", variable_get(VAR_EDITOR), tconf.homedir, MOTD_FILE);
31415242Sjkh	    if (!systemExecute(cmd))
31515242Sjkh		i = DITEM_SUCCESS;
31615242Sjkh	    else
31715242Sjkh		i = DITEM_FAILURE;
31814321Sjkh	}
31914321Sjkh    }
32012214Sjkh    else {
32112214Sjkh	msgConfirm("Invalid Directory: %s\n"
32212214Sjkh		   "Anonymous FTP will not be set up.", tconf.homedir);
32315242Sjkh	i = DITEM_FAILURE;
32414321Sjkh    }
32515419Sjkh    if (DITEM_STATUS(i) == DITEM_SUCCESS)
32643685Sjkh	variable_set2("anon_ftp", "YES", 0);
32754722Sjkh    return i | DITEM_RESTORE;
32812156Sjkh}
329