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