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