10Sstevel@tonic-gate /*
2*12333SMilan.Jurik@Sun.COM * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
30Sstevel@tonic-gate */
40Sstevel@tonic-gate
50Sstevel@tonic-gate /****************************************************************************
60Sstevel@tonic-gate
70Sstevel@tonic-gate Copyright (c) 1999,2000,2001 WU-FTPD Development Group.
80Sstevel@tonic-gate All rights reserved.
90Sstevel@tonic-gate
100Sstevel@tonic-gate Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
110Sstevel@tonic-gate The Regents of the University of California.
120Sstevel@tonic-gate Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
130Sstevel@tonic-gate Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
140Sstevel@tonic-gate Portions Copyright (c) 1989 Massachusetts Institute of Technology.
150Sstevel@tonic-gate Portions Copyright (c) 1998 Sendmail, Inc.
160Sstevel@tonic-gate Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
170Sstevel@tonic-gate Portions Copyright (c) 1997 by Stan Barber.
180Sstevel@tonic-gate Portions Copyright (c) 1997 by Kent Landfield.
190Sstevel@tonic-gate Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
200Sstevel@tonic-gate Free Software Foundation, Inc.
210Sstevel@tonic-gate
220Sstevel@tonic-gate Use and distribution of this software and its source code are governed
230Sstevel@tonic-gate by the terms and conditions of the WU-FTPD Software License ("LICENSE").
240Sstevel@tonic-gate
250Sstevel@tonic-gate If you did not receive a copy of the license, it may be obtained online
260Sstevel@tonic-gate at http://www.wu-ftpd.org/license.html.
270Sstevel@tonic-gate
280Sstevel@tonic-gate $Id: ftpd.c,v 1.111 2000/07/01 18:17:39 wuftpd Exp $
290Sstevel@tonic-gate
300Sstevel@tonic-gate ****************************************************************************/
310Sstevel@tonic-gate /* FTP server. */
320Sstevel@tonic-gate #include "config.h"
330Sstevel@tonic-gate
340Sstevel@tonic-gate #include <sys/types.h>
350Sstevel@tonic-gate #include <sys/param.h>
360Sstevel@tonic-gate #include <sys/stat.h>
370Sstevel@tonic-gate #include <sys/ioctl.h>
380Sstevel@tonic-gate #include <sys/socket.h>
390Sstevel@tonic-gate #include <sys/file.h>
400Sstevel@tonic-gate #include <sys/wait.h>
410Sstevel@tonic-gate
420Sstevel@tonic-gate #ifdef AIX
430Sstevel@tonic-gate #include <sys/id.h>
440Sstevel@tonic-gate #include <sys/priv.h>
450Sstevel@tonic-gate #include <netinet/if_ether.h>
460Sstevel@tonic-gate #include <net/if_dl.h>
470Sstevel@tonic-gate #endif
480Sstevel@tonic-gate
490Sstevel@tonic-gate #ifdef AUX
500Sstevel@tonic-gate #include <compat.h>
510Sstevel@tonic-gate #endif
520Sstevel@tonic-gate
530Sstevel@tonic-gate #include <netinet/in.h>
540Sstevel@tonic-gate #include <netinet/in_systm.h>
550Sstevel@tonic-gate #include <netinet/ip.h>
560Sstevel@tonic-gate
570Sstevel@tonic-gate #define FTP_NAMES
580Sstevel@tonic-gate #include <arpa/ftp.h>
590Sstevel@tonic-gate #include <arpa/inet.h>
600Sstevel@tonic-gate
610Sstevel@tonic-gate #include <ctype.h>
620Sstevel@tonic-gate #include <stdio.h>
630Sstevel@tonic-gate #include <stdlib.h>
640Sstevel@tonic-gate #include <signal.h>
650Sstevel@tonic-gate #include <pwd.h>
660Sstevel@tonic-gate #include <grp.h>
670Sstevel@tonic-gate #include <setjmp.h>
680Sstevel@tonic-gate #include <errno.h>
690Sstevel@tonic-gate #include <string.h>
700Sstevel@tonic-gate #ifdef INTERNAL_LS
710Sstevel@tonic-gate #ifdef HAVE_GLOB_H
720Sstevel@tonic-gate #include <glob.h>
730Sstevel@tonic-gate #else
740Sstevel@tonic-gate #include <wuftpd_glob.h>
750Sstevel@tonic-gate #endif
760Sstevel@tonic-gate #endif
770Sstevel@tonic-gate #ifdef HAVE_GRP_H
780Sstevel@tonic-gate #include <grp.h>
790Sstevel@tonic-gate #endif
800Sstevel@tonic-gate #include <sys/stat.h>
810Sstevel@tonic-gate
820Sstevel@tonic-gate #define VA_LOCAL_DECL va_list ap;
830Sstevel@tonic-gate #define VA_START(f) va_start(ap, f)
840Sstevel@tonic-gate #define VA_END va_end(ap)
850Sstevel@tonic-gate
860Sstevel@tonic-gate #include "proto.h"
870Sstevel@tonic-gate
880Sstevel@tonic-gate #ifdef HAVE_UFS_QUOTA_H
890Sstevel@tonic-gate #include <ufs/quota.h>
900Sstevel@tonic-gate #endif
910Sstevel@tonic-gate #ifdef HAVE_SYS_FS_UFS_QUOTA_H
920Sstevel@tonic-gate #include <sys/fs/ufs_quota.h>
930Sstevel@tonic-gate #endif
940Sstevel@tonic-gate
950Sstevel@tonic-gate #ifdef HAVE_SYS_SYSLOG_H
960Sstevel@tonic-gate #include <sys/syslog.h>
970Sstevel@tonic-gate #endif
980Sstevel@tonic-gate #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H))
990Sstevel@tonic-gate #include <syslog.h>
1000Sstevel@tonic-gate #endif
1010Sstevel@tonic-gate #ifdef TIME_WITH_SYS_TIME
1020Sstevel@tonic-gate #include <time.h>
1030Sstevel@tonic-gate #include <sys/time.h>
1040Sstevel@tonic-gate #else
1050Sstevel@tonic-gate #ifdef HAVE_SYS_TIME_H
1060Sstevel@tonic-gate #include <sys/time.h>
1070Sstevel@tonic-gate #else
1080Sstevel@tonic-gate #include <time.h>
1090Sstevel@tonic-gate #endif
1100Sstevel@tonic-gate #endif
1110Sstevel@tonic-gate
1120Sstevel@tonic-gate #ifdef HAVE_SYS_SENDFILE_H
1130Sstevel@tonic-gate #include <sys/sendfile.h>
1140Sstevel@tonic-gate #endif
1150Sstevel@tonic-gate
1160Sstevel@tonic-gate #include "conversions.h"
1170Sstevel@tonic-gate #include "extensions.h"
1180Sstevel@tonic-gate
1190Sstevel@tonic-gate #ifdef SHADOW_PASSWORD
1200Sstevel@tonic-gate #include <shadow.h>
1210Sstevel@tonic-gate #endif
1220Sstevel@tonic-gate
1230Sstevel@tonic-gate #include "pathnames.h"
1240Sstevel@tonic-gate
1250Sstevel@tonic-gate #ifdef M_UNIX
1260Sstevel@tonic-gate #include <arpa/nameser.h>
1270Sstevel@tonic-gate #include <resolv.h>
1280Sstevel@tonic-gate #endif
1290Sstevel@tonic-gate
1300Sstevel@tonic-gate #if defined(HAVE_FCNTL_H)
1310Sstevel@tonic-gate #include <fcntl.h>
1320Sstevel@tonic-gate #endif
1330Sstevel@tonic-gate
1340Sstevel@tonic-gate #ifdef HAVE_SYSINFO
1350Sstevel@tonic-gate #include <sys/systeminfo.h>
1360Sstevel@tonic-gate #endif
1370Sstevel@tonic-gate
1380Sstevel@tonic-gate #ifdef KERBEROS
1390Sstevel@tonic-gate #include <sys/types.h>
1400Sstevel@tonic-gate #include <auth.h>
1410Sstevel@tonic-gate #include <krb.h>
1420Sstevel@tonic-gate #endif
1430Sstevel@tonic-gate
1440Sstevel@tonic-gate #ifdef ULTRIX_AUTH
1450Sstevel@tonic-gate #include <auth.h>
1460Sstevel@tonic-gate #include <sys/svcinfo.h>
1470Sstevel@tonic-gate #endif
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate #ifndef HAVE_LSTAT
1500Sstevel@tonic-gate #define lstat stat
1510Sstevel@tonic-gate #endif
1520Sstevel@tonic-gate
1530Sstevel@tonic-gate #ifdef AFS_AUTH
1540Sstevel@tonic-gate #include <afs/stds.h>
1550Sstevel@tonic-gate #include <afs/kautils.h>
1560Sstevel@tonic-gate #endif
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate #ifdef DCE_AUTH
1590Sstevel@tonic-gate #include <dce/rpc.h>
1600Sstevel@tonic-gate #include <dce/sec_login.h>
1610Sstevel@tonic-gate #include <dce/dce_error.h>
1620Sstevel@tonic-gate #endif
1630Sstevel@tonic-gate
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate #ifdef HAVE_DIRENT_H
1660Sstevel@tonic-gate #include <dirent.h>
1670Sstevel@tonic-gate #else
1680Sstevel@tonic-gate #include <sys/dir.h>
1690Sstevel@tonic-gate #endif
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate #if defined(USE_LONGJMP)
1720Sstevel@tonic-gate #define wu_longjmp(x, y) longjmp((x), (y))
1730Sstevel@tonic-gate #define wu_setjmp(x) setjmp(x)
1740Sstevel@tonic-gate #ifndef JMP_BUF
1750Sstevel@tonic-gate #define JMP_BUF jmp_buf
1760Sstevel@tonic-gate #endif
1770Sstevel@tonic-gate #else
1780Sstevel@tonic-gate #define wu_longjmp(x, y) siglongjmp((x), (y))
1790Sstevel@tonic-gate #define wu_setjmp(x) sigsetjmp((x), 1)
1800Sstevel@tonic-gate #ifndef JMP_BUF
1810Sstevel@tonic-gate #define JMP_BUF sigjmp_buf
1820Sstevel@tonic-gate #endif
1830Sstevel@tonic-gate #endif
1840Sstevel@tonic-gate
1850Sstevel@tonic-gate #ifndef MAXHOSTNAMELEN
1860Sstevel@tonic-gate #define MAXHOSTNAMELEN 64 /* may be too big */
1870Sstevel@tonic-gate #endif
1880Sstevel@tonic-gate
1890Sstevel@tonic-gate #ifndef TRUE
1900Sstevel@tonic-gate #define TRUE 1
1910Sstevel@tonic-gate #endif
1920Sstevel@tonic-gate
1930Sstevel@tonic-gate #ifndef FALSE
1940Sstevel@tonic-gate #define FALSE !TRUE
1950Sstevel@tonic-gate #endif
1960Sstevel@tonic-gate
1970Sstevel@tonic-gate #ifdef MAIL_ADMIN
1980Sstevel@tonic-gate #define MAILSERVERS 10
1990Sstevel@tonic-gate #define INCMAILS 10
2000Sstevel@tonic-gate int mailservers = 0;
2010Sstevel@tonic-gate char *mailserver[MAILSERVERS];
2020Sstevel@tonic-gate int incmails = 0;
2030Sstevel@tonic-gate char *incmail[INCMAILS];
2040Sstevel@tonic-gate char *mailfrom;
2050Sstevel@tonic-gate char *email(char *full_address);
2060Sstevel@tonic-gate FILE *SockOpen(char *host, int clientPort);
2070Sstevel@tonic-gate char *SockGets(FILE *sockfp, char *buf, int len);
2080Sstevel@tonic-gate int SockWrite(char *buf, int size, int nels, FILE *sockfp);
2090Sstevel@tonic-gate int SockPrintf(FILE *sockfp, char *format,...);
2100Sstevel@tonic-gate int SockPuts(FILE *sockfp, char *buf);
2110Sstevel@tonic-gate int Reply(FILE *sockfp);
2120Sstevel@tonic-gate int Send(FILE *sockfp, char *format,...);
2130Sstevel@tonic-gate #endif /* MAIL_ADMIN */
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate #if defined(_SCO_DS) && !defined(SIGURG)
2160Sstevel@tonic-gate #define SIGURG SIGUSR1
2170Sstevel@tonic-gate #endif
2180Sstevel@tonic-gate
2190Sstevel@tonic-gate /* File containing login names NOT to be used on this machine. Commonly used
2200Sstevel@tonic-gate * to disallow uucp. */
2210Sstevel@tonic-gate extern int errno;
2220Sstevel@tonic-gate
2230Sstevel@tonic-gate extern char *ctime(const time_t *);
2240Sstevel@tonic-gate #ifndef NO_CRYPT_PROTO
2250Sstevel@tonic-gate extern char *crypt(const char *, const char *);
2260Sstevel@tonic-gate #endif
2270Sstevel@tonic-gate
2280Sstevel@tonic-gate extern char version[];
2290Sstevel@tonic-gate extern char *home; /* pointer to home directory for glob */
2300Sstevel@tonic-gate extern char cbuf[];
2310Sstevel@tonic-gate extern off_t restart_point;
2320Sstevel@tonic-gate extern int yyerrorcalled;
2330Sstevel@tonic-gate
2340Sstevel@tonic-gate struct SOCKSTORAGE ctrl_addr;
2350Sstevel@tonic-gate struct SOCKSTORAGE data_source;
2360Sstevel@tonic-gate struct SOCKSTORAGE data_dest;
2370Sstevel@tonic-gate struct SOCKSTORAGE his_addr;
2380Sstevel@tonic-gate struct SOCKSTORAGE pasv_addr;
2390Sstevel@tonic-gate struct SOCKSTORAGE vect_addr;
2400Sstevel@tonic-gate int route_vectored = 0;
2410Sstevel@tonic-gate int passive_port_min = 1024;
2420Sstevel@tonic-gate int passive_port_max = 65535;
2430Sstevel@tonic-gate int restricted_user = 0;
2440Sstevel@tonic-gate unsigned short data_port = 0;
2450Sstevel@tonic-gate
2460Sstevel@tonic-gate #ifdef INET6
2470Sstevel@tonic-gate int ctrl_v4mapped = 0;
2480Sstevel@tonic-gate int epsv_all = 0;
2490Sstevel@tonic-gate int listen_v4 = 0; /* when set, listen on IPv4 socket in standalone mode */
2500Sstevel@tonic-gate #endif
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate #ifdef VIRTUAL
2530Sstevel@tonic-gate char virtual_root[MAXPATHLEN];
2540Sstevel@tonic-gate char virtual_banner[MAXPATHLEN];
2550Sstevel@tonic-gate char virtual_email[MAXPATHLEN];
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate char virtual_hostname[MAXHOSTNAMELEN];
2580Sstevel@tonic-gate char virtual_address[MAXHOSTNAMELEN];
2590Sstevel@tonic-gate
2600Sstevel@tonic-gate extern int virtual_mode;
2610Sstevel@tonic-gate extern int virtual_ftpaccess;
2620Sstevel@tonic-gate #endif
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate #ifdef QUOTA
2650Sstevel@tonic-gate extern struct dqblk quota;
2660Sstevel@tonic-gate #endif
2670Sstevel@tonic-gate
2680Sstevel@tonic-gate #if defined(USE_GSS)
2690Sstevel@tonic-gate #include "gssutil.h"
2700Sstevel@tonic-gate
2710Sstevel@tonic-gate extern gss_info_t gss_info;
2720Sstevel@tonic-gate
2730Sstevel@tonic-gate int allow_ccc = 0;
2740Sstevel@tonic-gate int ccc_ok = 0;
2750Sstevel@tonic-gate extern char *cur_auth_type;
2760Sstevel@tonic-gate #endif /* USE_GSS */
2770Sstevel@tonic-gate
2780Sstevel@tonic-gate int data;
2790Sstevel@tonic-gate jmp_buf errcatch;
2800Sstevel@tonic-gate JMP_BUF urgcatch;
2810Sstevel@tonic-gate int logged_in = 0;
2820Sstevel@tonic-gate struct passwd *pw;
2830Sstevel@tonic-gate char chroot_path[MAXPATHLEN];
2840Sstevel@tonic-gate int debug = 0;
2850Sstevel@tonic-gate int disable_rfc931 = 0;
2860Sstevel@tonic-gate extern unsigned int timeout_idle;
2870Sstevel@tonic-gate extern unsigned int timeout_maxidle;
2880Sstevel@tonic-gate extern unsigned int timeout_data;
2890Sstevel@tonic-gate extern unsigned int timeout_accept;
2900Sstevel@tonic-gate extern unsigned int timeout_connect;
2910Sstevel@tonic-gate
2920Sstevel@tonic-gate /* previously defaulted to 1, and -l or -L set them to 1, so that there was
2930Sstevel@tonic-gate no way to turn them *off*! Changed so that the manpage reflects common
2940Sstevel@tonic-gate sense. -L is way noisy; -l we'll change to be "just right". _H */
2950Sstevel@tonic-gate int logging = 0;
2960Sstevel@tonic-gate int log_commands = 0;
2970Sstevel@tonic-gate int log_security = 0;
2980Sstevel@tonic-gate int syslogmsg = 0;
2990Sstevel@tonic-gate static int wtmp_logging = 1;
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate #ifdef SECUREOSF
3020Sstevel@tonic-gate #define SecureWare /* Does this mean it works for all SecureWare? */
3030Sstevel@tonic-gate #endif
3040Sstevel@tonic-gate
3050Sstevel@tonic-gate #ifdef HPUX_10_TRUSTED
3060Sstevel@tonic-gate #include <hpsecurity.h>
3070Sstevel@tonic-gate #endif
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate #if defined(SecureWare) || defined(HPUX_10_TRUSTED)
3100Sstevel@tonic-gate #include <prot.h>
3110Sstevel@tonic-gate #endif
3120Sstevel@tonic-gate
3130Sstevel@tonic-gate int anonymous = 1;
3140Sstevel@tonic-gate int guest;
3150Sstevel@tonic-gate int type;
3160Sstevel@tonic-gate int form;
3170Sstevel@tonic-gate int stru; /* avoid C keyword */
3180Sstevel@tonic-gate int mode;
3190Sstevel@tonic-gate int usedefault = 1; /* for data transfers */
3200Sstevel@tonic-gate int pdata = -1; /* for passive mode */
3210Sstevel@tonic-gate int transflag;
3220Sstevel@tonic-gate int ftwflag;
3230Sstevel@tonic-gate off_t file_size;
3240Sstevel@tonic-gate off_t byte_count;
3250Sstevel@tonic-gate int TCPwindowsize = 0; /* 0 = use system default */
3260Sstevel@tonic-gate size_t sendbufsz; /* buffer size to use when sending data */
3270Sstevel@tonic-gate size_t recvbufsz; /* buffer size to use when receiving data */
3280Sstevel@tonic-gate
3290Sstevel@tonic-gate #ifdef TRANSFER_COUNT
3300Sstevel@tonic-gate off_t data_count_total = 0; /* total number of data bytes */
3310Sstevel@tonic-gate off_t data_count_in = 0;
3320Sstevel@tonic-gate off_t data_count_out = 0;
3330Sstevel@tonic-gate off_t byte_count_total = 0; /* total number of general traffic */
3340Sstevel@tonic-gate off_t byte_count_in = 0;
3350Sstevel@tonic-gate off_t byte_count_out = 0;
3360Sstevel@tonic-gate int file_count_total = 0; /* total number of data files */
3370Sstevel@tonic-gate int file_count_in = 0;
3380Sstevel@tonic-gate int file_count_out = 0;
3390Sstevel@tonic-gate int xfer_count_total = 0; /* total number of transfers */
3400Sstevel@tonic-gate int xfer_count_in = 0;
3410Sstevel@tonic-gate int xfer_count_out = 0;
3420Sstevel@tonic-gate #ifdef TRANSFER_LIMIT
3430Sstevel@tonic-gate int file_limit_raw_in = 0;
3440Sstevel@tonic-gate int file_limit_raw_out = 0;
3450Sstevel@tonic-gate int file_limit_raw_total = 0;
3460Sstevel@tonic-gate int file_limit_data_in = 0;
3470Sstevel@tonic-gate int file_limit_data_out = 0;
3480Sstevel@tonic-gate int file_limit_data_total = 0;
3490Sstevel@tonic-gate off_t data_limit_raw_in = 0;
3500Sstevel@tonic-gate off_t data_limit_raw_out = 0;
3510Sstevel@tonic-gate off_t data_limit_raw_total = 0;
3520Sstevel@tonic-gate off_t data_limit_data_in = 0;
3530Sstevel@tonic-gate off_t data_limit_data_out = 0;
3540Sstevel@tonic-gate off_t data_limit_data_total = 0;
3550Sstevel@tonic-gate #ifdef RATIO /* 1998/08/04 K.Wakui */
3560Sstevel@tonic-gate #define TRUNC_KB(n) ((n)/1024+(((n)%1024)?1:0))
3570Sstevel@tonic-gate off_t total_free_dl = 0;
3580Sstevel@tonic-gate int upload_download_rate = 0;
3590Sstevel@tonic-gate int freefile;
3600Sstevel@tonic-gate int is_downloadfree( char * );
3610Sstevel@tonic-gate #endif /* RATIO */
3620Sstevel@tonic-gate #endif
3630Sstevel@tonic-gate #endif
3640Sstevel@tonic-gate
3650Sstevel@tonic-gate int retrieve_is_data = 1; /* !0=data, 0=general traffic -- for 'ls' */
3660Sstevel@tonic-gate char LastFileTransferred[MAXPATHLEN] = "";
3670Sstevel@tonic-gate
3680Sstevel@tonic-gate static char *RootDirectory = NULL;
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate #if !defined(CMASK) || CMASK == 0
3710Sstevel@tonic-gate #undef CMASK
3720Sstevel@tonic-gate #define CMASK 022
3730Sstevel@tonic-gate #endif
3740Sstevel@tonic-gate mode_t defumask = CMASK; /* default umask value */
3750Sstevel@tonic-gate #ifdef ALTERNATE_CD
3760Sstevel@tonic-gate char defhome[] = "/";
3770Sstevel@tonic-gate #endif
3780Sstevel@tonic-gate char tmpline[7];
3790Sstevel@tonic-gate char hostname[MAXHOSTNAMELEN];
3800Sstevel@tonic-gate char remotehost[MAXHOSTNAMELEN];
3810Sstevel@tonic-gate char remoteaddr[MAXHOSTNAMELEN];
3820Sstevel@tonic-gate char *remoteident = "[nowhere yet]";
3830Sstevel@tonic-gate int rhlookup = TRUE; /* when TRUE lookup the remote hosts name */
3840Sstevel@tonic-gate
3853232Sas198278 #if defined(SOLARIS_2) && !defined(NAME_SERVICE_DOOR)
3863232Sas198278 #define NAME_SERVICE_DOOR "/var/run/name_service_door"
3873232Sas198278 #endif
3883232Sas198278
3893232Sas198278 #if defined(SOLARIS_2)
3903232Sas198278 int close_nsdoor(void *cb_data, int fd);
3913232Sas198278 void cleanup_nscd();
3923232Sas198278 #endif
3933232Sas198278
3940Sstevel@tonic-gate /* log failures 27-apr-93 ehk/bm */
3950Sstevel@tonic-gate #define MAXUSERNAMELEN 256
3960Sstevel@tonic-gate char the_user[MAXUSERNAMELEN];
3970Sstevel@tonic-gate
3980Sstevel@tonic-gate /* Access control and logging passwords */
3990Sstevel@tonic-gate /* OFF by default. _H */
4000Sstevel@tonic-gate int use_accessfile = 0;
4010Sstevel@tonic-gate char guestpw[MAXHOSTNAMELEN];
4020Sstevel@tonic-gate char privatepw[MAXHOSTNAMELEN];
4030Sstevel@tonic-gate int nameserved = 0;
4040Sstevel@tonic-gate extern char authuser[];
4050Sstevel@tonic-gate extern int authenticated;
4060Sstevel@tonic-gate extern int keepalive;
4070Sstevel@tonic-gate
4080Sstevel@tonic-gate /* File transfer logging (xferlog) */
4090Sstevel@tonic-gate int xferlog = 0;
4100Sstevel@tonic-gate int log_outbound_xfers = 0;
4110Sstevel@tonic-gate int log_incoming_xfers = 0;
4120Sstevel@tonic-gate char logfile[MAXPATHLEN];
4130Sstevel@tonic-gate
4140Sstevel@tonic-gate /* Allow use of lreply(); this is here since some older FTP clients don't
4150Sstevel@tonic-gate * support continuation messages. In violation of the RFCs... */
4160Sstevel@tonic-gate int dolreplies = 1;
4170Sstevel@tonic-gate
4180Sstevel@tonic-gate /* Spontaneous reply text. To be sent along with next reply to user */
4190Sstevel@tonic-gate char *autospout = NULL;
4200Sstevel@tonic-gate int autospout_free = 0;
4210Sstevel@tonic-gate
4220Sstevel@tonic-gate /* allowed on-the-fly file manipulations (compress, tar) */
4230Sstevel@tonic-gate int mangleopts = 0;
4240Sstevel@tonic-gate
4250Sstevel@tonic-gate /* number of login failures before attempts are logged and FTP *EXITS* */
4260Sstevel@tonic-gate int lgi_failure_threshold = 5;
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate /* Timeout intervals for retrying connections to hosts that don't accept PORT
4290Sstevel@tonic-gate * cmds. This is a kludge, but given the problems with TCP... */
4300Sstevel@tonic-gate #define SWAITMAX 90 /* wait at most 90 seconds */
4310Sstevel@tonic-gate #define SWAITINT 5 /* interval between retries */
4320Sstevel@tonic-gate
4330Sstevel@tonic-gate int swaitmax = SWAITMAX;
4340Sstevel@tonic-gate int swaitint = SWAITINT;
4350Sstevel@tonic-gate
4360Sstevel@tonic-gate SIGNAL_TYPE lostconn(int sig);
4370Sstevel@tonic-gate SIGNAL_TYPE randomsig(int sig);
4380Sstevel@tonic-gate SIGNAL_TYPE myoob(int sig);
4390Sstevel@tonic-gate FILE *getdatasock(char *mode);
4400Sstevel@tonic-gate FILE *dataconn(char *name, off_t size, char *mode);
4410Sstevel@tonic-gate void setproctitle(const char *fmt,...);
4420Sstevel@tonic-gate void initsetproctitle(int, char **, char **);
4430Sstevel@tonic-gate void reply(int, char *fmt,...);
4440Sstevel@tonic-gate void lreply(int, char *fmt,...);
4450Sstevel@tonic-gate
4460Sstevel@tonic-gate #ifndef HAVE_VSNPRINTF
4470Sstevel@tonic-gate extern int vsnprintf(char *, size_t, const char *, va_list);
4480Sstevel@tonic-gate #endif
4490Sstevel@tonic-gate
4500Sstevel@tonic-gate #ifndef HAVE_SNPRINTF
4510Sstevel@tonic-gate extern int snprintf(char *, size_t, const char *,...);
4520Sstevel@tonic-gate #endif
4530Sstevel@tonic-gate
4540Sstevel@tonic-gate #ifdef NEED_SIGFIX
4550Sstevel@tonic-gate extern sigset_t block_sigmask; /* defined in sigfix.c */
4560Sstevel@tonic-gate #endif
4570Sstevel@tonic-gate
4580Sstevel@tonic-gate char proctitle[BUFSIZ]; /* initial part of title */
4590Sstevel@tonic-gate
4600Sstevel@tonic-gate #if defined(SKEY) && defined(OPIE)
4610Sstevel@tonic-gate #error YOU SHOULD NOT HAVE BOTH SKEY AND OPIE DEFINED!!!!!
4620Sstevel@tonic-gate #endif
4630Sstevel@tonic-gate
4640Sstevel@tonic-gate #ifdef SKEY
4650Sstevel@tonic-gate #include <skey.h>
4660Sstevel@tonic-gate int pwok = 0;
4670Sstevel@tonic-gate #endif
4680Sstevel@tonic-gate
4690Sstevel@tonic-gate #ifdef OPIE
4700Sstevel@tonic-gate #include <opie.h>
4710Sstevel@tonic-gate int pwok = 0;
4720Sstevel@tonic-gate int af_pwok = 0;
4730Sstevel@tonic-gate struct opie opiestate;
4740Sstevel@tonic-gate #endif
4750Sstevel@tonic-gate
4760Sstevel@tonic-gate #ifdef KERBEROS
4770Sstevel@tonic-gate void init_krb();
4780Sstevel@tonic-gate void end_krb();
4790Sstevel@tonic-gate char krb_ticket_name[100];
4800Sstevel@tonic-gate #endif /* KERBEROS */
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate #ifdef ULTRIX_AUTH
4830Sstevel@tonic-gate int ultrix_check_pass(char *passwd, char *xpasswd);
4840Sstevel@tonic-gate #endif
4850Sstevel@tonic-gate
4860Sstevel@tonic-gate #ifdef USE_PAM
4870Sstevel@tonic-gate #if defined(ULTRIX_AUTH) || defined(SECUREOSF) || defined(KERBEROS) || defined(SKEY) || defined (OPIE) || defined (BSD_AUTH)
4880Sstevel@tonic-gate #error No other auth methods are allowed with PAM.
4890Sstevel@tonic-gate #endif
4900Sstevel@tonic-gate #include <security/pam_appl.h>
4910Sstevel@tonic-gate static int pam_check_pass(char *user, char *passwd);
4920Sstevel@tonic-gate pam_handle_t *pamh;
4930Sstevel@tonic-gate #endif
4940Sstevel@tonic-gate
4950Sstevel@tonic-gate #ifndef INTERNAL_LS
4960Sstevel@tonic-gate /* ls program commands and options for lreplies on and off */
4970Sstevel@tonic-gate char ls_long[BUFSIZ * 2];
4980Sstevel@tonic-gate char ls_short[BUFSIZ * 2];
4990Sstevel@tonic-gate char ls_plain[BUFSIZ * 2];
5000Sstevel@tonic-gate #endif
5010Sstevel@tonic-gate
5020Sstevel@tonic-gate #define FTPD_OPTS ":4aAdiIlLoP:qQr:t:T:u:vVwWxX"
5030Sstevel@tonic-gate #if defined(DAEMON)
5040Sstevel@tonic-gate # define DAEMON_OPTS "p:sS"
5050Sstevel@tonic-gate #else /* !(defined(DAEMON)) */
5060Sstevel@tonic-gate # define DAEMON_OPTS
5070Sstevel@tonic-gate #endif /* !(defined(DAEMON)) */
5080Sstevel@tonic-gate #if defined(USE_GSS)
5090Sstevel@tonic-gate # define GSS_OPTS "CK"
5100Sstevel@tonic-gate #else /* !(defined(USE_GSS)) */
5110Sstevel@tonic-gate # define GSS_OPTS
5120Sstevel@tonic-gate #endif /* !(defined(USE_GSS)) */
5130Sstevel@tonic-gate
5140Sstevel@tonic-gate /* Some systems use one format, some another. This takes care of the garbage */
5150Sstevel@tonic-gate #ifndef L_FORMAT /* Autoconf detects this... */
5160Sstevel@tonic-gate #if (defined(BSD) && (BSD >= 199103)) && !defined(LONGOFF_T)
5170Sstevel@tonic-gate #define L_FORMAT "qd"
5180Sstevel@tonic-gate #else
5190Sstevel@tonic-gate #ifdef _AIX42
5200Sstevel@tonic-gate #define L_FORMAT "lld"
5210Sstevel@tonic-gate #else
5220Sstevel@tonic-gate #ifdef SOLARIS_2
5230Sstevel@tonic-gate #define L_FORMAT "ld"
5240Sstevel@tonic-gate #else
5250Sstevel@tonic-gate #define L_FORMAT "d"
5260Sstevel@tonic-gate #endif
5270Sstevel@tonic-gate #endif
5280Sstevel@tonic-gate #endif
5290Sstevel@tonic-gate #endif
5300Sstevel@tonic-gate
5310Sstevel@tonic-gate #ifdef DAEMON
5320Sstevel@tonic-gate int be_daemon = 0; /* Run standalone? */
5330Sstevel@tonic-gate int daemon_port = 0;
5340Sstevel@tonic-gate static void do_daemon(void);
5350Sstevel@tonic-gate #endif
5360Sstevel@tonic-gate int Bypass_PID_Files = 0;
5370Sstevel@tonic-gate
5380Sstevel@tonic-gate #ifdef OTHER_PASSWD
5390Sstevel@tonic-gate #include "getpwnam.h"
5400Sstevel@tonic-gate char _path_passwd[MAXPATHLEN];
5410Sstevel@tonic-gate #ifdef SHADOW_PASSWORD
5420Sstevel@tonic-gate char _path_shadow[MAXPATHLEN];
5430Sstevel@tonic-gate #endif
5440Sstevel@tonic-gate #endif
5450Sstevel@tonic-gate #if defined(USE_PAM) && defined(OTHER_PASSWD)
5460Sstevel@tonic-gate int use_pam = 1;
5470Sstevel@tonic-gate #else
5480Sstevel@tonic-gate int use_pam = 0;
5490Sstevel@tonic-gate #endif
5500Sstevel@tonic-gate
5510Sstevel@tonic-gate void print_copyright(void);
5520Sstevel@tonic-gate char *mapping_getcwd(char *path, size_t size);
5530Sstevel@tonic-gate
5540Sstevel@tonic-gate void dolog(struct SOCKSTORAGE *);
5550Sstevel@tonic-gate
5560Sstevel@tonic-gate #ifdef THROUGHPUT
5570Sstevel@tonic-gate extern void throughput_calc(char *, int *, double *);
5580Sstevel@tonic-gate extern void throughput_adjust(char *);
5590Sstevel@tonic-gate #endif
5600Sstevel@tonic-gate
5610Sstevel@tonic-gate time_t login_time;
5620Sstevel@tonic-gate time_t limit_time = 0;
5630Sstevel@tonic-gate
5640Sstevel@tonic-gate int regexmatch(char *name, char *rgexp);
5650Sstevel@tonic-gate
5660Sstevel@tonic-gate int pasv_allowed(char *remoteaddr);
5670Sstevel@tonic-gate int port_allowed(char *remoteaddr);
5680Sstevel@tonic-gate
5690Sstevel@tonic-gate #if sparc && !__svr4__
5700Sstevel@tonic-gate int fclose(FILE *);
5710Sstevel@tonic-gate #endif
5720Sstevel@tonic-gate
alarm_signal(int sig)5730Sstevel@tonic-gate static SIGNAL_TYPE alarm_signal(int sig)
5740Sstevel@tonic-gate {
5750Sstevel@tonic-gate }
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate static FILE *draconian_FILE = NULL;
5780Sstevel@tonic-gate
draconian_alarm_signal(int sig)5790Sstevel@tonic-gate static SIGNAL_TYPE draconian_alarm_signal(int sig)
5800Sstevel@tonic-gate {
5810Sstevel@tonic-gate if (draconian_FILE != NULL) {
5820Sstevel@tonic-gate fclose(draconian_FILE);
5830Sstevel@tonic-gate draconian_FILE = NULL;
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
5860Sstevel@tonic-gate }
5870Sstevel@tonic-gate
socket_flush_wait(FILE * file)5880Sstevel@tonic-gate static void socket_flush_wait(FILE *file)
5890Sstevel@tonic-gate {
5900Sstevel@tonic-gate static int flushwait = TRUE;
5910Sstevel@tonic-gate static int first_time = TRUE;
5920Sstevel@tonic-gate char c;
5930Sstevel@tonic-gate int set;
5940Sstevel@tonic-gate int fd = fileno(file);
5950Sstevel@tonic-gate int serrno = errno;
5960Sstevel@tonic-gate struct aclmember *entry;
5970Sstevel@tonic-gate
5980Sstevel@tonic-gate if (first_time) {
5990Sstevel@tonic-gate entry = NULL;
6000Sstevel@tonic-gate /* flush-wait yes|no [typelist] */
6010Sstevel@tonic-gate while (getaclentry("flush-wait", &entry)) {
6020Sstevel@tonic-gate if (!ARG0)
6030Sstevel@tonic-gate continue;
6040Sstevel@tonic-gate if (strcasecmp(ARG0, "yes") == 0)
6050Sstevel@tonic-gate set = TRUE;
6060Sstevel@tonic-gate else if (strcasecmp(ARG0, "no") == 0)
6070Sstevel@tonic-gate set = FALSE;
6080Sstevel@tonic-gate else
6090Sstevel@tonic-gate continue;
6100Sstevel@tonic-gate
6110Sstevel@tonic-gate if (!ARG1)
6120Sstevel@tonic-gate flushwait = set;
6130Sstevel@tonic-gate else if (type_match(ARG1)) {
6140Sstevel@tonic-gate flushwait = set;
6150Sstevel@tonic-gate break;
6160Sstevel@tonic-gate }
6170Sstevel@tonic-gate }
6180Sstevel@tonic-gate first_time = FALSE;
6190Sstevel@tonic-gate }
6200Sstevel@tonic-gate if (flushwait) {
6210Sstevel@tonic-gate if (draconian_FILE != NULL)
6220Sstevel@tonic-gate shutdown(fd, 1);
6230Sstevel@tonic-gate if (draconian_FILE != NULL)
6240Sstevel@tonic-gate read(fd, &c, 1);
6250Sstevel@tonic-gate }
6260Sstevel@tonic-gate errno = serrno;
6270Sstevel@tonic-gate /*
6280Sstevel@tonic-gate * GAL - the read() here should be checked to ensure it returned 0 (indicating
6290Sstevel@tonic-gate * EOF) or -1 (an error occurred). Anything else (real data) is a protocol
6300Sstevel@tonic-gate * error.
6310Sstevel@tonic-gate */
6320Sstevel@tonic-gate }
6330Sstevel@tonic-gate
IPClassOfService(const char * type)6340Sstevel@tonic-gate static int IPClassOfService(const char *type)
6350Sstevel@tonic-gate {
6360Sstevel@tonic-gate int ipcos = -1, value;
6370Sstevel@tonic-gate char *endp;
6380Sstevel@tonic-gate struct aclmember *entry = NULL;
6390Sstevel@tonic-gate
6400Sstevel@tonic-gate /* ipcos control|data <value> [<typelist>] */
6410Sstevel@tonic-gate while (getaclentry("ipcos", &entry)) {
6420Sstevel@tonic-gate if (ARG0 && ARG1) {
6430Sstevel@tonic-gate if (strcasecmp(type, ARG0) == 0) {
6440Sstevel@tonic-gate if (!ARG2) {
6450Sstevel@tonic-gate errno = 0;
6460Sstevel@tonic-gate value = (int) strtol(ARG1, &endp, 0);
6470Sstevel@tonic-gate if ((errno == 0) && (value >= 0) && (*endp == '\0'))
6480Sstevel@tonic-gate ipcos = value;
6490Sstevel@tonic-gate }
6500Sstevel@tonic-gate else if (type_match(ARG2)) {
6510Sstevel@tonic-gate errno = 0;
6520Sstevel@tonic-gate value = (int) strtol(ARG1, &endp, 0);
6530Sstevel@tonic-gate if ((errno == 0) && (value >= 0) && (*endp == '\0')) {
6540Sstevel@tonic-gate ipcos = value;
6550Sstevel@tonic-gate break;
6560Sstevel@tonic-gate }
6570Sstevel@tonic-gate }
6580Sstevel@tonic-gate }
6590Sstevel@tonic-gate }
6600Sstevel@tonic-gate }
6610Sstevel@tonic-gate return ipcos;
6620Sstevel@tonic-gate }
6630Sstevel@tonic-gate
main(int argc,char ** argv,char ** envp)6640Sstevel@tonic-gate int main(int argc, char **argv, char **envp)
6650Sstevel@tonic-gate {
6660Sstevel@tonic-gate #if defined(UNIXWARE) || defined(AIX)
6670Sstevel@tonic-gate size_t addrlen;
6680Sstevel@tonic-gate #else
6690Sstevel@tonic-gate int addrlen;
6700Sstevel@tonic-gate #endif
6710Sstevel@tonic-gate int on = 1;
6720Sstevel@tonic-gate int cos;
6730Sstevel@tonic-gate int c;
6740Sstevel@tonic-gate #ifndef INTERNAL_LS
6750Sstevel@tonic-gate int which;
6760Sstevel@tonic-gate #endif
6770Sstevel@tonic-gate extern int optopt;
6780Sstevel@tonic-gate extern char *optarg;
6790Sstevel@tonic-gate char *hp;
6800Sstevel@tonic-gate struct aclmember *entry;
6810Sstevel@tonic-gate #ifdef VIRTUAL
6820Sstevel@tonic-gate #if defined(UNIXWARE) || defined(AIX)
6830Sstevel@tonic-gate size_t virtual_len;
6840Sstevel@tonic-gate #else
6850Sstevel@tonic-gate int virtual_len;
6860Sstevel@tonic-gate #endif
6870Sstevel@tonic-gate struct SOCKSTORAGE virtual_addr;
6880Sstevel@tonic-gate #endif
6890Sstevel@tonic-gate struct servent *serv;
6900Sstevel@tonic-gate
6910Sstevel@tonic-gate #ifdef AUX
6920Sstevel@tonic-gate setcompat(COMPAT_POSIX | COMPAT_BSDSETUGID);
6930Sstevel@tonic-gate #endif
6940Sstevel@tonic-gate
6950Sstevel@tonic-gate closelog();
6960Sstevel@tonic-gate #ifdef FACILITY
6970Sstevel@tonic-gate openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
6980Sstevel@tonic-gate #else
6990Sstevel@tonic-gate openlog("ftpd", LOG_PID);
7000Sstevel@tonic-gate #endif
7010Sstevel@tonic-gate
7020Sstevel@tonic-gate #ifdef SecureWare
7030Sstevel@tonic-gate setluid(1); /* make sure there is a valid luid */
7040Sstevel@tonic-gate set_auth_parameters(argc, argv);
7050Sstevel@tonic-gate setreuid(0, 0);
7060Sstevel@tonic-gate #endif
7070Sstevel@tonic-gate #if defined(M_UNIX) && !defined(_M_UNIX)
7080Sstevel@tonic-gate res_init(); /* bug in old (1.1.1) resolver */
7090Sstevel@tonic-gate _res.retrans = 20; /* because of fake syslog in 3.2.2 */
7100Sstevel@tonic-gate setlogmask(LOG_UPTO(LOG_INFO));
7110Sstevel@tonic-gate #endif
7120Sstevel@tonic-gate
7130Sstevel@tonic-gate while ((c = getopt(argc, argv, FTPD_OPTS DAEMON_OPTS GSS_OPTS)) != -1) {
7140Sstevel@tonic-gate switch (c) {
7150Sstevel@tonic-gate
7160Sstevel@tonic-gate case '4':
7170Sstevel@tonic-gate #ifdef INET6
7180Sstevel@tonic-gate listen_v4 = 1;
7190Sstevel@tonic-gate #endif
7200Sstevel@tonic-gate break;
7210Sstevel@tonic-gate
7220Sstevel@tonic-gate case 'a':
7230Sstevel@tonic-gate use_accessfile = 1;
7240Sstevel@tonic-gate break;
7250Sstevel@tonic-gate
7260Sstevel@tonic-gate case 'A':
7270Sstevel@tonic-gate use_accessfile = 0;
7280Sstevel@tonic-gate break;
7290Sstevel@tonic-gate
7300Sstevel@tonic-gate case 'v':
7310Sstevel@tonic-gate debug = 1;
7320Sstevel@tonic-gate break;
7330Sstevel@tonic-gate
7340Sstevel@tonic-gate case 'd':
7350Sstevel@tonic-gate debug = 1;
7360Sstevel@tonic-gate break;
7370Sstevel@tonic-gate
7380Sstevel@tonic-gate #if defined(USE_GSS)
7390Sstevel@tonic-gate case 'C':
7400Sstevel@tonic-gate gss_info.want_creds = 1;
7410Sstevel@tonic-gate break;
7420Sstevel@tonic-gate
7430Sstevel@tonic-gate case 'K':
7440Sstevel@tonic-gate gss_info.must_gss_auth = 1;
7450Sstevel@tonic-gate break;
7460Sstevel@tonic-gate #endif /* USE_GSS */
7470Sstevel@tonic-gate
7480Sstevel@tonic-gate case 'l':
7490Sstevel@tonic-gate logging = 1;
7500Sstevel@tonic-gate break;
7510Sstevel@tonic-gate
7520Sstevel@tonic-gate case 'L':
7530Sstevel@tonic-gate log_commands = 3;
7540Sstevel@tonic-gate break;
7550Sstevel@tonic-gate
7560Sstevel@tonic-gate case 'i':
7570Sstevel@tonic-gate log_incoming_xfers = 3;
7580Sstevel@tonic-gate break;
7590Sstevel@tonic-gate
7600Sstevel@tonic-gate case 'I':
7610Sstevel@tonic-gate disable_rfc931 = 1;
7620Sstevel@tonic-gate break;
7630Sstevel@tonic-gate
7640Sstevel@tonic-gate case 'o':
7650Sstevel@tonic-gate log_outbound_xfers = 3;
7660Sstevel@tonic-gate break;
7670Sstevel@tonic-gate
7680Sstevel@tonic-gate case 'q':
7690Sstevel@tonic-gate Bypass_PID_Files = 0;
7700Sstevel@tonic-gate break;
7710Sstevel@tonic-gate
7720Sstevel@tonic-gate case 'Q':
7730Sstevel@tonic-gate Bypass_PID_Files = 1;
7740Sstevel@tonic-gate break;
7750Sstevel@tonic-gate
7760Sstevel@tonic-gate case 'r':
7770Sstevel@tonic-gate if ((optarg != NULL) && (optarg[0] != '\0')) {
7780Sstevel@tonic-gate RootDirectory = malloc(strlen(optarg) + 1);
7790Sstevel@tonic-gate if (RootDirectory != NULL)
7800Sstevel@tonic-gate strcpy(RootDirectory, optarg);
7810Sstevel@tonic-gate }
7820Sstevel@tonic-gate break;
7830Sstevel@tonic-gate
7840Sstevel@tonic-gate case 'P':
7850Sstevel@tonic-gate data_port = htons(atoi(optarg));
7860Sstevel@tonic-gate break;
7870Sstevel@tonic-gate
7880Sstevel@tonic-gate #ifdef DAEMON
7890Sstevel@tonic-gate case 'p':
7900Sstevel@tonic-gate daemon_port = atoi(optarg);
7910Sstevel@tonic-gate break;
7920Sstevel@tonic-gate
7930Sstevel@tonic-gate case 's':
7940Sstevel@tonic-gate be_daemon = 1;
7950Sstevel@tonic-gate break;
7960Sstevel@tonic-gate
7970Sstevel@tonic-gate case 'S':
7980Sstevel@tonic-gate be_daemon = 2;
7990Sstevel@tonic-gate break;
8000Sstevel@tonic-gate #endif /* DAEMON */
8010Sstevel@tonic-gate
8020Sstevel@tonic-gate case 't':
8030Sstevel@tonic-gate timeout_idle = atoi(optarg);
8040Sstevel@tonic-gate if (timeout_maxidle < timeout_idle)
8050Sstevel@tonic-gate timeout_maxidle = timeout_idle;
8060Sstevel@tonic-gate break;
8070Sstevel@tonic-gate
8080Sstevel@tonic-gate case 'T':
8090Sstevel@tonic-gate timeout_maxidle = atoi(optarg);
8100Sstevel@tonic-gate if (timeout_idle > timeout_maxidle)
8110Sstevel@tonic-gate timeout_idle = timeout_maxidle;
8120Sstevel@tonic-gate break;
8130Sstevel@tonic-gate
8140Sstevel@tonic-gate case 'u':
8150Sstevel@tonic-gate {
8160Sstevel@tonic-gate unsigned int val = 0;
8170Sstevel@tonic-gate
8180Sstevel@tonic-gate while (*optarg && *optarg >= '0' && *optarg <= '7')
8190Sstevel@tonic-gate val = val * 8 + *optarg++ - '0';
8200Sstevel@tonic-gate if (*optarg || val > 0777)
8210Sstevel@tonic-gate syslog(LOG_ERR, "bad value for -u");
8220Sstevel@tonic-gate else
8230Sstevel@tonic-gate defumask = val;
8240Sstevel@tonic-gate break;
8250Sstevel@tonic-gate }
8260Sstevel@tonic-gate
8270Sstevel@tonic-gate case 'V':
8280Sstevel@tonic-gate print_copyright();
8290Sstevel@tonic-gate exit(0);
8300Sstevel@tonic-gate /* NOTREACHED */
8310Sstevel@tonic-gate case 'w':
8320Sstevel@tonic-gate wtmp_logging = 1;
8330Sstevel@tonic-gate break;
8340Sstevel@tonic-gate
8350Sstevel@tonic-gate case 'W':
8360Sstevel@tonic-gate wtmp_logging = 0;
8370Sstevel@tonic-gate break;
8380Sstevel@tonic-gate
8390Sstevel@tonic-gate case 'x':
8400Sstevel@tonic-gate syslogmsg = 2;
8410Sstevel@tonic-gate break;
8420Sstevel@tonic-gate
8430Sstevel@tonic-gate case 'X':
8440Sstevel@tonic-gate syslogmsg = 1;
8450Sstevel@tonic-gate break;
8460Sstevel@tonic-gate
8470Sstevel@tonic-gate case ':':
8480Sstevel@tonic-gate syslog(LOG_ERR, "option -%c requires an argument", optopt);
8490Sstevel@tonic-gate break;
8500Sstevel@tonic-gate
8510Sstevel@tonic-gate default:
8520Sstevel@tonic-gate syslog(LOG_ERR, "unknown option -%c ignored", optopt);
8530Sstevel@tonic-gate break;
8540Sstevel@tonic-gate }
8550Sstevel@tonic-gate }
8560Sstevel@tonic-gate initsetproctitle(argc, argv, envp);
8570Sstevel@tonic-gate (void) freopen(_PATH_DEVNULL, "w", stderr);
8580Sstevel@tonic-gate
8590Sstevel@tonic-gate /* Checking for random signals ... */
8600Sstevel@tonic-gate #ifdef NEED_SIGFIX
8610Sstevel@tonic-gate sigemptyset(&block_sigmask);
8620Sstevel@tonic-gate #endif
8630Sstevel@tonic-gate #ifndef SIG_DEBUG
8640Sstevel@tonic-gate #ifdef SIGHUP
8650Sstevel@tonic-gate (void) signal(SIGHUP, randomsig);
8660Sstevel@tonic-gate #ifdef NEED_SIGFIX
8670Sstevel@tonic-gate sigaddset(&block_sigmask, SIGHUP);
8680Sstevel@tonic-gate #endif
8690Sstevel@tonic-gate #endif
8700Sstevel@tonic-gate #ifdef SIGINT
8710Sstevel@tonic-gate (void) signal(SIGINT, randomsig);
8720Sstevel@tonic-gate #ifdef NEED_SIGFIX
8730Sstevel@tonic-gate sigaddset(&block_sigmask, SIGINT);
8740Sstevel@tonic-gate #endif
8750Sstevel@tonic-gate #endif
8760Sstevel@tonic-gate #ifdef SIGQUIT
8770Sstevel@tonic-gate (void) signal(SIGQUIT, randomsig);
8780Sstevel@tonic-gate #ifdef NEED_SIGFIX
8790Sstevel@tonic-gate sigaddset(&block_sigmask, SIGQUIT);
8800Sstevel@tonic-gate #endif
8810Sstevel@tonic-gate #endif
8820Sstevel@tonic-gate #ifdef SIGILL
8830Sstevel@tonic-gate (void) signal(SIGILL, randomsig);
8840Sstevel@tonic-gate #ifdef NEED_SIGFIX
8850Sstevel@tonic-gate sigaddset(&block_sigmask, SIGILL);
8860Sstevel@tonic-gate #endif
8870Sstevel@tonic-gate #endif
8880Sstevel@tonic-gate #ifdef SIGTRAP
8890Sstevel@tonic-gate (void) signal(SIGTRAP, randomsig);
8900Sstevel@tonic-gate #ifdef NEED_SIGFIX
8910Sstevel@tonic-gate sigaddset(&block_sigmask, SIGTRAP);
8920Sstevel@tonic-gate #endif
8930Sstevel@tonic-gate #endif
8940Sstevel@tonic-gate #ifdef SIGIOT
8950Sstevel@tonic-gate (void) signal(SIGIOT, randomsig);
8960Sstevel@tonic-gate #ifdef NEED_SIGFIX
8970Sstevel@tonic-gate sigaddset(&block_sigmask, SIGIOT);
8980Sstevel@tonic-gate #endif
8990Sstevel@tonic-gate #endif
9000Sstevel@tonic-gate #ifdef SIGEMT
9010Sstevel@tonic-gate (void) signal(SIGEMT, randomsig);
9020Sstevel@tonic-gate #ifdef NEED_SIGFIX
9030Sstevel@tonic-gate sigaddset(&block_sigmask, SIGEMT);
9040Sstevel@tonic-gate #endif
9050Sstevel@tonic-gate #endif
9060Sstevel@tonic-gate #ifdef SIGFPE
9070Sstevel@tonic-gate (void) signal(SIGFPE, randomsig);
9080Sstevel@tonic-gate #ifdef NEED_SIGFIX
9090Sstevel@tonic-gate sigaddset(&block_sigmask, SIGFPE);
9100Sstevel@tonic-gate #endif
9110Sstevel@tonic-gate #endif
9120Sstevel@tonic-gate #ifdef SIGKILL
9130Sstevel@tonic-gate (void) signal(SIGKILL, randomsig);
9140Sstevel@tonic-gate #ifdef NEED_SIGFIX
9150Sstevel@tonic-gate sigaddset(&block_sigmask, SIGKILL);
9160Sstevel@tonic-gate #endif
9170Sstevel@tonic-gate #endif
9180Sstevel@tonic-gate #ifdef SIGBUS
9190Sstevel@tonic-gate (void) signal(SIGBUS, randomsig);
9200Sstevel@tonic-gate #ifdef NEED_SIGFIX
9210Sstevel@tonic-gate sigaddset(&block_sigmask, SIGBUS);
9220Sstevel@tonic-gate #endif
9230Sstevel@tonic-gate #endif
9240Sstevel@tonic-gate #ifdef SIGSEGV
9250Sstevel@tonic-gate (void) signal(SIGSEGV, randomsig);
9260Sstevel@tonic-gate #ifdef NEED_SIGFIX
9270Sstevel@tonic-gate sigaddset(&block_sigmask, SIGSEGV);
9280Sstevel@tonic-gate #endif
9290Sstevel@tonic-gate #endif
9300Sstevel@tonic-gate #ifdef SIGSYS
9310Sstevel@tonic-gate (void) signal(SIGSYS, randomsig);
9320Sstevel@tonic-gate #ifdef NEED_SIGFIX
9330Sstevel@tonic-gate sigaddset(&block_sigmask, SIGSYS);
9340Sstevel@tonic-gate #endif
9350Sstevel@tonic-gate #endif
9360Sstevel@tonic-gate #ifdef SIGALRM
9370Sstevel@tonic-gate (void) signal(SIGALRM, randomsig);
9380Sstevel@tonic-gate #ifdef NEED_SIGFIX
9390Sstevel@tonic-gate sigaddset(&block_sigmask, SIGALRM);
9400Sstevel@tonic-gate #endif
9410Sstevel@tonic-gate #endif
9420Sstevel@tonic-gate #ifdef SIGSTOP
9430Sstevel@tonic-gate (void) signal(SIGSTOP, randomsig);
9440Sstevel@tonic-gate #ifdef NEED_SIGFIX
9450Sstevel@tonic-gate sigaddset(&block_sigmask, SIGSTOP);
9460Sstevel@tonic-gate #endif
9470Sstevel@tonic-gate #endif
9480Sstevel@tonic-gate #ifdef SIGTSTP
9490Sstevel@tonic-gate (void) signal(SIGTSTP, randomsig);
9500Sstevel@tonic-gate #ifdef NEED_SIGFIX
9510Sstevel@tonic-gate sigaddset(&block_sigmask, SIGTSTP);
9520Sstevel@tonic-gate #endif
9530Sstevel@tonic-gate #endif
9540Sstevel@tonic-gate #ifdef SIGTTIN
9550Sstevel@tonic-gate (void) signal(SIGTTIN, randomsig);
9560Sstevel@tonic-gate #ifdef NEED_SIGFIX
9570Sstevel@tonic-gate sigaddset(&block_sigmask, SIGTTIN);
9580Sstevel@tonic-gate #endif
9590Sstevel@tonic-gate #endif
9600Sstevel@tonic-gate #ifdef SIGTTOU
9610Sstevel@tonic-gate (void) signal(SIGTTOU, randomsig);
9620Sstevel@tonic-gate #ifdef NEED_SIGFIX
9630Sstevel@tonic-gate sigaddset(&block_sigmask, SIGTTOU);
9640Sstevel@tonic-gate #endif
9650Sstevel@tonic-gate #endif
9660Sstevel@tonic-gate #ifdef SIGIO
9670Sstevel@tonic-gate (void) signal(SIGIO, randomsig);
9680Sstevel@tonic-gate #ifdef NEED_SIGFIX
9690Sstevel@tonic-gate sigaddset(&block_sigmask, SIGIO);
9700Sstevel@tonic-gate #endif
9710Sstevel@tonic-gate #endif
9720Sstevel@tonic-gate #ifdef SIGXCPU
9730Sstevel@tonic-gate (void) signal(SIGXCPU, randomsig);
9740Sstevel@tonic-gate #ifdef NEED_SIGFIX
9750Sstevel@tonic-gate sigaddset(&block_sigmask, SIGXCPU);
9760Sstevel@tonic-gate #endif
9770Sstevel@tonic-gate #endif
9780Sstevel@tonic-gate #ifdef SIGXFSZ
9790Sstevel@tonic-gate (void) signal(SIGXFSZ, randomsig);
9800Sstevel@tonic-gate #ifdef NEED_SIGFIX
9810Sstevel@tonic-gate sigaddset(&block_sigmask, SIGXFSZ);
9820Sstevel@tonic-gate #endif
9830Sstevel@tonic-gate #endif
9840Sstevel@tonic-gate #ifdef SIGWINCH
9850Sstevel@tonic-gate (void) signal(SIGWINCH, randomsig);
9860Sstevel@tonic-gate #ifdef NEED_SIGFIX
9870Sstevel@tonic-gate sigaddset(&block_sigmask, SIGWINCH);
9880Sstevel@tonic-gate #endif
9890Sstevel@tonic-gate #endif
9900Sstevel@tonic-gate #ifdef SIGVTALRM
9910Sstevel@tonic-gate (void) signal(SIGVTALRM, randomsig);
9920Sstevel@tonic-gate #ifdef NEED_SIGFIX
9930Sstevel@tonic-gate sigaddset(&block_sigmask, SIGVTALRM);
9940Sstevel@tonic-gate #endif
9950Sstevel@tonic-gate #endif
9960Sstevel@tonic-gate #ifdef SIGPROF
9970Sstevel@tonic-gate (void) signal(SIGPROF, randomsig);
9980Sstevel@tonic-gate #ifdef NEED_SIGFIX
9990Sstevel@tonic-gate sigaddset(&block_sigmask, SIGPROF);
10000Sstevel@tonic-gate #endif
10010Sstevel@tonic-gate #endif
10020Sstevel@tonic-gate #ifdef SIGUSR1
10030Sstevel@tonic-gate (void) signal(SIGUSR1, randomsig);
10040Sstevel@tonic-gate #ifdef NEED_SIGFIX
10050Sstevel@tonic-gate sigaddset(&block_sigmask, SIGUSR1);
10060Sstevel@tonic-gate #endif
10070Sstevel@tonic-gate #endif
10080Sstevel@tonic-gate #ifdef SIGUSR2
10090Sstevel@tonic-gate (void) signal(SIGUSR2, randomsig);
10100Sstevel@tonic-gate #ifdef NEED_SIGFIX
10110Sstevel@tonic-gate sigaddset(&block_sigmask, SIGUSR2);
10120Sstevel@tonic-gate #endif
10130Sstevel@tonic-gate #endif
10140Sstevel@tonic-gate
10150Sstevel@tonic-gate #ifdef SIGPIPE
10160Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn);
10170Sstevel@tonic-gate #ifdef NEED_SIGFIX
10180Sstevel@tonic-gate sigaddset(&block_sigmask, SIGPIPE);
10190Sstevel@tonic-gate #endif
10200Sstevel@tonic-gate #endif
10210Sstevel@tonic-gate #ifdef SIGCHLD
10220Sstevel@tonic-gate (void) signal(SIGCHLD, SIG_IGN);
10230Sstevel@tonic-gate #ifdef NEED_SIGFIX
10240Sstevel@tonic-gate sigaddset(&block_sigmask, SIGCHLD);
10250Sstevel@tonic-gate #endif
10260Sstevel@tonic-gate #endif
10270Sstevel@tonic-gate
10280Sstevel@tonic-gate #ifdef SIGURG
10290Sstevel@tonic-gate if (signal(SIGURG, myoob) == SIG_ERR)
10300Sstevel@tonic-gate syslog(LOG_ERR, "signal: %m");
10310Sstevel@tonic-gate #ifdef NEED_SIGFIX
10320Sstevel@tonic-gate sigaddset(&block_sigmask, SIGURG);
10330Sstevel@tonic-gate #endif
10340Sstevel@tonic-gate #endif
10350Sstevel@tonic-gate #endif /* SIG_DEBUG */
10360Sstevel@tonic-gate
10370Sstevel@tonic-gate #ifdef VIRTUAL
10380Sstevel@tonic-gate virtual_root[0] = '\0';
10390Sstevel@tonic-gate virtual_banner[0] = '\0';
10400Sstevel@tonic-gate #endif
10410Sstevel@tonic-gate
10420Sstevel@tonic-gate setup_paths();
10430Sstevel@tonic-gate
10440Sstevel@tonic-gate #ifdef OTHER_PASSWD
10450Sstevel@tonic-gate strcpy(_path_passwd, "/etc/passwd");
10460Sstevel@tonic-gate #ifdef SHADOW_PASSWORD
10470Sstevel@tonic-gate strcpy(_path_shadow, "/etc/shadow");
10480Sstevel@tonic-gate #endif
10490Sstevel@tonic-gate #endif
10500Sstevel@tonic-gate
10510Sstevel@tonic-gate access_init();
10520Sstevel@tonic-gate
10530Sstevel@tonic-gate #ifdef DAEMON
10540Sstevel@tonic-gate if (be_daemon != 0)
10550Sstevel@tonic-gate do_daemon();
10560Sstevel@tonic-gate else {
10570Sstevel@tonic-gate #endif
10580Sstevel@tonic-gate addrlen = sizeof(his_addr);
10590Sstevel@tonic-gate if (getpeername(0, (struct sockaddr *) &his_addr, &addrlen) < 0) {
10600Sstevel@tonic-gate syslog(LOG_ERR, "getpeername: %m");
10610Sstevel@tonic-gate #ifndef DEBUG
10620Sstevel@tonic-gate exit(1);
10630Sstevel@tonic-gate #endif
10640Sstevel@tonic-gate }
10650Sstevel@tonic-gate #ifdef DAEMON
10660Sstevel@tonic-gate }
10670Sstevel@tonic-gate #endif
10680Sstevel@tonic-gate addrlen = sizeof(ctrl_addr);
10690Sstevel@tonic-gate if (getsockname(0, (struct sockaddr *) &ctrl_addr, &addrlen) < 0) {
10700Sstevel@tonic-gate syslog(LOG_ERR, "getsockname: %m");
10710Sstevel@tonic-gate #ifndef DEBUG
10720Sstevel@tonic-gate exit(1);
10730Sstevel@tonic-gate #endif
10740Sstevel@tonic-gate }
10750Sstevel@tonic-gate /* Sanity check */
10760Sstevel@tonic-gate if ((SOCK_FAMILY(ctrl_addr) != AF_INET)
10770Sstevel@tonic-gate #ifdef INET6
10780Sstevel@tonic-gate && (SOCK_FAMILY(ctrl_addr) != AF_INET6)
10790Sstevel@tonic-gate #endif
10800Sstevel@tonic-gate ) {
10810Sstevel@tonic-gate syslog(LOG_ERR, "control connection address family (%d) not supported.",
10820Sstevel@tonic-gate SOCK_FAMILY(ctrl_addr));
10830Sstevel@tonic-gate #ifndef DEBUG
10840Sstevel@tonic-gate exit(1);
10850Sstevel@tonic-gate #endif
10860Sstevel@tonic-gate }
10870Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
10880Sstevel@tonic-gate /* Set audit characteristics */
10890Sstevel@tonic-gate if (audit_settid(0)) {
10900Sstevel@tonic-gate syslog(LOG_ERR, "audit failure");
10910Sstevel@tonic-gate exit(1);
10920Sstevel@tonic-gate }
10930Sstevel@tonic-gate #endif
10940Sstevel@tonic-gate #ifdef INET6
10950Sstevel@tonic-gate /* IP_TOS is an IPv4 socket option */
10960Sstevel@tonic-gate if (SOCK_FAMILY(ctrl_addr) == AF_INET)
10970Sstevel@tonic-gate #endif
10980Sstevel@tonic-gate if ((cos = IPClassOfService("control")) >= 0) {
10990Sstevel@tonic-gate if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &cos, sizeof(int)) < 0)
11000Sstevel@tonic-gate syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
11010Sstevel@tonic-gate }
11020Sstevel@tonic-gate
11030Sstevel@tonic-gate #ifdef TCP_NODELAY
11040Sstevel@tonic-gate /*
11050Sstevel@tonic-gate * Disable Nagle on the control channel so that we don't have to wait
11060Sstevel@tonic-gate * for peer's ACK before issuing our next reply.
11070Sstevel@tonic-gate */
11080Sstevel@tonic-gate if (setsockopt(0, IPPROTO_TCP, TCP_NODELAY, &on, sizeof (on)) < 0)
11090Sstevel@tonic-gate syslog(LOG_WARNING, "control setsockopt TCP_NODELAY: %m");
11100Sstevel@tonic-gate #endif
11110Sstevel@tonic-gate
11120Sstevel@tonic-gate if (keepalive)
11130Sstevel@tonic-gate if (setsockopt(0, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on)) < 0)
11140Sstevel@tonic-gate syslog(LOG_ERR, "setsockopt SO_KEEPALIVE %m");
11150Sstevel@tonic-gate
11160Sstevel@tonic-gate /* Try to handle urgent data inline */
11170Sstevel@tonic-gate #ifdef SO_OOBINLINE
11180Sstevel@tonic-gate if (setsockopt(0, SOL_SOCKET, SO_OOBINLINE, (char *) &on, sizeof(int)) < 0)
11190Sstevel@tonic-gate syslog(LOG_ERR, "setsockopt (SO_OOBINLINE): %m");
11200Sstevel@tonic-gate #endif
11210Sstevel@tonic-gate
11220Sstevel@tonic-gate #ifdef F_SETOWN
11230Sstevel@tonic-gate if (fcntl(fileno(stdin), F_SETOWN, getpid()) == -1)
11240Sstevel@tonic-gate syslog(LOG_ERR, "fcntl F_SETOWN: %m");
11250Sstevel@tonic-gate #elif defined(SIOCSPGRP)
11260Sstevel@tonic-gate {
11270Sstevel@tonic-gate int pid;
11280Sstevel@tonic-gate pid = getpid();
11290Sstevel@tonic-gate if (ioctl(fileno(stdin), SIOCSPGRP, &pid) == -1)
11300Sstevel@tonic-gate syslog(LOG_ERR, "ioctl SIOCSPGRP: %m");
11310Sstevel@tonic-gate }
11320Sstevel@tonic-gate #endif
11330Sstevel@tonic-gate
11340Sstevel@tonic-gate #ifdef INET6
11350Sstevel@tonic-gate if ((SOCK_FAMILY(ctrl_addr) == AF_INET6) &&
11360Sstevel@tonic-gate IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&(ctrl_addr))->sin6_addr))
11370Sstevel@tonic-gate ctrl_v4mapped = 1;
11380Sstevel@tonic-gate #endif
11390Sstevel@tonic-gate
11400Sstevel@tonic-gate if (data_port == 0) {
11410Sstevel@tonic-gate serv = getservbyname("ftp-data", "tcp");
11420Sstevel@tonic-gate if (serv != NULL)
11430Sstevel@tonic-gate data_port = serv->s_port;
11440Sstevel@tonic-gate else
11450Sstevel@tonic-gate data_port = htons(ntohs(SOCK_PORT(ctrl_addr)) - 1);
11460Sstevel@tonic-gate }
11470Sstevel@tonic-gate
11480Sstevel@tonic-gate if (RootDirectory != NULL) {
11490Sstevel@tonic-gate if ((chroot(RootDirectory) < 0)
11500Sstevel@tonic-gate || (chdir("/") < 0)) {
11510Sstevel@tonic-gate syslog(LOG_ERR, "Cannot chroot to initial directory, aborting.");
11520Sstevel@tonic-gate exit(1);
11530Sstevel@tonic-gate }
11540Sstevel@tonic-gate }
11550Sstevel@tonic-gate
11560Sstevel@tonic-gate load_timeouts();
11570Sstevel@tonic-gate
11580Sstevel@tonic-gate /* set resolver options */
11590Sstevel@tonic-gate set_res_options();
11600Sstevel@tonic-gate
11610Sstevel@tonic-gate dolog(&his_addr);
11620Sstevel@tonic-gate /* Set up default state */
11630Sstevel@tonic-gate data = -1;
11640Sstevel@tonic-gate type = TYPE_A;
11650Sstevel@tonic-gate form = FORM_N;
11660Sstevel@tonic-gate stru = STRU_F;
11670Sstevel@tonic-gate mode = MODE_S;
11680Sstevel@tonic-gate tmpline[0] = '\0';
11690Sstevel@tonic-gate yyerrorcalled = 0;
11700Sstevel@tonic-gate
11710Sstevel@tonic-gate entry = (struct aclmember *) NULL;
11720Sstevel@tonic-gate if ((getaclentry("hostname", &entry)) && ARG0) {
11730Sstevel@tonic-gate (void) strncpy(hostname, ARG0, sizeof(hostname));
11740Sstevel@tonic-gate hostname[sizeof(hostname) - 1] = '\0';
11750Sstevel@tonic-gate }
11760Sstevel@tonic-gate else {
11770Sstevel@tonic-gate #ifdef HAVE_SYSINFO
11780Sstevel@tonic-gate sysinfo(SI_HOSTNAME, hostname, sizeof(hostname));
11790Sstevel@tonic-gate #else
11800Sstevel@tonic-gate (void) gethostname(hostname, sizeof(hostname));
11810Sstevel@tonic-gate #endif
11820Sstevel@tonic-gate /* set the FQDN here */
11830Sstevel@tonic-gate hp = wu_gethostbyname(hostname);
11840Sstevel@tonic-gate if (hp) {
11850Sstevel@tonic-gate (void) strncpy(hostname, hp, sizeof(hostname));
11860Sstevel@tonic-gate hostname[sizeof(hostname) - 1] = '\0';
11870Sstevel@tonic-gate }
11880Sstevel@tonic-gate }
11890Sstevel@tonic-gate route_vectored = routevector();
11900Sstevel@tonic-gate conv_init();
11910Sstevel@tonic-gate
11920Sstevel@tonic-gate #ifdef MAIL_ADMIN
11930Sstevel@tonic-gate incmails = 0;
11940Sstevel@tonic-gate mailfrom = NULL;
11950Sstevel@tonic-gate #endif /* MAIL_ADMIN */
11960Sstevel@tonic-gate #ifdef VIRTUAL
11970Sstevel@tonic-gate /*
11980Sstevel@tonic-gate ** If virtual_mode is set at this point then an alternate ftpaccess
11990Sstevel@tonic-gate ** is in use. Otherwise we need to check the Master ftpaccess file
12000Sstevel@tonic-gate ** to see if the site is only using the "virtual" directives to
12010Sstevel@tonic-gate ** specify virtual site directives.
12020Sstevel@tonic-gate **
12030Sstevel@tonic-gate ** In this manner an admin can put a virtual site in the ftpservers
12040Sstevel@tonic-gate ** file if they need expanded configuration support or can use the
12050Sstevel@tonic-gate ** minimal root/banner/logfile if they do not need any more than that.
12060Sstevel@tonic-gate */
12070Sstevel@tonic-gate
12080Sstevel@tonic-gate if (virtual_mode) {
12090Sstevel@tonic-gate /* Get the root of the virtual server directory */
12100Sstevel@tonic-gate entry = (struct aclmember *) NULL;
12110Sstevel@tonic-gate if (getaclentry("root", &entry)) {
12120Sstevel@tonic-gate if (ARG0)
12130Sstevel@tonic-gate strcpy(virtual_root, ARG0);
12140Sstevel@tonic-gate }
12150Sstevel@tonic-gate
12160Sstevel@tonic-gate /* Get the logfile to use */
12170Sstevel@tonic-gate entry = (struct aclmember *) NULL;
12180Sstevel@tonic-gate if (getaclentry("logfile", &entry)) {
12190Sstevel@tonic-gate if (ARG0)
12200Sstevel@tonic-gate strcpy(logfile, ARG0);
12210Sstevel@tonic-gate }
12220Sstevel@tonic-gate }
12230Sstevel@tonic-gate else {
12240Sstevel@tonic-gate virtual_hostname[0] = '\0';
12250Sstevel@tonic-gate virtual_address[0] = '\0';
12260Sstevel@tonic-gate virtual_len = sizeof(virtual_addr);
12270Sstevel@tonic-gate if (getsockname(0, (struct sockaddr *) &virtual_addr, &virtual_len) == 0) {
12280Sstevel@tonic-gate strcpy(virtual_address, inet_stop(&virtual_addr));
12290Sstevel@tonic-gate wu_gethostbyaddr(&virtual_addr, virtual_hostname, sizeof(virtual_hostname));
12300Sstevel@tonic-gate entry = (struct aclmember *) NULL;
12310Sstevel@tonic-gate while (getaclentry("virtual", &entry)) {
12320Sstevel@tonic-gate if (!ARG0 || !ARG1 || !ARG2)
12330Sstevel@tonic-gate continue;
12340Sstevel@tonic-gate if (hostmatch(ARG0, virtual_address, virtual_hostname)) {
12350Sstevel@tonic-gate if (!strcasecmp(ARG1, "root")) {
12360Sstevel@tonic-gate if (debug)
12370Sstevel@tonic-gate syslog(LOG_DEBUG, "VirtualFTP Connect to: %s [%s]",
12380Sstevel@tonic-gate virtual_hostname, virtual_address);
12390Sstevel@tonic-gate virtual_mode = 1;
12400Sstevel@tonic-gate strncpy(virtual_root, ARG2, sizeof(virtual_root));
12410Sstevel@tonic-gate virtual_root[sizeof(virtual_root) - 1] = '\0';
12420Sstevel@tonic-gate /* reset hostname to this virtual name */
12430Sstevel@tonic-gate (void) strcpy(hostname, virtual_hostname);
12440Sstevel@tonic-gate virtual_email[0] = '\0';
12450Sstevel@tonic-gate }
12460Sstevel@tonic-gate if (!strcasecmp(ARG1, "banner")) {
12470Sstevel@tonic-gate strncpy(virtual_banner, ARG2, sizeof(virtual_banner));
12480Sstevel@tonic-gate virtual_banner[sizeof(virtual_banner) - 1] = '\0';
12490Sstevel@tonic-gate }
12500Sstevel@tonic-gate if (!strcasecmp(ARG1, "logfile")) {
12510Sstevel@tonic-gate strncpy(logfile, ARG2, sizeof(logfile));
12520Sstevel@tonic-gate logfile[sizeof(logfile) - 1] = '\0';
12530Sstevel@tonic-gate }
12540Sstevel@tonic-gate if (!strcasecmp(ARG1, "hostname")) {
12550Sstevel@tonic-gate strncpy(hostname, ARG2, sizeof(hostname));
12560Sstevel@tonic-gate hostname[sizeof(hostname) - 1] = '\0';
12570Sstevel@tonic-gate }
12580Sstevel@tonic-gate if (!strcasecmp(ARG1, "email")) {
12590Sstevel@tonic-gate strncpy(virtual_email, ARG2, sizeof(virtual_email));
12600Sstevel@tonic-gate virtual_email[sizeof(virtual_email) - 1] = '\0';
12610Sstevel@tonic-gate }
12620Sstevel@tonic-gate #ifdef OTHER_PASSWD
12630Sstevel@tonic-gate if (!strcasecmp(ARG1, "passwd")) {
12640Sstevel@tonic-gate strncpy(_path_passwd, ARG2, sizeof(_path_passwd));
12650Sstevel@tonic-gate _path_passwd[sizeof(_path_passwd) - 1] = '\0';
12660Sstevel@tonic-gate #ifdef USE_PAM
12670Sstevel@tonic-gate use_pam = 0;
12680Sstevel@tonic-gate #endif
12690Sstevel@tonic-gate }
12700Sstevel@tonic-gate #ifdef SHADOW_PASSWORD
12710Sstevel@tonic-gate if (!strcasecmp(ARG1, "shadow")) {
12720Sstevel@tonic-gate strncpy(_path_shadow, ARG2, sizeof(_path_shadow));
12730Sstevel@tonic-gate _path_shadow[sizeof(_path_shadow) - 1] = '\0';
12740Sstevel@tonic-gate #ifdef USE_PAM
12750Sstevel@tonic-gate use_pam = 0;
12760Sstevel@tonic-gate #endif
12770Sstevel@tonic-gate }
12780Sstevel@tonic-gate #endif
12790Sstevel@tonic-gate #endif
12800Sstevel@tonic-gate #ifdef MAIL_ADMIN
12810Sstevel@tonic-gate if (mailfrom == NULL)
12820Sstevel@tonic-gate if (!strcasecmp(ARG1, "mailfrom")) {
12830Sstevel@tonic-gate mailfrom = strdup(ARG2);
12840Sstevel@tonic-gate }
12850Sstevel@tonic-gate if (!strcasecmp(ARG1, "incmail")) {
12860Sstevel@tonic-gate if (incmails < INCMAILS)
12870Sstevel@tonic-gate incmail[incmails++] = strdup(ARG2);
12880Sstevel@tonic-gate }
12890Sstevel@tonic-gate #endif
12900Sstevel@tonic-gate }
12910Sstevel@tonic-gate }
12920Sstevel@tonic-gate if (!virtual_mode) {
12930Sstevel@tonic-gate entry = (struct aclmember *) NULL;
12940Sstevel@tonic-gate while (getaclentry("defaultserver", &entry)) {
12950Sstevel@tonic-gate if (!ARG0 || !ARG1)
12960Sstevel@tonic-gate continue;
12970Sstevel@tonic-gate #ifdef MAIL_ADMIN
12980Sstevel@tonic-gate if (mailfrom == NULL)
12990Sstevel@tonic-gate if (!strcasecmp(ARG0, "mailfrom")) {
13000Sstevel@tonic-gate mailfrom = strdup(ARG1);
13010Sstevel@tonic-gate }
13020Sstevel@tonic-gate if (!strcasecmp(ARG0, "incmail")) {
13030Sstevel@tonic-gate if (incmails < INCMAILS)
13040Sstevel@tonic-gate incmail[incmails++] = strdup(ARG1);
13050Sstevel@tonic-gate }
13060Sstevel@tonic-gate #endif
13070Sstevel@tonic-gate }
13080Sstevel@tonic-gate }
13090Sstevel@tonic-gate }
13100Sstevel@tonic-gate }
13110Sstevel@tonic-gate
13120Sstevel@tonic-gate #ifdef VIRTUAL_DEBUG
13130Sstevel@tonic-gate lreply(220, "_path_ftpaccess == %s", _path_ftpaccess);
13140Sstevel@tonic-gate lreply(220, "_path_ftpusers == %s", _path_ftpusers);
13150Sstevel@tonic-gate lreply(220, "_path_ftphosts == %s", _path_ftphosts);
13160Sstevel@tonic-gate lreply(220, "_path_private == %s", _path_private);
13170Sstevel@tonic-gate lreply(220, "_path_cvt == %s", _path_cvt);
13180Sstevel@tonic-gate if (virtual_mode) {
13190Sstevel@tonic-gate if (virtual_ftpaccess)
13200Sstevel@tonic-gate lreply(220, "VIRTUAL Mode: Using %s specific %s access file",
13210Sstevel@tonic-gate hostname, _path_ftpaccess);
13220Sstevel@tonic-gate else
13230Sstevel@tonic-gate lreply(220, "VIRTUAL Mode: Using Master access file %s",
13240Sstevel@tonic-gate _path_ftpaccess);
13250Sstevel@tonic-gate
13260Sstevel@tonic-gate lreply(220, "virtual_root == %s", virtual_root);
13270Sstevel@tonic-gate if (!virtual_ftpaccess)
13280Sstevel@tonic-gate lreply(220, "virtual_banner == %s", virtual_banner);
13290Sstevel@tonic-gate }
13300Sstevel@tonic-gate lreply(220, "logfile == %s", logfile);
13310Sstevel@tonic-gate #endif
13320Sstevel@tonic-gate #endif
13330Sstevel@tonic-gate
13340Sstevel@tonic-gate if (is_shutdown(1, 1) != 0) {
13350Sstevel@tonic-gate syslog(LOG_INFO, "connection refused (server shut down) from %s",
13360Sstevel@tonic-gate remoteident);
13370Sstevel@tonic-gate reply(500, "%s FTP server shut down -- please try again later.",
13380Sstevel@tonic-gate hostname);
13390Sstevel@tonic-gate exit(0);
13400Sstevel@tonic-gate }
13410Sstevel@tonic-gate
13420Sstevel@tonic-gate #ifdef OPIE
13430Sstevel@tonic-gate af_pwok = opieaccessfile(remotehost);
13440Sstevel@tonic-gate #endif
13450Sstevel@tonic-gate
13460Sstevel@tonic-gate /* check permitted access based on name and address lookup of remote host */
13470Sstevel@tonic-gate if (!check_rhost_reverse()) {
13480Sstevel@tonic-gate exit(0);
13490Sstevel@tonic-gate }
13500Sstevel@tonic-gate if (!check_rhost_matches()) {
13510Sstevel@tonic-gate exit(0);
13520Sstevel@tonic-gate }
13530Sstevel@tonic-gate
13540Sstevel@tonic-gate show_banner(220);
13550Sstevel@tonic-gate
13560Sstevel@tonic-gate #ifndef INTERNAL_LS
13570Sstevel@tonic-gate entry = (struct aclmember *) NULL;
13580Sstevel@tonic-gate if (getaclentry("lslong", &entry) && ARG0 && (int) strlen(ARG0) > 0) {
13590Sstevel@tonic-gate strcpy(ls_long, ARG0);
13600Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
13610Sstevel@tonic-gate strcat(ls_long, " ");
13620Sstevel@tonic-gate strcat(ls_long, ARG[which]);
13630Sstevel@tonic-gate }
13640Sstevel@tonic-gate }
13650Sstevel@tonic-gate else {
13660Sstevel@tonic-gate #if defined(SVR4) || defined(ISC)
13670Sstevel@tonic-gate #if defined(AIX) || defined(SOLARIS_2)
13680Sstevel@tonic-gate strcpy(ls_long, "/bin/ls -lA");
13690Sstevel@tonic-gate #else
13700Sstevel@tonic-gate strcpy(ls_long, "/bin/ls -la");
13710Sstevel@tonic-gate #endif
13720Sstevel@tonic-gate #else
13730Sstevel@tonic-gate strcpy(ls_long, "/bin/ls -lgA");
13740Sstevel@tonic-gate #endif
13750Sstevel@tonic-gate }
13760Sstevel@tonic-gate strcat(ls_long, " %s");
13770Sstevel@tonic-gate
13780Sstevel@tonic-gate entry = (struct aclmember *) NULL;
13790Sstevel@tonic-gate if (getaclentry("lsshort", &entry) && ARG0 && (int) strlen(ARG0) > 0) {
13800Sstevel@tonic-gate strcpy(ls_short, ARG0);
13810Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
13820Sstevel@tonic-gate strcat(ls_short, " ");
13830Sstevel@tonic-gate strcat(ls_short, ARG[which]);
13840Sstevel@tonic-gate }
13850Sstevel@tonic-gate }
13860Sstevel@tonic-gate else {
13870Sstevel@tonic-gate #if defined(SVR4) || defined(ISC)
13880Sstevel@tonic-gate #if defined(AIX) || defined(SOLARIS_2)
13890Sstevel@tonic-gate strcpy(ls_short, "/bin/ls -lA");
13900Sstevel@tonic-gate #else
13910Sstevel@tonic-gate strcpy(ls_short, "/bin/ls -la");
13920Sstevel@tonic-gate
13930Sstevel@tonic-gate #endif
13940Sstevel@tonic-gate #else
13950Sstevel@tonic-gate strcpy(ls_short, "/bin/ls -lgA");
13960Sstevel@tonic-gate #endif
13970Sstevel@tonic-gate }
13980Sstevel@tonic-gate strcat(ls_short, " %s");
13990Sstevel@tonic-gate
14000Sstevel@tonic-gate entry = (struct aclmember *) NULL;
14010Sstevel@tonic-gate if (getaclentry("lsplain", &entry) && ARG0 && (int) strlen(ARG0) > 0) {
14020Sstevel@tonic-gate strcpy(ls_plain, ARG0);
14030Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
14040Sstevel@tonic-gate strcat(ls_plain, " ");
14050Sstevel@tonic-gate strcat(ls_plain, ARG[which]);
14060Sstevel@tonic-gate }
14070Sstevel@tonic-gate }
14080Sstevel@tonic-gate else
14090Sstevel@tonic-gate strcpy(ls_plain, "/bin/ls");
14100Sstevel@tonic-gate strcat(ls_plain, " %s");
14110Sstevel@tonic-gate #endif /* ! INTERNAL_LS */
14120Sstevel@tonic-gate #ifdef MAIL_ADMIN
14130Sstevel@tonic-gate mailservers = 0;
14140Sstevel@tonic-gate entry = (struct aclmember *) NULL;
14150Sstevel@tonic-gate while (getaclentry("mailserver", &entry) && ARG0 && mailservers < MAILSERVERS)
14160Sstevel@tonic-gate mailserver[mailservers++] = strdup(ARG0);
14170Sstevel@tonic-gate if (mailservers == 0)
14180Sstevel@tonic-gate mailserver[mailservers++] = strdup("localhost");
14190Sstevel@tonic-gate if (incmails == 0) {
14200Sstevel@tonic-gate entry = (struct aclmember *) NULL;
14210Sstevel@tonic-gate while (getaclentry("incmail", &entry) && ARG0 && incmails < INCMAILS)
14220Sstevel@tonic-gate incmail[incmails++] = strdup(ARG0);
14230Sstevel@tonic-gate }
14240Sstevel@tonic-gate if (mailfrom == NULL) {
14250Sstevel@tonic-gate entry = (struct aclmember *) NULL;
14260Sstevel@tonic-gate if (getaclentry("mailfrom", &entry) && ARG0)
14270Sstevel@tonic-gate mailfrom = strdup(ARG0);
14280Sstevel@tonic-gate else
14290Sstevel@tonic-gate mailfrom = strdup("wu-ftpd");
14300Sstevel@tonic-gate }
14310Sstevel@tonic-gate #endif /* MAIL_ADMIN */
14320Sstevel@tonic-gate {
14330Sstevel@tonic-gate #define OUTPUT_LEN (BUFSIZ * 2)
14340Sstevel@tonic-gate int version_option = 0;
14350Sstevel@tonic-gate char output_text[OUTPUT_LEN + 1];
14360Sstevel@tonic-gate int which;
14370Sstevel@tonic-gate
14380Sstevel@tonic-gate entry = NULL;
14390Sstevel@tonic-gate if (getaclentry("greeting", &entry) && ARG0) {
14400Sstevel@tonic-gate if (!strcasecmp(ARG0, "full"))
14410Sstevel@tonic-gate version_option = 0;
14420Sstevel@tonic-gate else if (!strcasecmp(ARG0, "text") && ARG1)
14430Sstevel@tonic-gate version_option = 3;
14440Sstevel@tonic-gate else if (!strcasecmp(ARG0, "terse"))
14450Sstevel@tonic-gate version_option = 2;
14460Sstevel@tonic-gate else if (!strcasecmp(ARG0, "brief"))
14470Sstevel@tonic-gate version_option = 1;
14480Sstevel@tonic-gate }
14490Sstevel@tonic-gate switch (version_option) {
14500Sstevel@tonic-gate default:
14510Sstevel@tonic-gate reply(220, "%s FTP server (%s) ready.", hostname, version);
14520Sstevel@tonic-gate break;
14530Sstevel@tonic-gate case 1:
14540Sstevel@tonic-gate reply(220, "%s FTP server ready.", hostname);
14550Sstevel@tonic-gate break;
14560Sstevel@tonic-gate case 2:
14570Sstevel@tonic-gate reply(220, "FTP server ready.");
14580Sstevel@tonic-gate break;
14590Sstevel@tonic-gate case 3:
14600Sstevel@tonic-gate output_text[0] = '\0';
14610Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
14620Sstevel@tonic-gate if (which > 1)
14630Sstevel@tonic-gate (void) strlcat(output_text, " ", sizeof(output_text));
14640Sstevel@tonic-gate (void) strlcat(output_text, ARG[which], sizeof(output_text));
14650Sstevel@tonic-gate }
14660Sstevel@tonic-gate reply(220, "%s", output_text);
14670Sstevel@tonic-gate break;
14680Sstevel@tonic-gate }
14690Sstevel@tonic-gate }
14700Sstevel@tonic-gate (void) setjmp(errcatch);
14710Sstevel@tonic-gate
14720Sstevel@tonic-gate for (;;)
14730Sstevel@tonic-gate (void) yyparse();
14740Sstevel@tonic-gate /* NOTREACHED */
14750Sstevel@tonic-gate }
14760Sstevel@tonic-gate
14770Sstevel@tonic-gate
randomsig(int sig)14780Sstevel@tonic-gate SIGNAL_TYPE randomsig(int sig)
14790Sstevel@tonic-gate {
14800Sstevel@tonic-gate #ifdef HAVE_SIGLIST
14810Sstevel@tonic-gate syslog(LOG_ERR, "exiting on signal %d: %s", sig, sys_siglist[sig]);
14820Sstevel@tonic-gate #else
14830Sstevel@tonic-gate syslog(LOG_ERR, "exiting on signal %d", sig);
14840Sstevel@tonic-gate #endif
14850Sstevel@tonic-gate chdir("/");
14860Sstevel@tonic-gate signal(SIGIOT, SIG_DFL);
14870Sstevel@tonic-gate signal(SIGILL, SIG_DFL);
14880Sstevel@tonic-gate exit(1);
14890Sstevel@tonic-gate /* dologout(-1); *//* NOTREACHED */
14900Sstevel@tonic-gate }
14910Sstevel@tonic-gate
lostconn(int sig)14920Sstevel@tonic-gate SIGNAL_TYPE lostconn(int sig)
14930Sstevel@tonic-gate {
14940Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
14950Sstevel@tonic-gate syslog(LOG_INFO, "lost connection to %s", remoteident);
14960Sstevel@tonic-gate #else
14970Sstevel@tonic-gate if (debug)
14980Sstevel@tonic-gate syslog(LOG_DEBUG, "lost connection to %s", remoteident);
14990Sstevel@tonic-gate #endif
15000Sstevel@tonic-gate dologout(-1);
15010Sstevel@tonic-gate }
15020Sstevel@tonic-gate
15030Sstevel@tonic-gate static char ttyline[20];
15040Sstevel@tonic-gate
15050Sstevel@tonic-gate #ifdef MAPPING_CHDIR
15060Sstevel@tonic-gate /* Keep track of the path the user has chdir'd into and respond with
15070Sstevel@tonic-gate * that to pwd commands. This is to avoid having the absolue disk
15080Sstevel@tonic-gate * path returned, which I want to avoid.
15090Sstevel@tonic-gate */
15100Sstevel@tonic-gate char mapped_path[MAXPATHLEN] = "/";
15110Sstevel@tonic-gate
15120Sstevel@tonic-gate #if !defined(HAVE_GETCWD)
mapping_getwd(char * path)15130Sstevel@tonic-gate char *mapping_getwd(char *path)
15140Sstevel@tonic-gate {
15150Sstevel@tonic-gate strcpy(path, mapped_path);
15160Sstevel@tonic-gate return path;
15170Sstevel@tonic-gate }
15180Sstevel@tonic-gate #endif /* !defined(HAVE_GETCWD) */
15190Sstevel@tonic-gate
mapping_getcwd(char * path,size_t size)15200Sstevel@tonic-gate char *mapping_getcwd(char *path, size_t size)
15210Sstevel@tonic-gate {
15220Sstevel@tonic-gate (void) strlcpy(path, mapped_path, size);
15230Sstevel@tonic-gate return path;
15240Sstevel@tonic-gate }
15250Sstevel@tonic-gate
15260Sstevel@tonic-gate /* Make these globals rather than local to mapping_chdir to avoid stack overflow */
15270Sstevel@tonic-gate char pathspace[MAXPATHLEN];
15280Sstevel@tonic-gate char old_mapped_path[MAXPATHLEN];
15290Sstevel@tonic-gate
do_elem(char * dir)15300Sstevel@tonic-gate void do_elem(char *dir)
15310Sstevel@tonic-gate {
15320Sstevel@tonic-gate /* . */
15330Sstevel@tonic-gate if (dir[0] == '.' && dir[1] == '\0') {
15340Sstevel@tonic-gate /* ignore it */
15350Sstevel@tonic-gate return;
15360Sstevel@tonic-gate }
15370Sstevel@tonic-gate
15380Sstevel@tonic-gate /* .. */
15390Sstevel@tonic-gate if (dir[0] == '.' && dir[1] == '.' && dir[2] == '\0') {
15400Sstevel@tonic-gate char *last;
15410Sstevel@tonic-gate /* lop the last directory off the path */
15420Sstevel@tonic-gate if ((last = strrchr(mapped_path, '/'))) {
15430Sstevel@tonic-gate /* If start of pathname leave the / */
15440Sstevel@tonic-gate if (last == mapped_path)
15450Sstevel@tonic-gate last++;
15460Sstevel@tonic-gate *last = '\0';
15470Sstevel@tonic-gate }
15480Sstevel@tonic-gate return;
15490Sstevel@tonic-gate }
15500Sstevel@tonic-gate
15510Sstevel@tonic-gate /* append the dir part with a leading / unless at root */
15520Sstevel@tonic-gate if (!(mapped_path[0] == '/' && mapped_path[1] == '\0'))
15530Sstevel@tonic-gate (void) strlcat(mapped_path, "/", sizeof(mapped_path));
15540Sstevel@tonic-gate (void) strlcat(mapped_path, dir, sizeof(mapped_path));
15550Sstevel@tonic-gate }
15560Sstevel@tonic-gate
mapping_chdir(char * orig_path)15570Sstevel@tonic-gate int mapping_chdir(char *orig_path)
15580Sstevel@tonic-gate {
15590Sstevel@tonic-gate int ret;
15600Sstevel@tonic-gate char *sl, *path;
15610Sstevel@tonic-gate
15620Sstevel@tonic-gate (void) strlcpy(old_mapped_path, mapped_path, sizeof(old_mapped_path));
15630Sstevel@tonic-gate (void) strlcpy(pathspace, orig_path, sizeof(pathspace));
15640Sstevel@tonic-gate path = pathspace;
15650Sstevel@tonic-gate
15660Sstevel@tonic-gate /* / at start of path, set the start of the mapped_path to / */
15670Sstevel@tonic-gate if (path[0] == '/') {
15680Sstevel@tonic-gate mapped_path[0] = '/';
15690Sstevel@tonic-gate mapped_path[1] = '\0';
15700Sstevel@tonic-gate path++;
15710Sstevel@tonic-gate }
15720Sstevel@tonic-gate
15730Sstevel@tonic-gate while ((sl = strchr(path, '/'))) {
15740Sstevel@tonic-gate char *dir;
15750Sstevel@tonic-gate dir = path;
15760Sstevel@tonic-gate *sl = '\0';
15770Sstevel@tonic-gate path = sl + 1;
15780Sstevel@tonic-gate if (*dir)
15790Sstevel@tonic-gate do_elem(dir);
15800Sstevel@tonic-gate if (*path == '\0')
15810Sstevel@tonic-gate break;
15820Sstevel@tonic-gate }
15830Sstevel@tonic-gate if (*path)
15840Sstevel@tonic-gate do_elem(path);
15850Sstevel@tonic-gate
15860Sstevel@tonic-gate if ((ret = chdir(mapped_path)) < 0) {
15870Sstevel@tonic-gate (void) strlcpy(mapped_path, old_mapped_path, sizeof(mapped_path));
15880Sstevel@tonic-gate }
15890Sstevel@tonic-gate
15900Sstevel@tonic-gate return ret;
15910Sstevel@tonic-gate }
15920Sstevel@tonic-gate /* From now on use the mapping version */
15930Sstevel@tonic-gate
15940Sstevel@tonic-gate #define chdir(d) mapping_chdir(d)
15950Sstevel@tonic-gate #define getwd(d) mapping_getwd(d)
15960Sstevel@tonic-gate #define getcwd(d,u) mapping_getcwd((d),(u))
15970Sstevel@tonic-gate
15980Sstevel@tonic-gate #endif /* MAPPING_CHDIR */
15990Sstevel@tonic-gate
16000Sstevel@tonic-gate /* Helper function for sgetpwnam(). */
sgetsave(char * s)16010Sstevel@tonic-gate char *sgetsave(char *s)
16020Sstevel@tonic-gate {
16030Sstevel@tonic-gate char *new;
16040Sstevel@tonic-gate
16050Sstevel@tonic-gate new = (char *) malloc(strlen(s) + 1);
16060Sstevel@tonic-gate
16070Sstevel@tonic-gate if (new == NULL) {
16080Sstevel@tonic-gate perror_reply(421, "Local resource failure: malloc");
16090Sstevel@tonic-gate dologout(1);
16100Sstevel@tonic-gate /* NOTREACHED */
16110Sstevel@tonic-gate }
16120Sstevel@tonic-gate (void) strcpy(new, s);
16130Sstevel@tonic-gate return (new);
16140Sstevel@tonic-gate }
16150Sstevel@tonic-gate
16160Sstevel@tonic-gate /* Save the result of a getpwnam. Used for USER command, since the data
16170Sstevel@tonic-gate * returned must not be clobbered by any other command (e.g., globbing). */
sgetpwnam(char * name)16180Sstevel@tonic-gate struct passwd *sgetpwnam(char *name)
16190Sstevel@tonic-gate {
16200Sstevel@tonic-gate static struct passwd save;
16210Sstevel@tonic-gate register struct passwd *p;
16220Sstevel@tonic-gate #ifdef M_UNIX
16230Sstevel@tonic-gate struct passwd *ret = (struct passwd *) NULL;
16240Sstevel@tonic-gate #endif
16250Sstevel@tonic-gate char *sgetsave(char *s);
16260Sstevel@tonic-gate #ifdef KERBEROS
16270Sstevel@tonic-gate register struct authorization *q;
16280Sstevel@tonic-gate #endif /* KERBEROS */
16290Sstevel@tonic-gate
16300Sstevel@tonic-gate #if defined(SecureWare) || defined(HPUX_10_TRUSTED)
16310Sstevel@tonic-gate struct pr_passwd *pr;
16320Sstevel@tonic-gate #endif
16330Sstevel@tonic-gate
16340Sstevel@tonic-gate #ifdef KERBEROS
16350Sstevel@tonic-gate init_krb();
16360Sstevel@tonic-gate q = getauthuid(p->pw_uid);
16370Sstevel@tonic-gate end_krb();
16380Sstevel@tonic-gate #endif /* KERBEROS */
16390Sstevel@tonic-gate
16400Sstevel@tonic-gate #ifdef M_UNIX
16410Sstevel@tonic-gate #if defined(SecureWare) || defined(HPUX_10_TRUSTED)
16420Sstevel@tonic-gate if ((pr = getprpwnam(name)) == NULL)
16430Sstevel@tonic-gate goto DONE;
16440Sstevel@tonic-gate #endif /* SecureWare || HPUX_10_TRUSTED */
16450Sstevel@tonic-gate #ifdef OTHER_PASSWD
16460Sstevel@tonic-gate if ((p = bero_getpwnam(name, _path_passwd)) == NULL)
16470Sstevel@tonic-gate #else
16480Sstevel@tonic-gate if ((p = getpwnam(name)) == NULL)
16490Sstevel@tonic-gate #endif
16500Sstevel@tonic-gate goto DONE;
16510Sstevel@tonic-gate #else /* M_UNIX */
16520Sstevel@tonic-gate #if defined(SecureWare) || defined(HPUX_10_TRUSTED)
16530Sstevel@tonic-gate if ((pr = getprpwnam(name)) == NULL)
16540Sstevel@tonic-gate return ((struct passwd *) pr);
16550Sstevel@tonic-gate #endif /* SecureWare || HPUX_10_TRUSTED */
16560Sstevel@tonic-gate #ifdef OTHER_PASSWD
16570Sstevel@tonic-gate if ((p = bero_getpwnam(name, _path_passwd)) == NULL)
16580Sstevel@tonic-gate #else
16590Sstevel@tonic-gate if ((p = getpwnam(name)) == NULL)
16600Sstevel@tonic-gate #endif
16610Sstevel@tonic-gate return (p);
16620Sstevel@tonic-gate #endif /* M_UNIX */
16630Sstevel@tonic-gate
16640Sstevel@tonic-gate if (save.pw_name)
16650Sstevel@tonic-gate free(save.pw_name);
16660Sstevel@tonic-gate if (save.pw_gecos)
16670Sstevel@tonic-gate free(save.pw_gecos);
16680Sstevel@tonic-gate if (save.pw_dir)
16690Sstevel@tonic-gate free(save.pw_dir);
16700Sstevel@tonic-gate if (save.pw_shell)
16710Sstevel@tonic-gate free(save.pw_shell);
16720Sstevel@tonic-gate if (save.pw_passwd)
16730Sstevel@tonic-gate free(save.pw_passwd);
16740Sstevel@tonic-gate
16750Sstevel@tonic-gate save = *p;
16760Sstevel@tonic-gate
16770Sstevel@tonic-gate save.pw_name = sgetsave(p->pw_name);
16780Sstevel@tonic-gate
16790Sstevel@tonic-gate #ifdef KERBEROS
16800Sstevel@tonic-gate save.pw_passwd = sgetsave(q->a_password);
16810Sstevel@tonic-gate #elif defined(SecureWare) || defined(HPUX_10_TRUSTED)
16820Sstevel@tonic-gate if (pr->uflg.fg_encrypt && pr->ufld.fd_encrypt && *pr->ufld.fd_encrypt)
16830Sstevel@tonic-gate save.pw_passwd = sgetsave(pr->ufld.fd_encrypt);
16840Sstevel@tonic-gate else
16850Sstevel@tonic-gate save.pw_passwd = sgetsave("");
16860Sstevel@tonic-gate #else
16870Sstevel@tonic-gate save.pw_passwd = sgetsave(p->pw_passwd);
16880Sstevel@tonic-gate #endif
16890Sstevel@tonic-gate #ifdef SHADOW_PASSWORD
16900Sstevel@tonic-gate if (p && (p->pw_passwd==NULL || strlen(p->pw_passwd)<8)) {
16910Sstevel@tonic-gate struct spwd *spw;
16920Sstevel@tonic-gate #ifdef OTHER_PASSWD
16930Sstevel@tonic-gate if ((spw = bero_getspnam(p->pw_name, _path_shadow)) != NULL) {
16940Sstevel@tonic-gate #else
16950Sstevel@tonic-gate setspent();
16960Sstevel@tonic-gate if ((spw = getspnam(p->pw_name)) != NULL) {
16970Sstevel@tonic-gate #endif
16980Sstevel@tonic-gate int expired = 0;
16990Sstevel@tonic-gate /*XXX Does this work on all Shadow Password Implementations? */
17000Sstevel@tonic-gate /* it is supposed to work on Solaris 2.x */
17010Sstevel@tonic-gate time_t now;
17020Sstevel@tonic-gate long today;
17030Sstevel@tonic-gate
17040Sstevel@tonic-gate now = time((time_t *) 0);
17050Sstevel@tonic-gate today = now / (60 * 60 * 24);
17060Sstevel@tonic-gate
17070Sstevel@tonic-gate if ((spw->sp_expire > 0) && (spw->sp_expire < today))
17080Sstevel@tonic-gate expired++;
17090Sstevel@tonic-gate if ((spw->sp_max > 0) && (spw->sp_lstchg > 0) &&
17100Sstevel@tonic-gate (spw->sp_lstchg + spw->sp_max < today))
17110Sstevel@tonic-gate expired++;
17120Sstevel@tonic-gate free(save.pw_passwd);
17130Sstevel@tonic-gate save.pw_passwd = sgetsave(expired ? "" : spw->sp_pwdp);
17140Sstevel@tonic-gate }
17150Sstevel@tonic-gate /* Don't overwrite the password if the shadow read fails, getpwnam() is NIS
17160Sstevel@tonic-gate aware but getspnam() is not. */
17170Sstevel@tonic-gate /* Shadow passwords are optional on Linux. --marekm */
17180Sstevel@tonic-gate #if !defined(LINUX) && !defined(UNIXWARE)
17190Sstevel@tonic-gate else {
17200Sstevel@tonic-gate free(save.pw_passwd);
17210Sstevel@tonic-gate save.pw_passwd = sgetsave("");
17220Sstevel@tonic-gate }
17230Sstevel@tonic-gate #endif
17240Sstevel@tonic-gate /* marekm's fix for linux proc file system shadow passwd exposure problem */
17250Sstevel@tonic-gate #ifndef OTHER_PASSWD
17260Sstevel@tonic-gate endspent();
17270Sstevel@tonic-gate #endif
17280Sstevel@tonic-gate }
17290Sstevel@tonic-gate #endif
17300Sstevel@tonic-gate save.pw_gecos = sgetsave(p->pw_gecos);
17310Sstevel@tonic-gate save.pw_dir = sgetsave(p->pw_dir);
17320Sstevel@tonic-gate save.pw_shell = sgetsave(p->pw_shell);
17330Sstevel@tonic-gate #ifdef M_UNIX
17340Sstevel@tonic-gate ret = &save;
17350Sstevel@tonic-gate DONE:
17360Sstevel@tonic-gate endpwent();
17370Sstevel@tonic-gate #endif
17380Sstevel@tonic-gate #if defined(SecureWare) || defined(HPUX_10_TRUSTED)
17390Sstevel@tonic-gate endprpwent();
17400Sstevel@tonic-gate #endif
17410Sstevel@tonic-gate #ifdef M_UNIX
17420Sstevel@tonic-gate return (ret);
17430Sstevel@tonic-gate #else
17440Sstevel@tonic-gate return (&save);
17450Sstevel@tonic-gate #endif
17460Sstevel@tonic-gate }
17470Sstevel@tonic-gate #if defined(SKEY) && !defined(__NetBSD__)
17480Sstevel@tonic-gate /*
17490Sstevel@tonic-gate * From Wietse Venema, Eindhoven University of Technology.
17500Sstevel@tonic-gate */
17510Sstevel@tonic-gate /* skey_challenge - additional password prompt stuff */
17520Sstevel@tonic-gate
17530Sstevel@tonic-gate char *skey_challenge(char *name, struct passwd *pwd, int pwok)
17540Sstevel@tonic-gate {
17550Sstevel@tonic-gate static char buf[128];
17560Sstevel@tonic-gate char sbuf[40];
17570Sstevel@tonic-gate struct skey skey;
17580Sstevel@tonic-gate
17590Sstevel@tonic-gate /* Display s/key challenge where appropriate. */
17600Sstevel@tonic-gate
17610Sstevel@tonic-gate if (pwd == NULL || skeychallenge(&skey, pwd->pw_name, sbuf))
17620Sstevel@tonic-gate sprintf(buf, "Password required for %s.", name);
17630Sstevel@tonic-gate else
17640Sstevel@tonic-gate sprintf(buf, "%s %s for %s.", sbuf,
17650Sstevel@tonic-gate pwok ? "allowed" : "required", name);
17660Sstevel@tonic-gate return (buf);
17670Sstevel@tonic-gate }
17680Sstevel@tonic-gate #endif
17690Sstevel@tonic-gate
17700Sstevel@tonic-gate int login_attempts; /* number of failed login attempts */
17710Sstevel@tonic-gate int askpasswd; /* had user command, ask for passwd */
17720Sstevel@tonic-gate #ifndef HELP_CRACKERS
17730Sstevel@tonic-gate int DenyLoginAfterPassword;
17740Sstevel@tonic-gate char DelayedMessageFile[MAXPATHLEN];
17750Sstevel@tonic-gate extern void pr_mesg(int msgcode, char *msgfile);
17760Sstevel@tonic-gate #endif
17770Sstevel@tonic-gate
17780Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
17790Sstevel@tonic-gate static int defaultserver_allow(const char *username)
17800Sstevel@tonic-gate {
17810Sstevel@tonic-gate struct aclmember *entry = NULL;
17820Sstevel@tonic-gate int which;
17830Sstevel@tonic-gate
17840Sstevel@tonic-gate while (getaclentry("defaultserver", &entry))
17850Sstevel@tonic-gate if (ARG0 && !strcasecmp(ARG0, "allow"))
17860Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++)
17870Sstevel@tonic-gate if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
17880Sstevel@tonic-gate return (1);
17890Sstevel@tonic-gate return (0);
17900Sstevel@tonic-gate }
17910Sstevel@tonic-gate
17920Sstevel@tonic-gate static int defaultserver_deny(const char *username)
17930Sstevel@tonic-gate {
17940Sstevel@tonic-gate struct aclmember *entry = NULL;
17950Sstevel@tonic-gate int which;
17960Sstevel@tonic-gate
17970Sstevel@tonic-gate while (getaclentry("defaultserver", &entry))
17980Sstevel@tonic-gate if (ARG0 && !strcasecmp(ARG0, "deny"))
17990Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++)
18000Sstevel@tonic-gate if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
18010Sstevel@tonic-gate return (1);
18020Sstevel@tonic-gate return (0);
18030Sstevel@tonic-gate }
18040Sstevel@tonic-gate
18050Sstevel@tonic-gate static int defaultserver_private(void)
18060Sstevel@tonic-gate {
18070Sstevel@tonic-gate struct aclmember *entry = NULL;
18080Sstevel@tonic-gate
18090Sstevel@tonic-gate while (getaclentry("defaultserver", &entry))
18100Sstevel@tonic-gate if (ARG0 && !strcasecmp(ARG0, "private"))
18110Sstevel@tonic-gate return (1);
18120Sstevel@tonic-gate return (0);
18130Sstevel@tonic-gate }
18140Sstevel@tonic-gate #endif
18150Sstevel@tonic-gate
18160Sstevel@tonic-gate /* USER command. Sets global passwd pointer pw if named account exists and is
18170Sstevel@tonic-gate * acceptable; sets askpasswd if a PASS command is expected. If logged in
18180Sstevel@tonic-gate * previously, need to reset state. If name is "ftp" or "anonymous", the
18190Sstevel@tonic-gate * name is not in the ftpusers file, and ftp account exists, set anonymous and
18200Sstevel@tonic-gate * pw, then just return. If account doesn't exist, ask for passwd anyway.
18210Sstevel@tonic-gate * Otherwise, check user requesting login privileges. Disallow anyone who
18220Sstevel@tonic-gate * does not have a standard shell as returned by getusershell(). Disallow
18230Sstevel@tonic-gate * anyone mentioned in the ftpusers file to allow people such as root and
18240Sstevel@tonic-gate * uucp to be avoided. */
18250Sstevel@tonic-gate
18260Sstevel@tonic-gate /*
18270Sstevel@tonic-gate char *getusershell();
18280Sstevel@tonic-gate */
18290Sstevel@tonic-gate void user(char *name)
18300Sstevel@tonic-gate {
18310Sstevel@tonic-gate char *cp;
18320Sstevel@tonic-gate char *shell;
18330Sstevel@tonic-gate #ifdef BSD_AUTH
18340Sstevel@tonic-gate char *auth;
18350Sstevel@tonic-gate #endif
18360Sstevel@tonic-gate #if defined(USE_GSS)
18370Sstevel@tonic-gate int gss_need_passwd = 1;
18380Sstevel@tonic-gate #endif
18390Sstevel@tonic-gate
18400Sstevel@tonic-gate /* H* fix: if we're logged in at all, we can't log in again. */
18410Sstevel@tonic-gate if (logged_in) {
18420Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
18430Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (already logged in as %s) FROM %s, %s",
18440Sstevel@tonic-gate pw->pw_name, remoteident, name);
18450Sstevel@tonic-gate #endif
18460Sstevel@tonic-gate reply(530, "Already logged in.");
18470Sstevel@tonic-gate return;
18480Sstevel@tonic-gate }
18490Sstevel@tonic-gate #ifndef HELP_CRACKERS
18500Sstevel@tonic-gate askpasswd = 1;
18510Sstevel@tonic-gate DenyLoginAfterPassword = 0;
18520Sstevel@tonic-gate DelayedMessageFile[0] = '\0';
18530Sstevel@tonic-gate #endif
18540Sstevel@tonic-gate #ifdef BSD_AUTH
18550Sstevel@tonic-gate if ((auth = strchr(name, ':')))
18560Sstevel@tonic-gate *auth++ = 0;
18570Sstevel@tonic-gate #endif
18580Sstevel@tonic-gate
18590Sstevel@tonic-gate #ifdef HOST_ACCESS /* 19-Mar-93 BM */
18600Sstevel@tonic-gate if (!rhost_ok(name, remotehost, remoteaddr)) {
18610Sstevel@tonic-gate #ifndef HELP_CRACKERS
18620Sstevel@tonic-gate DenyLoginAfterPassword = 1;
18630Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (name in %s) FROM %s, %s",
18640Sstevel@tonic-gate _path_ftphosts, remoteident, name);
18650Sstevel@tonic-gate #else
18660Sstevel@tonic-gate reply(530, "User %s access denied.", name);
18670Sstevel@tonic-gate syslog(LOG_NOTICE,
18680Sstevel@tonic-gate "FTP LOGIN REFUSED (name in %s) FROM %s, %s",
18690Sstevel@tonic-gate _path_ftphosts, remoteident, name);
18700Sstevel@tonic-gate return;
18710Sstevel@tonic-gate #endif
18720Sstevel@tonic-gate }
18730Sstevel@tonic-gate #endif
18740Sstevel@tonic-gate
18750Sstevel@tonic-gate strncpy(the_user, name, MAXUSERNAMELEN - 1);
18760Sstevel@tonic-gate
18770Sstevel@tonic-gate anonymous = 0;
18780Sstevel@tonic-gate guest = 0;
18790Sstevel@tonic-gate
18800Sstevel@tonic-gate if (!strcasecmp(name, "ftp") || !strcasecmp(name, "anonymous")) {
18810Sstevel@tonic-gate struct aclmember *entry = NULL;
18820Sstevel@tonic-gate int machineok = 1;
18830Sstevel@tonic-gate char guestservername[MAXHOSTNAMELEN];
18840Sstevel@tonic-gate guestservername[0] = '\0';
18850Sstevel@tonic-gate
18860Sstevel@tonic-gate #ifdef NO_ANONYMOUS_ACCESS
18870Sstevel@tonic-gate reply(530, "Anonymous FTP access denied.");
18880Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (anonymous ftp not supported) FROM %s, %s",
18890Sstevel@tonic-gate remoteident, name);
18900Sstevel@tonic-gate return;
18910Sstevel@tonic-gate #else
18920Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
18930Sstevel@tonic-gate if (!virtual_mode && defaultserver_private()) {
18940Sstevel@tonic-gate #ifndef HELP_CRACKERS
18950Sstevel@tonic-gate DenyLoginAfterPassword = 1;
18960Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (anonymous ftp denied on default server) FROM %s, %s",
18970Sstevel@tonic-gate remoteident, name);
18980Sstevel@tonic-gate #else
18990Sstevel@tonic-gate reply(530, "User %s access denied.", name);
19000Sstevel@tonic-gate syslog(LOG_NOTICE,
19010Sstevel@tonic-gate "FTP LOGIN REFUSED (anonymous ftp denied on default server) FROM %s, %s",
19020Sstevel@tonic-gate remoteident, name);
19030Sstevel@tonic-gate return;
19040Sstevel@tonic-gate #endif
19050Sstevel@tonic-gate }
19060Sstevel@tonic-gate #endif
19070Sstevel@tonic-gate if (checkuser("ftp") || checkuser("anonymous")) {
19080Sstevel@tonic-gate #ifndef HELP_CRACKERS
19090Sstevel@tonic-gate DenyLoginAfterPassword = 1;
19100Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp in %s) FROM %s, %s",
19110Sstevel@tonic-gate _path_ftpusers, remoteident, name);
19120Sstevel@tonic-gate #else
19130Sstevel@tonic-gate reply(530, "User %s access denied.", name);
19140Sstevel@tonic-gate syslog(LOG_NOTICE,
19150Sstevel@tonic-gate "FTP LOGIN REFUSED (ftp in %s) FROM %s, %s",
19160Sstevel@tonic-gate _path_ftpusers, remoteident, name);
19170Sstevel@tonic-gate return;
19180Sstevel@tonic-gate #endif
19190Sstevel@tonic-gate
19200Sstevel@tonic-gate /*
19210Sstevel@tonic-gate ** Algorithm used:
19220Sstevel@tonic-gate ** - if no "guestserver" directive is present,
19230Sstevel@tonic-gate ** anonymous access is allowed, for backward compatibility.
19240Sstevel@tonic-gate ** - if a "guestserver" directive is present,
19250Sstevel@tonic-gate ** anonymous access is restricted to the machines listed,
19260Sstevel@tonic-gate ** usually the machine whose CNAME on the current domain
19270Sstevel@tonic-gate ** is "ftp"...
19280Sstevel@tonic-gate **
19290Sstevel@tonic-gate ** the format of the "guestserver" line is
19300Sstevel@tonic-gate ** guestserver [<machine1> [<machineN>]]
19310Sstevel@tonic-gate ** that is, "guestserver" will forbid anonymous access on all machines
19320Sstevel@tonic-gate ** while "guestserver ftp inf" will allow anonymous access on
19330Sstevel@tonic-gate ** the two machines whose CNAMES are "ftp.enst.fr" and "inf.enst.fr".
19340Sstevel@tonic-gate **
19350Sstevel@tonic-gate ** if anonymous access is denied on the current machine,
19360Sstevel@tonic-gate ** the user will be asked to use the first machine listed (if any)
19370Sstevel@tonic-gate ** on the "guestserver" line instead:
19380Sstevel@tonic-gate ** 530- Guest login not allowed on this machine,
19390Sstevel@tonic-gate ** connect to ftp.enst.fr instead.
19400Sstevel@tonic-gate **
19410Sstevel@tonic-gate ** -- <Nicolas.Pioch@enst.fr>
19420Sstevel@tonic-gate */
19430Sstevel@tonic-gate }
19440Sstevel@tonic-gate else if (getaclentry("guestserver", &entry)) {
19450Sstevel@tonic-gate char *tmphost;
19460Sstevel@tonic-gate
19470Sstevel@tonic-gate /*
19480Sstevel@tonic-gate ** if a "guestserver" line is present,
19490Sstevel@tonic-gate ** default is not to allow guest logins
19500Sstevel@tonic-gate */
19510Sstevel@tonic-gate machineok = 0;
19520Sstevel@tonic-gate
19530Sstevel@tonic-gate if (hostname[0]
19540Sstevel@tonic-gate && ((tmphost = wu_gethostbyname(hostname)))) {
19550Sstevel@tonic-gate
19560Sstevel@tonic-gate /*
19570Sstevel@tonic-gate ** hostname is the only first part of the FQDN
19580Sstevel@tonic-gate ** this may or may not correspond to the h_name value
19590Sstevel@tonic-gate ** (machines with more than one IP#, CNAMEs...)
19600Sstevel@tonic-gate ** -> need to fix that, calling gethostbyname on hostname
19610Sstevel@tonic-gate **
19620Sstevel@tonic-gate ** WARNING!
19630Sstevel@tonic-gate ** for SunOS 4.x, you need to have a working resolver in the libc
19640Sstevel@tonic-gate ** for CNAMES to work properly.
19650Sstevel@tonic-gate ** If you don't, add "-lresolv" to the libraries before compiling!
19660Sstevel@tonic-gate */
19670Sstevel@tonic-gate char dns_localhost[MAXHOSTNAMELEN];
19680Sstevel@tonic-gate int machinecount;
19690Sstevel@tonic-gate
19700Sstevel@tonic-gate strncpy(dns_localhost, tmphost, sizeof(dns_localhost));
19710Sstevel@tonic-gate dns_localhost[sizeof(dns_localhost) - 1] = '\0';
19720Sstevel@tonic-gate
19730Sstevel@tonic-gate for (machinecount = 0;
19740Sstevel@tonic-gate (machinecount < MAXARGS) && entry->arg[machinecount];
19750Sstevel@tonic-gate machinecount++) {
19760Sstevel@tonic-gate
19770Sstevel@tonic-gate if ((tmphost = wu_gethostbyname(entry->arg[machinecount]))) {
19780Sstevel@tonic-gate /*
19790Sstevel@tonic-gate ** remember the name of the first machine for redirection
19800Sstevel@tonic-gate */
19810Sstevel@tonic-gate
19820Sstevel@tonic-gate if (!machinecount) {
19830Sstevel@tonic-gate strncpy(guestservername, entry->arg[machinecount],
19840Sstevel@tonic-gate sizeof(guestservername));
19850Sstevel@tonic-gate guestservername[sizeof(guestservername) - 1] = '\0';
19860Sstevel@tonic-gate }
19870Sstevel@tonic-gate
19880Sstevel@tonic-gate if (!strcasecmp(tmphost, dns_localhost)) {
19890Sstevel@tonic-gate machineok++;
19900Sstevel@tonic-gate break;
19910Sstevel@tonic-gate }
19920Sstevel@tonic-gate }
19930Sstevel@tonic-gate }
19940Sstevel@tonic-gate }
19950Sstevel@tonic-gate }
19960Sstevel@tonic-gate if (!machineok) {
19970Sstevel@tonic-gate if (guestservername[0])
19980Sstevel@tonic-gate reply(530,
19990Sstevel@tonic-gate "Guest login not allowed on this machine, connect to %s instead.",
20000Sstevel@tonic-gate guestservername);
20010Sstevel@tonic-gate else
20020Sstevel@tonic-gate reply(530,
20030Sstevel@tonic-gate "Guest login not allowed on this machine.");
20040Sstevel@tonic-gate syslog(LOG_NOTICE,
20050Sstevel@tonic-gate "FTP LOGIN REFUSED (localhost not in guestservers) FROM %s, %s",
20060Sstevel@tonic-gate remoteident, name);
20070Sstevel@tonic-gate /* End of the big patch -- Nap */
20080Sstevel@tonic-gate
20090Sstevel@tonic-gate dologout(0);
20100Sstevel@tonic-gate }
20110Sstevel@tonic-gate else if ((pw = sgetpwnam("ftp")) != NULL) {
20120Sstevel@tonic-gate anonymous = 1; /* for the access_ok call */
20130Sstevel@tonic-gate if (access_ok(530) < 1) {
20140Sstevel@tonic-gate #ifndef HELP_CRACKERS
20150Sstevel@tonic-gate DenyLoginAfterPassword = 1;
20160Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s, %s",
20170Sstevel@tonic-gate remoteident, name);
20180Sstevel@tonic-gate reply(331, "Guest login ok, send your complete e-mail address as password.");
20190Sstevel@tonic-gate #else
20200Sstevel@tonic-gate reply(530, "User %s access denied.", name);
20210Sstevel@tonic-gate syslog(LOG_NOTICE,
20220Sstevel@tonic-gate "FTP LOGIN REFUSED (access denied) FROM %s, %s",
20230Sstevel@tonic-gate remoteident, name);
20240Sstevel@tonic-gate dologout(0);
20250Sstevel@tonic-gate #endif
20260Sstevel@tonic-gate }
20270Sstevel@tonic-gate else {
20280Sstevel@tonic-gate askpasswd = 1;
20290Sstevel@tonic-gate /* H* fix: obey use_accessfile a little better. This way, things set on the
20300Sstevel@tonic-gate command line [like xferlog stuff] don't get stupidly overridden.
20310Sstevel@tonic-gate XXX: all these checks maybe should be in acl.c and access.c */
20320Sstevel@tonic-gate if (use_accessfile)
20330Sstevel@tonic-gate acl_setfunctions();
20340Sstevel@tonic-gate reply(331, "Guest login ok, send your complete e-mail address as password.");
20350Sstevel@tonic-gate }
20360Sstevel@tonic-gate }
20370Sstevel@tonic-gate else {
20380Sstevel@tonic-gate #ifndef HELP_CRACKERS
20390Sstevel@tonic-gate DenyLoginAfterPassword = 1;
20400Sstevel@tonic-gate reply(331, "Guest login ok, send your complete e-mail address as password.");
20410Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp not in /etc/passwd) FROM %s, %s",
20420Sstevel@tonic-gate remoteident, name);
20430Sstevel@tonic-gate #else
20440Sstevel@tonic-gate reply(530, "User %s unknown.", name);
20450Sstevel@tonic-gate syslog(LOG_NOTICE,
20460Sstevel@tonic-gate "FTP LOGIN REFUSED (ftp not in /etc/passwd) FROM %s, %s",
20470Sstevel@tonic-gate remoteident, name);
20480Sstevel@tonic-gate #endif
20490Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
20500Sstevel@tonic-gate audit_ftpd_no_anon();
20510Sstevel@tonic-gate #endif
20520Sstevel@tonic-gate }
20530Sstevel@tonic-gate return;
20540Sstevel@tonic-gate #endif
20550Sstevel@tonic-gate }
20560Sstevel@tonic-gate #ifdef ANON_ONLY
20570Sstevel@tonic-gate /* H* fix: define the above to completely DISABLE logins by real users,
20580Sstevel@tonic-gate despite ftpusers, shells, or any of that rot. You can always hang your
20590Sstevel@tonic-gate "real" server off some other port, and access-control it. */
20600Sstevel@tonic-gate
20610Sstevel@tonic-gate else { /* "ftp" or "anon" -- MARK your conditionals, okay?! */
20620Sstevel@tonic-gate #ifndef HELP_CRACKERS
20630Sstevel@tonic-gate DenyLoginAfterPassword = 1;
20640Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (not anonymous) FROM %s, %s",
20650Sstevel@tonic-gate remoteident, name);
20660Sstevel@tonic-gate reply(331, "Password required for %s.", name);
20670Sstevel@tonic-gate #else
20680Sstevel@tonic-gate reply(530, "User %s unknown.", name);
20690Sstevel@tonic-gate syslog(LOG_NOTICE,
20700Sstevel@tonic-gate "FTP LOGIN REFUSED (not anonymous) FROM %s, %s",
20710Sstevel@tonic-gate remoteident, name);
20720Sstevel@tonic-gate #endif
20730Sstevel@tonic-gate return;
20740Sstevel@tonic-gate }
20750Sstevel@tonic-gate /* fall here if username okay in any case */
20760Sstevel@tonic-gate #endif /* ANON_ONLY */
20770Sstevel@tonic-gate
20780Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
20790Sstevel@tonic-gate if (!virtual_mode && defaultserver_deny(name) && !defaultserver_allow(name)) {
20800Sstevel@tonic-gate #ifndef HELP_CRACKERS
20810Sstevel@tonic-gate DenyLoginAfterPassword = 1;
20820Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (ftp denied on default server) FROM %s, %s",
20830Sstevel@tonic-gate remoteident, name);
20840Sstevel@tonic-gate #else
20850Sstevel@tonic-gate reply(530, "User %s access denied.", name);
20860Sstevel@tonic-gate syslog(LOG_NOTICE,
20870Sstevel@tonic-gate "FTP LOGIN REFUSED (ftp denied on default server) FROM %s, %s",
20880Sstevel@tonic-gate remoteident, name);
20890Sstevel@tonic-gate return;
20900Sstevel@tonic-gate #endif
20910Sstevel@tonic-gate }
20920Sstevel@tonic-gate #endif
20930Sstevel@tonic-gate
20940Sstevel@tonic-gate #if defined(USE_GSS)
20950Sstevel@tonic-gate if (gss_info.must_gss_auth &&
20960Sstevel@tonic-gate (!IS_GSSAUTH(cur_auth_type) ||
20970Sstevel@tonic-gate !(gss_info.authstate & GSS_ADAT_DONE))) {
20980Sstevel@tonic-gate reply(530, "Must perform authentication before identifying USER.");
20990Sstevel@tonic-gate return;
21000Sstevel@tonic-gate }
21010Sstevel@tonic-gate #endif /* USE_GSS */
21020Sstevel@tonic-gate
21030Sstevel@tonic-gate if ((pw = sgetpwnam(name)) != NULL) {
21040Sstevel@tonic-gate if ((denieduid(pw->pw_uid) && !alloweduid(pw->pw_uid))
21050Sstevel@tonic-gate || (deniedgid(pw->pw_gid) && !allowedgid(pw->pw_gid))) {
21060Sstevel@tonic-gate #ifndef HELP_CRACKERS
21070Sstevel@tonic-gate DenyLoginAfterPassword = 1;
21080Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (username in denied-uid) FROM %s, %s",
21090Sstevel@tonic-gate remoteident, name);
21100Sstevel@tonic-gate reply(331, "Password required for %s.", name);
21110Sstevel@tonic-gate #else
21120Sstevel@tonic-gate reply(530, "User %s access denied.", name);
21130Sstevel@tonic-gate syslog(LOG_NOTICE,
21140Sstevel@tonic-gate "FTP LOGIN REFUSED (username in denied-uid) FROM %s, %s",
21150Sstevel@tonic-gate remoteident, name);
21160Sstevel@tonic-gate #endif
21170Sstevel@tonic-gate return;
21180Sstevel@tonic-gate }
21190Sstevel@tonic-gate #if defined(USE_GSS)
21200Sstevel@tonic-gate if (IS_GSSAUTH(cur_auth_type) &&
21210Sstevel@tonic-gate (gss_info.authstate & GSS_ADAT_DONE)) {
21220Sstevel@tonic-gate char buf[BUFSIZ];
21230Sstevel@tonic-gate
21240Sstevel@tonic-gate if (gss_user(pw))
21250Sstevel@tonic-gate gss_info.authstate |= GSS_USER_DONE;
21260Sstevel@tonic-gate
21270Sstevel@tonic-gate if (gss_info.must_gss_auth &&
21280Sstevel@tonic-gate !GSSUSERAUTH_OK(gss_info)) {
21290Sstevel@tonic-gate reply(530, "User %s access denied", name);
21300Sstevel@tonic-gate if (logging)
21310Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP GSSAPI LOGIN REFUSED FROM %s, %s",
21320Sstevel@tonic-gate remoteident, name);
21330Sstevel@tonic-gate pw = NULL;
21340Sstevel@tonic-gate return;
21350Sstevel@tonic-gate }
21360Sstevel@tonic-gate /*
21370Sstevel@tonic-gate * If GSSAPI user auth failed, or it succeeded but creds were
21380Sstevel@tonic-gate * not forwarded as required, prompt for password.
21390Sstevel@tonic-gate */
21400Sstevel@tonic-gate gss_need_passwd = !GSSUSERAUTH_OK(gss_info) ||
21410Sstevel@tonic-gate (GSSUSERAUTH_OK(gss_info) &&
21420Sstevel@tonic-gate (gss_info.want_creds && !gss_info.have_creds));
21430Sstevel@tonic-gate if (gss_need_passwd) {
21440Sstevel@tonic-gate snprintf(buf, sizeof(buf),
21450Sstevel@tonic-gate "GSSAPI user %s is authorized as %s password required",
21460Sstevel@tonic-gate gss_info.display_name, name);
21470Sstevel@tonic-gate reply(331, "%s", buf);
21480Sstevel@tonic-gate askpasswd = 1;
21490Sstevel@tonic-gate syslog(LOG_DEBUG, "%s", buf);
21500Sstevel@tonic-gate return;
21510Sstevel@tonic-gate }
21520Sstevel@tonic-gate }
21530Sstevel@tonic-gate #endif /* defined(USE_GSS) */
21540Sstevel@tonic-gate
21550Sstevel@tonic-gate #if !defined(USE_PAM) || (defined(USE_PAM) && defined(OTHER_PASSWD)) || defined(SOLARIS_2) /* PAM should be doing these checks, not ftpd */
21560Sstevel@tonic-gate #if defined(USE_PAM) && !defined(SOLARIS_2)
21570Sstevel@tonic-gate if(!use_pam) {
21580Sstevel@tonic-gate #endif
21590Sstevel@tonic-gate if ((shell = pw->pw_shell) == NULL || *shell == 0)
21600Sstevel@tonic-gate shell = _PATH_BSHELL;
21610Sstevel@tonic-gate while ((cp = getusershell()) != NULL)
21620Sstevel@tonic-gate if (strcmp(cp, shell) == 0)
21630Sstevel@tonic-gate break;
21640Sstevel@tonic-gate endusershell();
21650Sstevel@tonic-gate if (cp == NULL || checkuser(name)) {
21660Sstevel@tonic-gate #ifndef HELP_CRACKERS
21670Sstevel@tonic-gate DenyLoginAfterPassword = 1;
21680Sstevel@tonic-gate if (cp == NULL)
21690Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (shell not in /etc/shells) FROM %s, %s", remoteident, name);
21700Sstevel@tonic-gate else
21710Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (username in %s) FROM %s, %s", _path_ftpusers, remoteident, name);
21720Sstevel@tonic-gate reply(331, "Password required for %s.", name);
21730Sstevel@tonic-gate #else
21740Sstevel@tonic-gate reply(530, "User %s access denied.", name);
21750Sstevel@tonic-gate if (cp == NULL)
21760Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (shell not in /etc/shells) FROM %s, %s", remoteident, name);
21770Sstevel@tonic-gate else
21780Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (username in %s) FROM %s, %s", _path_ftpusers, remoteident, name);
21790Sstevel@tonic-gate #endif /* HELP_CRACKERS */
21800Sstevel@tonic-gate pw = (struct passwd *) NULL;
21810Sstevel@tonic-gate return;
21820Sstevel@tonic-gate }
21830Sstevel@tonic-gate #if defined(USE_PAM) && !defined(SOLARIS_2)
21840Sstevel@tonic-gate } /* if(!use_pam) */
21850Sstevel@tonic-gate #endif
21860Sstevel@tonic-gate #endif /* !USE_PAM || (USE_PAM && OTHER_PASSWD) || SOLARIS_2 */
21870Sstevel@tonic-gate /* if user is a member of any of the guestgroups, cause a chroot() */
21880Sstevel@tonic-gate /* after they log in successfully */
21890Sstevel@tonic-gate if (use_accessfile) { /* see above. _H */
21900Sstevel@tonic-gate guest = acl_guestgroup(pw);
21910Sstevel@tonic-gate if (guest && acl_realgroup(pw))
21920Sstevel@tonic-gate guest = 0;
21930Sstevel@tonic-gate }
21940Sstevel@tonic-gate }
21950Sstevel@tonic-gate if (access_ok(530) < 1) {
21960Sstevel@tonic-gate #ifndef HELP_CRACKERS
21970Sstevel@tonic-gate DenyLoginAfterPassword = 1;
21980Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s, %s",
21990Sstevel@tonic-gate remoteident, name);
22000Sstevel@tonic-gate reply(331, "Password required for %s.", name);
22010Sstevel@tonic-gate #else
22020Sstevel@tonic-gate reply(530, "User %s access denied.", name);
22030Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (access denied) FROM %s, %s",
22040Sstevel@tonic-gate remoteident, name);
22050Sstevel@tonic-gate #endif
22060Sstevel@tonic-gate return;
22070Sstevel@tonic-gate }
22080Sstevel@tonic-gate else if (use_accessfile) /* see above. _H */
22090Sstevel@tonic-gate acl_setfunctions();
22100Sstevel@tonic-gate
22110Sstevel@tonic-gate #ifdef BSD_AUTH
22120Sstevel@tonic-gate if ((cp = start_auth(auth, name, pw)) != NULL) {
22130Sstevel@tonic-gate char *s;
22140Sstevel@tonic-gate
22150Sstevel@tonic-gate for (;;) {
22160Sstevel@tonic-gate s = strsep(&cp, "\n");
22170Sstevel@tonic-gate if (cp == NULL || *cp == '\0')
22180Sstevel@tonic-gate break;
22190Sstevel@tonic-gate lreply(331, "%s", s);
22200Sstevel@tonic-gate }
22210Sstevel@tonic-gate reply(331, "%s", s);
22220Sstevel@tonic-gate }
22230Sstevel@tonic-gate else {
22240Sstevel@tonic-gate #endif /* BSD_AUTH */
22250Sstevel@tonic-gate #ifdef SKEY
22260Sstevel@tonic-gate #ifndef __NetBSD__
22270Sstevel@tonic-gate #ifdef SKEY_NAME
22280Sstevel@tonic-gate /* this is the old way, but freebsd uses it */
22290Sstevel@tonic-gate pwok = skeyaccess(name, NULL, remotehost, remoteaddr);
22300Sstevel@tonic-gate #else
22310Sstevel@tonic-gate /* this is the new way */
22320Sstevel@tonic-gate pwok = skeyaccess(pw, NULL, remotehost, remoteaddr);
22330Sstevel@tonic-gate #endif /* SKEY_NAME */
22340Sstevel@tonic-gate reply(331, "%s", skey_challenge(name, pw, pwok));
22350Sstevel@tonic-gate #else
22360Sstevel@tonic-gate if (skey_haskey(name) == 0) {
22370Sstevel@tonic-gate char *myskey;
22380Sstevel@tonic-gate
22390Sstevel@tonic-gate myskey = skey_keyinfo(name);
22400Sstevel@tonic-gate reply(331, "Password [%s] required for %s.",
22410Sstevel@tonic-gate myskey ? myskey : "error getting challenge", name);
22420Sstevel@tonic-gate }
22430Sstevel@tonic-gate else
22440Sstevel@tonic-gate reply(331, "Password required for %s.", name);
22450Sstevel@tonic-gate #endif /* __NetBSD__ */
22460Sstevel@tonic-gate #else
22470Sstevel@tonic-gate #ifdef OPIE
22480Sstevel@tonic-gate {
22490Sstevel@tonic-gate char prompt[OPIE_CHALLENGE_MAX + 1];
22500Sstevel@tonic-gate opiechallenge(&opiestate, name, prompt);
22510Sstevel@tonic-gate
22520Sstevel@tonic-gate if (askpasswd == -1) {
22530Sstevel@tonic-gate syslog(LOG_WARNING, "Invalid FTP user name %s attempted from %s", name, remotehost);
22540Sstevel@tonic-gate pwok = 0;
22550Sstevel@tonic-gate }
22560Sstevel@tonic-gate else
22570Sstevel@tonic-gate pwok = af_pwok && opiealways(pw->pw_dir);
22580Sstevel@tonic-gate reply(331, "Response to %s %s for %s.",
22590Sstevel@tonic-gate prompt, pwok ? "requested" : "required", name);
22600Sstevel@tonic-gate }
22610Sstevel@tonic-gate #else /* !SKEY */
22620Sstevel@tonic-gate
22630Sstevel@tonic-gate #if defined(USE_GSS)
22640Sstevel@tonic-gate if (GSSUSERAUTH_OK(gss_info) && !gss_need_passwd) {
22650Sstevel@tonic-gate /*
22660Sstevel@tonic-gate * We got this far, we are allowing the GSSAPI authentication
22670Sstevel@tonic-gate * to succeed without further passwd prompting. Jump
22680Sstevel@tonic-gate * to "pass" processing.
22690Sstevel@tonic-gate */
22700Sstevel@tonic-gate askpasswd = 0;
22710Sstevel@tonic-gate logged_in = 1;
22720Sstevel@tonic-gate pass("");
22730Sstevel@tonic-gate return;
22740Sstevel@tonic-gate }
22750Sstevel@tonic-gate #endif /* defined(USE_GSS) */
22760Sstevel@tonic-gate reply(331, "Password required for %s.", name);
22770Sstevel@tonic-gate #endif /* OPIE */
22780Sstevel@tonic-gate #endif /* SKEY */
22790Sstevel@tonic-gate #ifdef BSD_AUTH
22800Sstevel@tonic-gate }
22810Sstevel@tonic-gate #endif /* BSD_AUTH */
22820Sstevel@tonic-gate
22830Sstevel@tonic-gate askpasswd = 1;
22840Sstevel@tonic-gate /* Delay before reading passwd after first failed attempt to slow down
22850Sstevel@tonic-gate * passwd-guessing programs. */
22860Sstevel@tonic-gate if (login_attempts) {
22870Sstevel@tonic-gate enable_signaling(); /* we can allow signals once again: kinch */
22880Sstevel@tonic-gate sleep((unsigned) login_attempts);
22890Sstevel@tonic-gate }
22900Sstevel@tonic-gate return;
22910Sstevel@tonic-gate }
22920Sstevel@tonic-gate
22930Sstevel@tonic-gate /* Check if a user is in the ftpusers file */
22940Sstevel@tonic-gate int checkuser(char *name)
22950Sstevel@tonic-gate {
22960Sstevel@tonic-gate register FILE *fd;
22970Sstevel@tonic-gate register char *p;
22980Sstevel@tonic-gate char line[BUFSIZ];
22990Sstevel@tonic-gate
23000Sstevel@tonic-gate #ifdef SOLARIS_ETC_FTPUSERS
23010Sstevel@tonic-gate static int etc_ftpusers = 0;
23020Sstevel@tonic-gate
23030Sstevel@tonic-gate if (etc_ftpusers) {
23040Sstevel@tonic-gate strcpy(_path_ftpusers, _PATH_FTPUSERS);
23050Sstevel@tonic-gate etc_ftpusers = 0;
23060Sstevel@tonic-gate }
23070Sstevel@tonic-gate retry:
23080Sstevel@tonic-gate #endif
23090Sstevel@tonic-gate if ((fd = fopen(_path_ftpusers, "r")) != NULL) {
23100Sstevel@tonic-gate while (fgets(line, sizeof(line), fd) != NULL)
23110Sstevel@tonic-gate if ((p = strchr(line, '\n')) != NULL) {
23120Sstevel@tonic-gate *p = '\0';
23130Sstevel@tonic-gate if (line[0] == '#')
23140Sstevel@tonic-gate continue;
23150Sstevel@tonic-gate if (strcasecmp(line, name) == 0) {
23160Sstevel@tonic-gate (void) fclose(fd);
23170Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
23180Sstevel@tonic-gate audit_ftpd_excluded(name);
23190Sstevel@tonic-gate #endif
23200Sstevel@tonic-gate #ifdef SOLARIS_ETC_FTPUSERS
23210Sstevel@tonic-gate if (etc_ftpusers)
23220Sstevel@tonic-gate syslog(LOG_NOTICE, "%s is deprecated, use %s instead", _path_ftpusers, _PATH_FTPUSERS);
23230Sstevel@tonic-gate #endif
23240Sstevel@tonic-gate return (1);
23250Sstevel@tonic-gate }
23260Sstevel@tonic-gate }
23270Sstevel@tonic-gate (void) fclose(fd);
23280Sstevel@tonic-gate }
23290Sstevel@tonic-gate #ifdef SOLARIS_ETC_FTPUSERS
23300Sstevel@tonic-gate if (!etc_ftpusers && (strcmp(_path_ftpusers, _PATH_FTPUSERS) == 0)) {
23310Sstevel@tonic-gate strcpy(_path_ftpusers, "/etc/ftpusers");
23320Sstevel@tonic-gate etc_ftpusers = 1;
23330Sstevel@tonic-gate goto retry;
23340Sstevel@tonic-gate }
23350Sstevel@tonic-gate #endif
23360Sstevel@tonic-gate return (0);
23370Sstevel@tonic-gate }
23380Sstevel@tonic-gate
23390Sstevel@tonic-gate int uid_match(char *keyword, uid_t uid)
23400Sstevel@tonic-gate {
23410Sstevel@tonic-gate struct aclmember *entry = NULL;
23420Sstevel@tonic-gate int which;
23430Sstevel@tonic-gate char *ptr;
23440Sstevel@tonic-gate struct passwd *pw;
23450Sstevel@tonic-gate
23460Sstevel@tonic-gate /*
23470Sstevel@tonic-gate * keyword <uid-range> [<uid-range> ...]
23480Sstevel@tonic-gate *
23490Sstevel@tonic-gate * uid-range may be a username or begin with '%' and be treated as numeric:
23500Sstevel@tonic-gate * %<uid> A single numeric UID
23510Sstevel@tonic-gate * %<uid>+ All UIDs greater or equal to UID
23520Sstevel@tonic-gate * %<uid>- All UIDs greater or equal to UID
23530Sstevel@tonic-gate * %-<uid> All UIDs less or equal to UID
23540Sstevel@tonic-gate * %<uid>-<uid> All UIDs between the two (inclusive)
23550Sstevel@tonic-gate * * All UIDs
23560Sstevel@tonic-gate */
23570Sstevel@tonic-gate while (getaclentry(keyword, &entry)) {
23580Sstevel@tonic-gate for (which = 0; (which < MAXARGS) && ARG[which]; which++) {
23590Sstevel@tonic-gate if (!strcmp(ARG[which], "*"))
23600Sstevel@tonic-gate return (1);
23610Sstevel@tonic-gate if (ARG[which][0] == '%') {
23620Sstevel@tonic-gate if ((ptr = strchr(ARG[which] + 1, '-')) == NULL) {
23630Sstevel@tonic-gate if ((ptr = strchr(ARG[which] + 1, '+')) == NULL) {
23640Sstevel@tonic-gate if (uid == strtoul(ARG[which] + 1, NULL, 0))
23650Sstevel@tonic-gate return (1);
23660Sstevel@tonic-gate }
23670Sstevel@tonic-gate else {
23680Sstevel@tonic-gate *ptr++ = '\0';
23690Sstevel@tonic-gate if ((ARG[which][1] == '\0')
23700Sstevel@tonic-gate || (uid >= strtoul(ARG[which] + 1, NULL, 0))) {
23710Sstevel@tonic-gate *--ptr = '+';
23720Sstevel@tonic-gate return (1);
23730Sstevel@tonic-gate }
23740Sstevel@tonic-gate *--ptr = '+';
23750Sstevel@tonic-gate }
23760Sstevel@tonic-gate }
23770Sstevel@tonic-gate else {
23780Sstevel@tonic-gate *ptr++ = '\0';
23790Sstevel@tonic-gate if (((ARG[which][1] == '\0')
23800Sstevel@tonic-gate || (uid >= strtoul(ARG[which] + 1, NULL, 0)))
23810Sstevel@tonic-gate && ((*ptr == '\0')
23820Sstevel@tonic-gate || (uid <= strtoul(ptr, NULL, 0)))) {
23830Sstevel@tonic-gate *--ptr = '-';
23840Sstevel@tonic-gate return (1);
23850Sstevel@tonic-gate }
23860Sstevel@tonic-gate *--ptr = '-';
23870Sstevel@tonic-gate }
23880Sstevel@tonic-gate }
23890Sstevel@tonic-gate else {
23900Sstevel@tonic-gate #ifdef OTHER_PASSWD
23910Sstevel@tonic-gate pw = bero_getpwnam(ARG[which], _path_passwd);
23920Sstevel@tonic-gate #else
23930Sstevel@tonic-gate pw = getpwnam(ARG[which]);
23940Sstevel@tonic-gate #endif
23950Sstevel@tonic-gate if (pw && (uid == pw->pw_uid))
23960Sstevel@tonic-gate return (1);
23970Sstevel@tonic-gate }
23980Sstevel@tonic-gate }
23990Sstevel@tonic-gate }
24000Sstevel@tonic-gate return (0);
24010Sstevel@tonic-gate }
24020Sstevel@tonic-gate
24030Sstevel@tonic-gate int gid_match(char *keyword, gid_t gid, char *username)
24040Sstevel@tonic-gate {
24050Sstevel@tonic-gate struct aclmember *entry = NULL;
24060Sstevel@tonic-gate int which;
24070Sstevel@tonic-gate char *ptr;
24080Sstevel@tonic-gate struct group *grp;
24090Sstevel@tonic-gate char **member;
24100Sstevel@tonic-gate
24110Sstevel@tonic-gate /*
24120Sstevel@tonic-gate * keyword <gid-range> [<gid-range> ...]
24130Sstevel@tonic-gate *
24140Sstevel@tonic-gate * gid-range may be a groupname or begin with '%' and be treated as numeric:
24150Sstevel@tonic-gate * %<gid> A single GID
24160Sstevel@tonic-gate * %<gid>+ All GIDs greater or equal to GID
24170Sstevel@tonic-gate * %<gid>- All GIDs greater or equal to GID
24180Sstevel@tonic-gate * %-<gid> All GIDs less or equal to GID
24190Sstevel@tonic-gate * %<gid>-<gid> All GIDs between the two (inclusive)
24200Sstevel@tonic-gate * * All GIDs
24210Sstevel@tonic-gate */
24220Sstevel@tonic-gate while (getaclentry(keyword, &entry)) {
24230Sstevel@tonic-gate for (which = 0; (which < MAXARGS) && ARG[which]; which++) {
24240Sstevel@tonic-gate if (!strcmp(ARG[which], "*"))
24250Sstevel@tonic-gate return (1);
24260Sstevel@tonic-gate if (ARG[which][0] == '%') {
24270Sstevel@tonic-gate if ((ptr = strchr(ARG[which] + 1, '-')) == NULL) {
24280Sstevel@tonic-gate if ((ptr = strchr(ARG[which] + 1, '+')) == NULL) {
24290Sstevel@tonic-gate if (gid == strtoul(ARG[which] + 1, NULL, 0))
24300Sstevel@tonic-gate return (1);
24310Sstevel@tonic-gate }
24320Sstevel@tonic-gate else {
24330Sstevel@tonic-gate *ptr++ = '\0';
24340Sstevel@tonic-gate if ((ARG[which][1] == '\0')
24350Sstevel@tonic-gate || (gid >= strtoul(ARG[which] + 1, NULL, 0))) {
24360Sstevel@tonic-gate *--ptr = '+';
24370Sstevel@tonic-gate return (1);
24380Sstevel@tonic-gate }
24390Sstevel@tonic-gate *--ptr = '+';
24400Sstevel@tonic-gate }
24410Sstevel@tonic-gate }
24420Sstevel@tonic-gate else {
24430Sstevel@tonic-gate *ptr++ = '\0';
24440Sstevel@tonic-gate if (((ARG[which][1] == '\0')
24450Sstevel@tonic-gate || (gid >= strtoul(ARG[which] + 1, NULL, 0)))
24460Sstevel@tonic-gate && ((*ptr == '\0')
24470Sstevel@tonic-gate || (gid <= strtoul(ptr, NULL, 0)))) {
24480Sstevel@tonic-gate *--ptr = '-';
24490Sstevel@tonic-gate return (1);
24500Sstevel@tonic-gate }
24510Sstevel@tonic-gate *--ptr = '-';
24520Sstevel@tonic-gate }
24530Sstevel@tonic-gate }
24540Sstevel@tonic-gate else {
24550Sstevel@tonic-gate if ((grp = getgrnam(ARG[which]))) {
24560Sstevel@tonic-gate if (gid == grp->gr_gid)
24570Sstevel@tonic-gate return (1);
24580Sstevel@tonic-gate if (username) {
24590Sstevel@tonic-gate for (member = grp->gr_mem; *member; member++)
24600Sstevel@tonic-gate if (!strcasecmp(*member, username))
24610Sstevel@tonic-gate return (1);
24620Sstevel@tonic-gate }
24630Sstevel@tonic-gate }
24640Sstevel@tonic-gate }
24650Sstevel@tonic-gate }
24660Sstevel@tonic-gate }
24670Sstevel@tonic-gate return (0);
24680Sstevel@tonic-gate }
24690Sstevel@tonic-gate
24700Sstevel@tonic-gate int denieduid(uid_t uid)
24710Sstevel@tonic-gate {
24720Sstevel@tonic-gate return uid_match("deny-uid", uid);
24730Sstevel@tonic-gate }
24740Sstevel@tonic-gate
24750Sstevel@tonic-gate int alloweduid(uid_t uid)
24760Sstevel@tonic-gate {
24770Sstevel@tonic-gate return uid_match("allow-uid", uid);
24780Sstevel@tonic-gate }
24790Sstevel@tonic-gate
24800Sstevel@tonic-gate int deniedgid(gid_t gid)
24810Sstevel@tonic-gate {
24820Sstevel@tonic-gate return gid_match("deny-gid", gid, NULL);
24830Sstevel@tonic-gate }
24840Sstevel@tonic-gate
24850Sstevel@tonic-gate int allowedgid(gid_t gid)
24860Sstevel@tonic-gate {
24870Sstevel@tonic-gate return gid_match("allow-gid", gid, NULL);
24880Sstevel@tonic-gate }
24890Sstevel@tonic-gate
24900Sstevel@tonic-gate /* Terminate login as previous user, if any, resetting state; used when USER
24910Sstevel@tonic-gate * command is given or login fails. */
24920Sstevel@tonic-gate
24930Sstevel@tonic-gate void end_login(void)
24940Sstevel@tonic-gate {
24950Sstevel@tonic-gate delay_signaling(); /* we can't allow any signals while euid==0: kinch */
24960Sstevel@tonic-gate (void) seteuid((uid_t) 0);
24970Sstevel@tonic-gate if (logged_in) {
24980Sstevel@tonic-gate if (wtmp_logging)
24990Sstevel@tonic-gate wu_logwtmp(ttyline, pw->pw_name, remotehost, 0);
25000Sstevel@tonic-gate #ifdef USE_PAM
25010Sstevel@tonic-gate if (!anonymous && pamh) {
25020Sstevel@tonic-gate (void) pam_close_session(pamh, 0);
25030Sstevel@tonic-gate (void) pam_end(pamh, PAM_SUCCESS);
25040Sstevel@tonic-gate pamh = (pam_handle_t *)0;
25050Sstevel@tonic-gate }
25060Sstevel@tonic-gate #endif
25070Sstevel@tonic-gate }
25080Sstevel@tonic-gate pw = NULL;
25090Sstevel@tonic-gate #ifdef AFS_AUTH
25100Sstevel@tonic-gate ktc_ForgetAllTokens();
25110Sstevel@tonic-gate #endif
25120Sstevel@tonic-gate logged_in = 0;
25130Sstevel@tonic-gate anonymous = 0;
25140Sstevel@tonic-gate guest = 0;
25150Sstevel@tonic-gate }
25160Sstevel@tonic-gate
25170Sstevel@tonic-gate int validate_eaddr(char *eaddr)
25180Sstevel@tonic-gate {
25190Sstevel@tonic-gate int i, host, state;
25200Sstevel@tonic-gate
25210Sstevel@tonic-gate for (i = host = state = 0; eaddr[i] != '\0'; i++) {
25220Sstevel@tonic-gate switch (eaddr[i]) {
25230Sstevel@tonic-gate case '.':
25240Sstevel@tonic-gate if (!host)
25250Sstevel@tonic-gate return 0;
25260Sstevel@tonic-gate if (state == 2)
25270Sstevel@tonic-gate state = 3;
25280Sstevel@tonic-gate host = 0;
25290Sstevel@tonic-gate break;
25300Sstevel@tonic-gate case '@':
25310Sstevel@tonic-gate if (!host || state > 1 || !strncasecmp("ftp", eaddr + i - host, host))
25320Sstevel@tonic-gate return 0;
25330Sstevel@tonic-gate state = 2;
25340Sstevel@tonic-gate host = 0;
25350Sstevel@tonic-gate break;
25360Sstevel@tonic-gate case '!':
25370Sstevel@tonic-gate case '%':
25380Sstevel@tonic-gate if (!host || state > 1)
25390Sstevel@tonic-gate return 0;
25400Sstevel@tonic-gate state = 1;
25410Sstevel@tonic-gate host = 0;
25420Sstevel@tonic-gate break;
25430Sstevel@tonic-gate case '-':
25440Sstevel@tonic-gate break;
25450Sstevel@tonic-gate default:
25460Sstevel@tonic-gate host++;
25470Sstevel@tonic-gate }
25480Sstevel@tonic-gate }
25490Sstevel@tonic-gate if (((state == 3) && host > 1) || ((state == 1) && host > 1))
25500Sstevel@tonic-gate return 1;
25510Sstevel@tonic-gate else
25520Sstevel@tonic-gate return 0;
25530Sstevel@tonic-gate }
25540Sstevel@tonic-gate
25550Sstevel@tonic-gate
25560Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
25570Sstevel@tonic-gate static int AllowVirtualUser(const char *username)
25580Sstevel@tonic-gate {
25590Sstevel@tonic-gate struct aclmember *entry = NULL;
25600Sstevel@tonic-gate int which;
25610Sstevel@tonic-gate
25620Sstevel@tonic-gate while (getaclentry("virtual", &entry))
25630Sstevel@tonic-gate if (ARG0 && hostmatch(ARG0, virtual_address, virtual_hostname)
25640Sstevel@tonic-gate && ARG1 && !strcasecmp(ARG1, "allow"))
25650Sstevel@tonic-gate for (which = 2; (which < MAXARGS) && ARG[which]; which++)
25660Sstevel@tonic-gate if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
25670Sstevel@tonic-gate return (1);
25680Sstevel@tonic-gate return (0);
25690Sstevel@tonic-gate }
25700Sstevel@tonic-gate
25710Sstevel@tonic-gate static int DenyVirtualUser(const char *username)
25720Sstevel@tonic-gate {
25730Sstevel@tonic-gate struct aclmember *entry = NULL;
25740Sstevel@tonic-gate int which;
25750Sstevel@tonic-gate
25760Sstevel@tonic-gate while (getaclentry("virtual", &entry))
25770Sstevel@tonic-gate if (ARG0 && hostmatch(ARG0, virtual_address, virtual_hostname)
25780Sstevel@tonic-gate && ARG1 && !strcasecmp(ARG1, "deny"))
25790Sstevel@tonic-gate for (which = 2; (which < MAXARGS) && ARG[which]; which++)
25800Sstevel@tonic-gate if (!strcasecmp(username, ARG[which]) || !strcmp("*", ARG[which]))
25810Sstevel@tonic-gate return (1);
25820Sstevel@tonic-gate return (0);
25830Sstevel@tonic-gate }
25840Sstevel@tonic-gate
25850Sstevel@tonic-gate static int DenyVirtualAnonymous(void)
25860Sstevel@tonic-gate {
25870Sstevel@tonic-gate struct aclmember *entry = NULL;
25880Sstevel@tonic-gate
25890Sstevel@tonic-gate while (getaclentry("virtual", &entry))
25900Sstevel@tonic-gate if (ARG0 && hostmatch(ARG0, virtual_address, virtual_hostname)
25910Sstevel@tonic-gate && ARG1 && !strcasecmp(ARG1, "private"))
25920Sstevel@tonic-gate return (1);
25930Sstevel@tonic-gate return (0);
25940Sstevel@tonic-gate }
25950Sstevel@tonic-gate #endif
25960Sstevel@tonic-gate
25970Sstevel@tonic-gate void pass(char *passwd)
25980Sstevel@tonic-gate {
25990Sstevel@tonic-gate
26000Sstevel@tonic-gate #if !defined(USE_PAM) || (defined(USE_PAM) && defined(OTHER_PASSWD))
26010Sstevel@tonic-gate char *xpasswd, *salt;
26020Sstevel@tonic-gate #endif
26030Sstevel@tonic-gate
26040Sstevel@tonic-gate int passwarn = 0;
26050Sstevel@tonic-gate int rval = 1;
26060Sstevel@tonic-gate int success_code = 230;
26070Sstevel@tonic-gate int cos;
26080Sstevel@tonic-gate
26090Sstevel@tonic-gate #ifdef SECUREOSF
26100Sstevel@tonic-gate struct pr_passwd *pr;
26110Sstevel@tonic-gate int crypt_alg = 0;
26120Sstevel@tonic-gate #endif
26130Sstevel@tonic-gate
26140Sstevel@tonic-gate #ifdef BSD_AUTH
26150Sstevel@tonic-gate extern int ext_auth;
26160Sstevel@tonic-gate extern char *check_auth();
26170Sstevel@tonic-gate #endif
26180Sstevel@tonic-gate
26190Sstevel@tonic-gate #ifdef ULTRIX_AUTH
26200Sstevel@tonic-gate int numfails;
26210Sstevel@tonic-gate #endif /* ULTRIX_AUTH */
26220Sstevel@tonic-gate
26230Sstevel@tonic-gate #ifdef HAS_PW_EXPIRE
26240Sstevel@tonic-gate int set_expired = FALSE;
26250Sstevel@tonic-gate #endif
26260Sstevel@tonic-gate
26270Sstevel@tonic-gate #ifdef AFS_AUTH
26280Sstevel@tonic-gate char *reason;
26290Sstevel@tonic-gate #endif /* AFS_AUTH */
26300Sstevel@tonic-gate
26310Sstevel@tonic-gate #ifdef DCE_AUTH
26320Sstevel@tonic-gate sec_passwd_rec_t pwr;
26330Sstevel@tonic-gate sec_login_handle_t lhdl;
26340Sstevel@tonic-gate boolean32 rstpwd;
26350Sstevel@tonic-gate sec_login_auth_src_t asrc;
26360Sstevel@tonic-gate error_status_t status;
26370Sstevel@tonic-gate #endif /* DCE_AUTH */
26380Sstevel@tonic-gate
26390Sstevel@tonic-gate #if defined(USE_GSS)
26400Sstevel@tonic-gate /*
26410Sstevel@tonic-gate * LOGIC:
26420Sstevel@tonic-gate * If [ the user presented GSSAPI creds and was authorized ]
26430Sstevel@tonic-gate * jump down past the password validation code.
26440Sstevel@tonic-gate */
26450Sstevel@tonic-gate if (GSSUSERAUTH_OK(gss_info) && logged_in) {
26460Sstevel@tonic-gate /*
26470Sstevel@tonic-gate * We could reply(202, "PASS command superfluous.") here, but
26480Sstevel@tonic-gate * allow this for compat with some clients.
26490Sstevel@tonic-gate */
26500Sstevel@tonic-gate success_code = 232;
26510Sstevel@tonic-gate goto pwd_validation_done;
26520Sstevel@tonic-gate }
26530Sstevel@tonic-gate #endif /* defined(USE_GSS) */
26540Sstevel@tonic-gate
26550Sstevel@tonic-gate if (logged_in || askpasswd == 0) {
26560Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
26570Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN REFUSED (PASS before USER) FROM %s",
26580Sstevel@tonic-gate remoteident);
26590Sstevel@tonic-gate #endif
26600Sstevel@tonic-gate reply(503, "Login with USER first.");
26610Sstevel@tonic-gate return;
26620Sstevel@tonic-gate }
26630Sstevel@tonic-gate askpasswd = 0;
26640Sstevel@tonic-gate
26650Sstevel@tonic-gate /* Disable lreply() if the first character of the password is '-' since
26660Sstevel@tonic-gate * some hosts don't understand continuation messages and hang... */
26670Sstevel@tonic-gate
26680Sstevel@tonic-gate if (*passwd == '-')
26690Sstevel@tonic-gate dolreplies = 0;
26700Sstevel@tonic-gate else
26710Sstevel@tonic-gate dolreplies = 1;
26720Sstevel@tonic-gate /* ******** REGULAR/GUEST USER PASSWORD PROCESSING ********** */
26730Sstevel@tonic-gate if (!anonymous) { /* "ftp" is only account allowed no password */
26740Sstevel@tonic-gate #ifndef HELP_CRACKERS
26750Sstevel@tonic-gate if (DenyLoginAfterPassword) {
26760Sstevel@tonic-gate pr_mesg(530, DelayedMessageFile);
26770Sstevel@tonic-gate reply(530, "Login incorrect.");
26780Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
26790Sstevel@tonic-gate audit_ftpd_failure(the_user);
26800Sstevel@tonic-gate #endif
26810Sstevel@tonic-gate acl_remove();
26820Sstevel@tonic-gate pw = NULL;
26830Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
26840Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s",
26850Sstevel@tonic-gate remoteident);
26860Sstevel@tonic-gate exit(0);
26870Sstevel@tonic-gate }
26880Sstevel@tonic-gate return;
26890Sstevel@tonic-gate }
26900Sstevel@tonic-gate #endif
26910Sstevel@tonic-gate if (*passwd == '-')
26920Sstevel@tonic-gate passwd++;
26930Sstevel@tonic-gate #ifdef USE_PAM
26940Sstevel@tonic-gate #ifdef OTHER_PASSWD
26950Sstevel@tonic-gate if (use_pam
26960Sstevel@tonic-gate #if defined(USE_GSS)
26970Sstevel@tonic-gate && !GSSUSERAUTH_OK(gss_info)
26980Sstevel@tonic-gate #endif
26990Sstevel@tonic-gate ) {
27000Sstevel@tonic-gate #endif
27010Sstevel@tonic-gate /* PAM authentication
27020Sstevel@tonic-gate * If PAM authenticates a user we know nothing about on the local
27030Sstevel@tonic-gate * system, use the generic guest account credentials. We should make
27040Sstevel@tonic-gate * this somehow a configurable item somewhere; later more on that.
27050Sstevel@tonic-gate *
27060Sstevel@tonic-gate * For now assume the guest (not anonymous) identity, so the site
27070Sstevel@tonic-gate * admins can still differentiate between the truw anonymous user and
27080Sstevel@tonic-gate * a little bit more special ones. Otherwise he wouldn't go the extra
27090Sstevel@tonic-gate * mile to have a different user database, right?
27100Sstevel@tonic-gate * --gaftonc */
27110Sstevel@tonic-gate if (pam_check_pass(the_user, passwd)) {
27120Sstevel@tonic-gate rval = 0;
27130Sstevel@tonic-gate if (pw == NULL) {
27140Sstevel@tonic-gate /* assume guest account identity */
27150Sstevel@tonic-gate pw = sgetpwnam("ftp");
27160Sstevel@tonic-gate anonymous = 0;
27170Sstevel@tonic-gate guest = 1;
27180Sstevel@tonic-gate /* even go as far as... */
27190Sstevel@tonic-gate if (pw != NULL && pw->pw_name != NULL) {
27200Sstevel@tonic-gate free(pw->pw_name);
27210Sstevel@tonic-gate pw->pw_name = sgetsave(the_user);
27220Sstevel@tonic-gate }
27230Sstevel@tonic-gate }
27240Sstevel@tonic-gate }
27250Sstevel@tonic-gate #ifdef OTHER_PASSWD
27260Sstevel@tonic-gate } else {
27270Sstevel@tonic-gate #endif
27280Sstevel@tonic-gate #endif /* USE_PAM */
27290Sstevel@tonic-gate #if !defined(USE_PAM) || (defined(USE_PAM) && defined(OTHER_PASSWD))
27300Sstevel@tonic-gate #ifdef BSD_AUTH
27310Sstevel@tonic-gate if (ext_auth) {
27320Sstevel@tonic-gate if ((salt = check_auth(the_user, passwd))) {
27330Sstevel@tonic-gate reply(530, "%s", salt);
27340Sstevel@tonic-gate #ifdef LOG_FAILED /* 27-Apr-93 EHK/BM */
27350Sstevel@tonic-gate /*
27360Sstevel@tonic-gate * To avoid logging passwords mistakenly entered as
27370Sstevel@tonic-gate * usernames, only log the names of users which exist.
27380Sstevel@tonic-gate */
27390Sstevel@tonic-gate syslog(LOG_INFO, "failed login from %s, %s", remoteident,
27400Sstevel@tonic-gate (pw == NULL) ? "[unknown]" : the_user);
27410Sstevel@tonic-gate #endif /* LOG_FAILED */
27420Sstevel@tonic-gate acl_remove();
27430Sstevel@tonic-gate pw = NULL;
27440Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
27450Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s",
27460Sstevel@tonic-gate remoteident);
27470Sstevel@tonic-gate exit(0);
27480Sstevel@tonic-gate }
27490Sstevel@tonic-gate return;
27500Sstevel@tonic-gate }
27510Sstevel@tonic-gate }
27520Sstevel@tonic-gate else {
27530Sstevel@tonic-gate #endif /* BSD_AUTH */
27540Sstevel@tonic-gate *guestpw = '\0';
27550Sstevel@tonic-gate if (pw == NULL)
27560Sstevel@tonic-gate salt = "xx";
27570Sstevel@tonic-gate else
27580Sstevel@tonic-gate #ifndef OPIE
27590Sstevel@tonic-gate salt = pw->pw_passwd;
27600Sstevel@tonic-gate #ifdef SECUREOSF
27610Sstevel@tonic-gate if ((pr = getprpwnam(pw->pw_name)) != NULL) {
27620Sstevel@tonic-gate if (pr->uflg.fg_newcrypt)
27630Sstevel@tonic-gate crypt_alg = pr->ufld.fd_newcrypt;
27640Sstevel@tonic-gate else if (pr->sflg.fg_newcrypt)
27650Sstevel@tonic-gate crypt_alg = pr->sfld.fd_newcrypt;
27660Sstevel@tonic-gate else
27670Sstevel@tonic-gate crypt_alg = 0;
27680Sstevel@tonic-gate }
27690Sstevel@tonic-gate else
27700Sstevel@tonic-gate crypt_alg = 0;
27710Sstevel@tonic-gate
27720Sstevel@tonic-gate xpasswd = dispcrypt(passwd, salt, crypt_alg);
27730Sstevel@tonic-gate #elif defined(SecureWare) || defined(HPUX_10_TRUSTED)
27740Sstevel@tonic-gate xpasswd = bigcrypt(passwd, salt);
27750Sstevel@tonic-gate #elif defined(KERBEROS)
27760Sstevel@tonic-gate xpasswd = crypt16(passwd, salt);
27770Sstevel@tonic-gate #elif defined(SKEY)
27780Sstevel@tonic-gate #ifndef __NetBSD__
27790Sstevel@tonic-gate xpasswd = skey_crypt(passwd, salt, pw, pwok);
27800Sstevel@tonic-gate pwok = 0;
27810Sstevel@tonic-gate #else
27820Sstevel@tonic-gate if ((pw != NULL) && (pw->pw_name != NULL) && skey_haskey(pw->pw_name) == 0 &&
27830Sstevel@tonic-gate skey_passcheck(pw->pw_name, passwd) != -1)
27840Sstevel@tonic-gate xpasswd = pw->pw_passwd;
27850Sstevel@tonic-gate else
27860Sstevel@tonic-gate xpasswd = crypt(passwd, salt);
27870Sstevel@tonic-gate #endif
27880Sstevel@tonic-gate #else /* !SKEY */
27890Sstevel@tonic-gate xpasswd = crypt(passwd, salt);
27900Sstevel@tonic-gate #endif /* SKEY */
27910Sstevel@tonic-gate #else /* OPIE */
27920Sstevel@tonic-gate if (!opieverify(&opiestate, passwd))
27930Sstevel@tonic-gate rval = 0;
27940Sstevel@tonic-gate xpasswd = crypt(passwd, pw->pw_passwd);
27950Sstevel@tonic-gate #endif /* OPIE */
27960Sstevel@tonic-gate #ifdef ULTRIX_AUTH
27970Sstevel@tonic-gate if ((numfails = ultrix_check_pass(passwd, xpasswd)) >= 0) {
27980Sstevel@tonic-gate #else
27990Sstevel@tonic-gate if (pw != NULL) {
28000Sstevel@tonic-gate #ifdef AFS_AUTH
28010Sstevel@tonic-gate if (strcmp(pw->pw_passwd, "X") == 0)
28020Sstevel@tonic-gate if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION | KA_USERAUTH_DOSETPAG, pw->pw_name, "", 0, passwd, 0, 0, 0, &reason) == 0)
28030Sstevel@tonic-gate rval = 0;
28040Sstevel@tonic-gate else
28050Sstevel@tonic-gate printf("230-AFS: %s", reason);
28060Sstevel@tonic-gate else
28070Sstevel@tonic-gate #endif /* AFS_AUTH */
28080Sstevel@tonic-gate /* The strcmp does not catch null passwords! */
28090Sstevel@tonic-gate #ifdef HAS_PW_EXPIRE
28100Sstevel@tonic-gate if(pw->pw_expire != NULL) {
28110Sstevel@tonic-gate if(pw->pw_expire && time(NULL) >= pw->pw_expire) {
28120Sstevel@tonic-gate set_expired = TRUE;
28130Sstevel@tonic-gate }
28140Sstevel@tonic-gate }
28150Sstevel@tonic-gate #endif
28160Sstevel@tonic-gate
28170Sstevel@tonic-gate if (*pw->pw_passwd != '\0' &&
28180Sstevel@tonic-gate #ifdef HAS_PW_EXPIRE
28190Sstevel@tonic-gate !set_expired &&
28200Sstevel@tonic-gate #endif
28210Sstevel@tonic-gate strcmp(xpasswd, pw->pw_passwd) == 0) {
28220Sstevel@tonic-gate #endif
28230Sstevel@tonic-gate rval = 0;
28240Sstevel@tonic-gate }
28250Sstevel@tonic-gate #ifdef DCE_AUTH
28260Sstevel@tonic-gate #ifndef ALWAYS_TRY_DCE
28270Sstevel@tonic-gate else
28280Sstevel@tonic-gate #endif /* ALWAYS_TRY_DCE */
28290Sstevel@tonic-gate {
28300Sstevel@tonic-gate sec_login_setup_identity((unsigned_char_p_t) pw->pw_name, sec_login_no_flags, &lhdl, &status);
28310Sstevel@tonic-gate if (status == error_status_ok) {
28320Sstevel@tonic-gate printf("230-sec_login_setup_identity OK\n");
28330Sstevel@tonic-gate pwr.key.tagged_union.plain = (idl_char *) passwd;
28340Sstevel@tonic-gate pwr.key.key_type = sec_passwd_plain;
28350Sstevel@tonic-gate pwr.pepper = 0;
28360Sstevel@tonic-gate pwr.version_number = sec_passwd_c_version_none;
28370Sstevel@tonic-gate /* validate password with login context */
28380Sstevel@tonic-gate sec_login_valid_and_cert_ident(lhdl, &pwr, &rstpwd, &asrc, &status);
28390Sstevel@tonic-gate if (!rstpwd && (asrc == sec_login_auth_src_network) && (status == error_status_ok)) {
28400Sstevel@tonic-gate printf("230-sec_login_valid_and_cert_ident OK\n");
28410Sstevel@tonic-gate sec_login_set_context(lhdl, &status);
28420Sstevel@tonic-gate printf("230-sec_login_set_context finished\n");
28430Sstevel@tonic-gate if (status != error_status_ok) {
28440Sstevel@tonic-gate int pstatus;
28450Sstevel@tonic-gate dce_error_string_t s;
28460Sstevel@tonic-gate printf("230-Error status: %d:\n", status);
28470Sstevel@tonic-gate dce_error_inq_text(status, s, &pstatus);
28480Sstevel@tonic-gate printf("230-%s\n", s);
28490Sstevel@tonic-gate fflush(stderr);
28500Sstevel@tonic-gate sec_login_purge_context(lhdl, &status);
28510Sstevel@tonic-gate }
28520Sstevel@tonic-gate else {
28530Sstevel@tonic-gate /*sec_login_get_pwent(lhdl, &pw, &status); */
28540Sstevel@tonic-gate rval = 0;
28550Sstevel@tonic-gate }
28560Sstevel@tonic-gate }
28570Sstevel@tonic-gate }
28580Sstevel@tonic-gate }
28590Sstevel@tonic-gate #endif /* DCE_AUTH */
28600Sstevel@tonic-gate }
28610Sstevel@tonic-gate #ifdef USE_PAM
28620Sstevel@tonic-gate }
28630Sstevel@tonic-gate #endif
28640Sstevel@tonic-gate #endif /* !USE_PAM || (USE_PAM && OTHER_PASSWD) */
28650Sstevel@tonic-gate if (rval) {
28660Sstevel@tonic-gate reply(530, "Login incorrect.");
28670Sstevel@tonic-gate
28680Sstevel@tonic-gate #ifdef LOG_FAILED /* 27-Apr-93 EHK/BM */
28690Sstevel@tonic-gate /* H* add-on: yell about attempts to use the trojan. This may alarm you
28700Sstevel@tonic-gate if you're "stringsing" the binary and you see "NULL" pop out in just
28710Sstevel@tonic-gate about the same place as it would have in 2.2c! */
28720Sstevel@tonic-gate if (!strcasecmp(passwd, "NULL"))
28730Sstevel@tonic-gate syslog(LOG_NOTICE, "REFUSED \"NULL\" from %s, %s",
28740Sstevel@tonic-gate remoteident, the_user);
28750Sstevel@tonic-gate else {
28760Sstevel@tonic-gate /*
28770Sstevel@tonic-gate * To avoid logging passwords mistakenly entered as
28780Sstevel@tonic-gate * usernames, only log the names of users which exist.
28790Sstevel@tonic-gate */
28800Sstevel@tonic-gate syslog(LOG_INFO, "failed login from %s, %s", remoteident,
28810Sstevel@tonic-gate (pw == NULL) ? "[unknown]" : the_user);
28820Sstevel@tonic-gate }
28830Sstevel@tonic-gate #endif
28840Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
28850Sstevel@tonic-gate audit_ftpd_failure(the_user);
28860Sstevel@tonic-gate #endif
28870Sstevel@tonic-gate acl_remove();
28880Sstevel@tonic-gate
28890Sstevel@tonic-gate pw = NULL;
28900Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
28910Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s",
28920Sstevel@tonic-gate remoteident);
28930Sstevel@tonic-gate exit(0);
28940Sstevel@tonic-gate }
28950Sstevel@tonic-gate return;
28960Sstevel@tonic-gate }
28970Sstevel@tonic-gate #ifdef BSD_AUTH
28980Sstevel@tonic-gate }
28990Sstevel@tonic-gate #endif
29000Sstevel@tonic-gate /* ANONYMOUS USER PROCESSING STARTS HERE */
29010Sstevel@tonic-gate }
29020Sstevel@tonic-gate else {
29030Sstevel@tonic-gate char *pwin, *pwout = guestpw;
29040Sstevel@tonic-gate struct aclmember *entry = NULL;
29050Sstevel@tonic-gate int valid;
29060Sstevel@tonic-gate int enforce = 0;
29070Sstevel@tonic-gate
29080Sstevel@tonic-gate if (getaclentry("passwd-check", &entry) &&
29090Sstevel@tonic-gate ARG0 && strcasecmp(ARG0, "none")) {
29100Sstevel@tonic-gate
29110Sstevel@tonic-gate if (!strcasecmp(ARG0, "rfc822"))
29120Sstevel@tonic-gate valid = validate_eaddr(passwd);
29130Sstevel@tonic-gate else if (!strcasecmp(ARG0, "trivial"))
29140Sstevel@tonic-gate valid = (strchr(passwd, '@') == NULL) ? 0 : 1;
29150Sstevel@tonic-gate else
29160Sstevel@tonic-gate valid = 1;
29170Sstevel@tonic-gate if (ARG1 && !strcasecmp(ARG1, "enforce"))
29180Sstevel@tonic-gate enforce = 1;
29190Sstevel@tonic-gate /* Block off "default" responses like mozilla@ and IE30User@
29200Sstevel@tonic-gate * (at the administrator's discretion). --AC
29210Sstevel@tonic-gate */
29220Sstevel@tonic-gate entry = NULL;
29230Sstevel@tonic-gate while (getaclentry("deny-email", &entry)) {
29240Sstevel@tonic-gate if (ARG0
29250Sstevel@tonic-gate && ((strcasecmp(passwd, ARG0) == 0)
29260Sstevel@tonic-gate || regexmatch(passwd, ARG0)
29270Sstevel@tonic-gate || ((*passwd == '-')
29280Sstevel@tonic-gate && ((strcasecmp(passwd + 1, ARG0) == 0)
29290Sstevel@tonic-gate || regexmatch(passwd + 1, ARG0))))) {
29300Sstevel@tonic-gate valid = 0;
29310Sstevel@tonic-gate break;
29320Sstevel@tonic-gate }
29330Sstevel@tonic-gate }
29340Sstevel@tonic-gate if (!valid && enforce) {
29350Sstevel@tonic-gate lreply(530, "The response '%s' is not valid", passwd);
29360Sstevel@tonic-gate lreply(530, "Please use your e-mail address as your password");
29370Sstevel@tonic-gate lreply(530, " for example: %s@%s%s",
29380Sstevel@tonic-gate authenticated ? authuser : "joe", remotehost,
29390Sstevel@tonic-gate strchr(remotehost, '.') ? "" : ".network");
29400Sstevel@tonic-gate reply(530, "Login incorrect.");
29410Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
29420Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP ACCESS REFUSED (anonymous password not rfc822) from %s",
29430Sstevel@tonic-gate remoteident);
29440Sstevel@tonic-gate #endif
29450Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
29460Sstevel@tonic-gate audit_ftpd_bad_pw(the_user);
29470Sstevel@tonic-gate #endif
29480Sstevel@tonic-gate acl_remove();
29490Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
29500Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s",
29510Sstevel@tonic-gate remoteident);
29520Sstevel@tonic-gate exit(0);
29530Sstevel@tonic-gate }
29540Sstevel@tonic-gate return;
29550Sstevel@tonic-gate }
29560Sstevel@tonic-gate else if (!valid)
29570Sstevel@tonic-gate passwarn = 1;
29580Sstevel@tonic-gate }
29590Sstevel@tonic-gate if (!*passwd) {
29600Sstevel@tonic-gate strcpy(guestpw, "[none_given]");
29610Sstevel@tonic-gate }
29620Sstevel@tonic-gate else {
29630Sstevel@tonic-gate int cnt = sizeof(guestpw) - 2;
29640Sstevel@tonic-gate
29650Sstevel@tonic-gate for (pwin = passwd; *pwin && cnt--; pwin++)
29660Sstevel@tonic-gate if (!isgraph(*pwin))
29670Sstevel@tonic-gate *pwout++ = '_';
29680Sstevel@tonic-gate else
29690Sstevel@tonic-gate *pwout++ = *pwin;
29700Sstevel@tonic-gate }
29710Sstevel@tonic-gate #ifndef HELP_CRACKERS
29720Sstevel@tonic-gate if (DenyLoginAfterPassword) {
29730Sstevel@tonic-gate pr_mesg(530, DelayedMessageFile);
29740Sstevel@tonic-gate reply(530, "Login incorrect.");
29750Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
29760Sstevel@tonic-gate audit_ftpd_failure(the_user);
29770Sstevel@tonic-gate #endif
29780Sstevel@tonic-gate acl_remove();
29790Sstevel@tonic-gate pw = NULL;
29800Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
29810Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s",
29820Sstevel@tonic-gate remoteident);
29830Sstevel@tonic-gate exit(0);
29840Sstevel@tonic-gate }
29850Sstevel@tonic-gate return;
29860Sstevel@tonic-gate }
29870Sstevel@tonic-gate #endif
29880Sstevel@tonic-gate }
29890Sstevel@tonic-gate
29900Sstevel@tonic-gate #if defined(USE_GSS)
29910Sstevel@tonic-gate pwd_validation_done:
29920Sstevel@tonic-gate #endif /* USE_GSS */
29930Sstevel@tonic-gate /* if logging is enabled, open logfile before chroot or set group ID */
29940Sstevel@tonic-gate if ((log_outbound_xfers || log_incoming_xfers) && (syslogmsg != 1)) {
29950Sstevel@tonic-gate mode_t oldmask;
29960Sstevel@tonic-gate oldmask = umask(0);
29970Sstevel@tonic-gate xferlog = open(logfile, O_WRONLY | O_APPEND | O_CREAT, 0640);
29980Sstevel@tonic-gate (void) umask(oldmask);
29990Sstevel@tonic-gate if (xferlog < 0) {
30000Sstevel@tonic-gate syslog(LOG_ERR, "cannot open logfile %s: %s", logfile,
30010Sstevel@tonic-gate strerror(errno));
30020Sstevel@tonic-gate xferlog = 0;
30030Sstevel@tonic-gate }
30040Sstevel@tonic-gate }
30050Sstevel@tonic-gate
30060Sstevel@tonic-gate #ifdef DEBUG
30070Sstevel@tonic-gate /* I had a lot of trouble getting xferlog working, because of two factors:
30080Sstevel@tonic-gate acl_setfunctions making stupid assumptions, and sprintf LOSING. _H */
30090Sstevel@tonic-gate /*
30100Sstevel@tonic-gate * Actually, sprintf was not losing, but the rules changed... next release
30110Sstevel@tonic-gate * this will be fixed the correct way, but right now, it works well enough
30120Sstevel@tonic-gate * -- sob
30130Sstevel@tonic-gate */
30140Sstevel@tonic-gate syslog(LOG_INFO, "-i %d,-o %d,xferlog %s: %d",
30150Sstevel@tonic-gate log_incoming_xfers, log_outbound_xfers, logfile, xferlog);
30160Sstevel@tonic-gate #endif
30170Sstevel@tonic-gate enable_signaling(); /* we can allow signals once again: kinch */
30180Sstevel@tonic-gate /* if autogroup command applies to user's class change pw->pw_gid */
30190Sstevel@tonic-gate if (anonymous && use_accessfile) { /* see above. _H */
30200Sstevel@tonic-gate (void) acl_autogroup(pw);
30210Sstevel@tonic-gate guest = acl_guestgroup(pw); /* the new group may be a guest */
30220Sstevel@tonic-gate if (guest && acl_realgroup(pw))
30230Sstevel@tonic-gate guest = 0;
30240Sstevel@tonic-gate anonymous = !guest;
30250Sstevel@tonic-gate }
30260Sstevel@tonic-gate /* END AUTHENTICATION */
30270Sstevel@tonic-gate
30280Sstevel@tonic-gate /* SET GROUP ID STARTS HERE */
30290Sstevel@tonic-gate #ifndef AIX
30300Sstevel@tonic-gate (void) setegid((gid_t) pw->pw_gid);
30310Sstevel@tonic-gate #else
30320Sstevel@tonic-gate (void) setgid((gid_t) pw->pw_gid);
30330Sstevel@tonic-gate #endif
30340Sstevel@tonic-gate (void) initgroups(pw->pw_name, pw->pw_gid);
30350Sstevel@tonic-gate #ifdef DEBUG
30360Sstevel@tonic-gate syslog(LOG_DEBUG, "initgroups has been called");
30370Sstevel@tonic-gate #endif
30380Sstevel@tonic-gate /* WTMP PROCESSING STARTS HERE */
30390Sstevel@tonic-gate if (wtmp_logging) {
30400Sstevel@tonic-gate /* open wtmp before chroot */
30410Sstevel@tonic-gate #if ((defined(BSD) && (BSD >= 199103)) || defined(sun))
30420Sstevel@tonic-gate (void) sprintf(ttyline, "ftp%ld", (long) getpid());
30430Sstevel@tonic-gate #else
30440Sstevel@tonic-gate (void) sprintf(ttyline, "ftpd%d", getpid());
30450Sstevel@tonic-gate #endif
30460Sstevel@tonic-gate #ifdef DEBUG
30470Sstevel@tonic-gate syslog(LOG_DEBUG, "about to call wtmp");
30480Sstevel@tonic-gate #endif
30490Sstevel@tonic-gate wu_logwtmp(ttyline, pw->pw_name, remotehost, 1);
30500Sstevel@tonic-gate }
30510Sstevel@tonic-gate logged_in = 1;
30520Sstevel@tonic-gate
30530Sstevel@tonic-gate expand_id();
30540Sstevel@tonic-gate
30550Sstevel@tonic-gate #ifdef QUOTA
30560Sstevel@tonic-gate memset("a, 0, sizeof(quota));
30570Sstevel@tonic-gate get_quota(pw->pw_dir, pw->pw_uid);
30580Sstevel@tonic-gate #endif
30590Sstevel@tonic-gate
30600Sstevel@tonic-gate restricted_user = 0;
30610Sstevel@tonic-gate if (!anonymous)
30620Sstevel@tonic-gate if ((restricteduid(pw->pw_uid) && !unrestricteduid(pw->pw_uid))
30630Sstevel@tonic-gate || (restrictedgid(pw->pw_gid) && !unrestrictedgid(pw->pw_gid)))
30640Sstevel@tonic-gate restricted_user = 1;
30650Sstevel@tonic-gate if (anonymous || guest) {
30660Sstevel@tonic-gate char *sp;
30670Sstevel@tonic-gate /* We MUST do a chdir() after the chroot. Otherwise the old current
30680Sstevel@tonic-gate * directory will be accessible as "." outside the new root! */
30690Sstevel@tonic-gate #ifdef ALTERNATE_CD
30700Sstevel@tonic-gate home = defhome;
30710Sstevel@tonic-gate #endif
30720Sstevel@tonic-gate #ifdef VIRTUAL
30730Sstevel@tonic-gate if (virtual_mode && !guest) {
30740Sstevel@tonic-gate #ifdef CLOSED_VIRTUAL_SERVER
30750Sstevel@tonic-gate if (DenyVirtualAnonymous()) {
30760Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
30770Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host anonymous access denied) for %s",
30780Sstevel@tonic-gate remoteident);
30790Sstevel@tonic-gate #endif
30800Sstevel@tonic-gate reply(530, "Login incorrect.");
30810Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
30820Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
30830Sstevel@tonic-gate dologout(0);
30840Sstevel@tonic-gate }
30850Sstevel@tonic-gate goto bad;
30860Sstevel@tonic-gate }
30870Sstevel@tonic-gate #endif
30880Sstevel@tonic-gate /* Anonymous user in virtual_mode */
30890Sstevel@tonic-gate if (pw->pw_dir)
30900Sstevel@tonic-gate free(pw->pw_dir);
30910Sstevel@tonic-gate pw->pw_dir = sgetsave(virtual_root);
30920Sstevel@tonic-gate }
30930Sstevel@tonic-gate else
30940Sstevel@tonic-gate #endif
30950Sstevel@tonic-gate
30960Sstevel@tonic-gate /*
30970Sstevel@tonic-gate * New chroot logic.
30980Sstevel@tonic-gate *
30990Sstevel@tonic-gate * If VIRTUAL is supported, the chroot for anonymous users on the
31000Sstevel@tonic-gate * virtual host has already been determined. Otherwise the logic
31010Sstevel@tonic-gate * below applies:
31020Sstevel@tonic-gate *
31030Sstevel@tonic-gate * If this is an anonymous user, the chroot directory is determined
31040Sstevel@tonic-gate * by the "anonymous-root" clause and the home directory is taken
31050Sstevel@tonic-gate * from the etc/passwd file found after chroot'ing.
31060Sstevel@tonic-gate *
31070Sstevel@tonic-gate * If this a guest user, the chroot directory is determined by the
31080Sstevel@tonic-gate * "guest-root" clause and the home directory is taken from the
31090Sstevel@tonic-gate * etc/passwd file found after chroot'ing.
31100Sstevel@tonic-gate *
31110Sstevel@tonic-gate * The effect of this logic is that the entire chroot environment
31120Sstevel@tonic-gate * is under the control of the ftpaccess file and the supporting
31130Sstevel@tonic-gate * files in the ftp environment. The system-wide passwd file is
31140Sstevel@tonic-gate * used only to authenticate the user.
31150Sstevel@tonic-gate */
31160Sstevel@tonic-gate
31170Sstevel@tonic-gate {
31180Sstevel@tonic-gate struct aclmember *entry = NULL;
31190Sstevel@tonic-gate char *root_path = NULL;
31200Sstevel@tonic-gate
31210Sstevel@tonic-gate if (anonymous) {
31220Sstevel@tonic-gate char class[BUFSIZ];
31230Sstevel@tonic-gate
31240Sstevel@tonic-gate (void) acl_getclass(class);
31250Sstevel@tonic-gate while (getaclentry("anonymous-root", &entry) && ARG0) {
31260Sstevel@tonic-gate if (!ARG1) {
31270Sstevel@tonic-gate if (!root_path)
31280Sstevel@tonic-gate root_path = ARG0;
31290Sstevel@tonic-gate }
31300Sstevel@tonic-gate else {
31310Sstevel@tonic-gate int which;
31320Sstevel@tonic-gate
31330Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
31340Sstevel@tonic-gate if (!strcmp(ARG[which], "*")) {
31350Sstevel@tonic-gate if (!root_path)
31360Sstevel@tonic-gate root_path = ARG0;
31370Sstevel@tonic-gate }
31380Sstevel@tonic-gate else {
31390Sstevel@tonic-gate if (!strcasecmp(ARG[which], class))
31400Sstevel@tonic-gate root_path = ARG0;
31410Sstevel@tonic-gate }
31420Sstevel@tonic-gate }
31430Sstevel@tonic-gate }
31440Sstevel@tonic-gate }
31450Sstevel@tonic-gate }
31460Sstevel@tonic-gate else { /* (guest) */
31470Sstevel@tonic-gate while (getaclentry("guest-root", &entry) && ARG0) {
31480Sstevel@tonic-gate if (!ARG1) {
31490Sstevel@tonic-gate if (!root_path)
31500Sstevel@tonic-gate root_path = ARG0;
31510Sstevel@tonic-gate }
31520Sstevel@tonic-gate else {
31530Sstevel@tonic-gate int which;
31540Sstevel@tonic-gate char *ptr;
31550Sstevel@tonic-gate
31560Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && ARG[which]; which++) {
31570Sstevel@tonic-gate if (!strcmp(ARG[which], "*")) {
31580Sstevel@tonic-gate if (!root_path)
31590Sstevel@tonic-gate root_path = ARG0;
31600Sstevel@tonic-gate }
31610Sstevel@tonic-gate else {
31620Sstevel@tonic-gate if (ARG[which][0] == '%') {
31630Sstevel@tonic-gate if ((ptr = strchr(ARG[which] + 1, '-')) == NULL) {
31640Sstevel@tonic-gate if ((ptr = strchr(ARG[which] + 1, '+')) == NULL) {
31650Sstevel@tonic-gate if (pw->pw_uid == strtoul(ARG[which] + 1, NULL, 0))
31660Sstevel@tonic-gate root_path = ARG0;
31670Sstevel@tonic-gate }
31680Sstevel@tonic-gate else {
31690Sstevel@tonic-gate *ptr++ = '\0';
31700Sstevel@tonic-gate if ((ARG[which][1] == '\0')
31710Sstevel@tonic-gate || (pw->pw_uid >= strtoul(ARG[which] + 1, NULL, 0)))
31720Sstevel@tonic-gate root_path = ARG0;
31730Sstevel@tonic-gate *--ptr = '+';
31740Sstevel@tonic-gate }
31750Sstevel@tonic-gate }
31760Sstevel@tonic-gate else {
31770Sstevel@tonic-gate *ptr++ = '\0';
31780Sstevel@tonic-gate if (((ARG[which][1] == '\0')
31790Sstevel@tonic-gate || (pw->pw_uid >= strtoul(ARG[which] + 1, NULL, 0)))
31800Sstevel@tonic-gate && ((*ptr == '\0')
31810Sstevel@tonic-gate || (pw->pw_uid <= strtoul(ptr, NULL, 0))))
31820Sstevel@tonic-gate root_path = ARG0;
31830Sstevel@tonic-gate *--ptr = '-';
31840Sstevel@tonic-gate }
31850Sstevel@tonic-gate }
31860Sstevel@tonic-gate else {
31870Sstevel@tonic-gate #ifdef OTHER_PASSWD
31880Sstevel@tonic-gate struct passwd *guest_pw = bero_getpwnam(ARG[which], _path_passwd);
31890Sstevel@tonic-gate #else
31900Sstevel@tonic-gate struct passwd *guest_pw = getpwnam(ARG[which]);
31910Sstevel@tonic-gate #endif
31920Sstevel@tonic-gate if (guest_pw && (pw->pw_uid == guest_pw->pw_uid))
31930Sstevel@tonic-gate root_path = ARG0;
31940Sstevel@tonic-gate }
31950Sstevel@tonic-gate }
31960Sstevel@tonic-gate }
31970Sstevel@tonic-gate }
31980Sstevel@tonic-gate }
31990Sstevel@tonic-gate }
32000Sstevel@tonic-gate
32010Sstevel@tonic-gate if (root_path) {
32020Sstevel@tonic-gate struct passwd *chroot_pw = NULL;
32030Sstevel@tonic-gate
32040Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
32050Sstevel@tonic-gate if (virtual_mode && strcmp(root_path, virtual_root) && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
32060Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
32070Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
32080Sstevel@tonic-gate remoteident, pw->pw_name);
32090Sstevel@tonic-gate #endif
32100Sstevel@tonic-gate reply(530, "Login incorrect.");
32110Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
32120Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
32130Sstevel@tonic-gate dologout(0);
32140Sstevel@tonic-gate }
32150Sstevel@tonic-gate goto bad;
32160Sstevel@tonic-gate }
32170Sstevel@tonic-gate #endif
32180Sstevel@tonic-gate (void) strncpy(chroot_path, root_path, sizeof(chroot_path));
32190Sstevel@tonic-gate chroot_path[sizeof(chroot_path) - 1] = '\0';
32200Sstevel@tonic-gate if (pw->pw_dir)
32210Sstevel@tonic-gate free(pw->pw_dir);
32220Sstevel@tonic-gate pw->pw_dir = sgetsave(chroot_path);
32233232Sas198278 #if defined(SOLARIS_2)
32243232Sas198278 cleanup_nscd();
32253232Sas198278 #endif
32260Sstevel@tonic-gate if (chroot(root_path) < 0 || chdir("/") < 0) {
32270Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
32280Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set guest privileges) for %s, %s",
32290Sstevel@tonic-gate remoteident, pw->pw_name);
32300Sstevel@tonic-gate #endif
32310Sstevel@tonic-gate reply(530, "Can't set guest privileges.");
32320Sstevel@tonic-gate goto bad;
32330Sstevel@tonic-gate }
32340Sstevel@tonic-gate #ifdef OTHER_PASSWD
32350Sstevel@tonic-gate if ((chroot_pw = bero_getpwuid(pw->pw_uid, _path_passwd)) != NULL)
32360Sstevel@tonic-gate #else
32370Sstevel@tonic-gate if ((chroot_pw = getpwuid(pw->pw_uid)) != NULL)
32380Sstevel@tonic-gate #endif
32390Sstevel@tonic-gate if (chdir(chroot_pw->pw_dir) >= 0)
32400Sstevel@tonic-gate home = sgetsave(chroot_pw->pw_dir);
32410Sstevel@tonic-gate goto slimy_hack; /* onea these days I'll make this structured code, honest ... */
32420Sstevel@tonic-gate }
32430Sstevel@tonic-gate }
32440Sstevel@tonic-gate
32450Sstevel@tonic-gate /* determine root and home directory */
32460Sstevel@tonic-gate
32470Sstevel@tonic-gate if ((sp = strstr(pw->pw_dir, "/./")) == NULL) {
32480Sstevel@tonic-gate (void) strncpy(chroot_path, pw->pw_dir, sizeof(chroot_path));
32490Sstevel@tonic-gate chroot_path[sizeof(chroot_path) - 1] = '\0';
32500Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
32510Sstevel@tonic-gate if (virtual_mode && strcmp(chroot_path, virtual_root) && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
32520Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
32530Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
32540Sstevel@tonic-gate remoteident, pw->pw_name);
32550Sstevel@tonic-gate #endif
32560Sstevel@tonic-gate reply(530, "Login incorrect.");
32570Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
32580Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
32590Sstevel@tonic-gate dologout(0);
32600Sstevel@tonic-gate }
32610Sstevel@tonic-gate goto bad;
32620Sstevel@tonic-gate }
32630Sstevel@tonic-gate #endif
32643232Sas198278 #if defined(SOLARIS_2)
32653232Sas198278 cleanup_nscd();
32663232Sas198278 #endif
32670Sstevel@tonic-gate if (chroot(pw->pw_dir) < 0 || chdir("/") < 0) {
32680Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
32690Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set guest privileges) for %s, %s",
32700Sstevel@tonic-gate remoteident, pw->pw_name);
32710Sstevel@tonic-gate #endif
32720Sstevel@tonic-gate reply(530, "Can't set guest privileges.");
32730Sstevel@tonic-gate goto bad;
32740Sstevel@tonic-gate }
32750Sstevel@tonic-gate }
32760Sstevel@tonic-gate else {
32770Sstevel@tonic-gate *sp++ = '\0';
32780Sstevel@tonic-gate (void) strncpy(chroot_path, pw->pw_dir, sizeof(chroot_path));
32790Sstevel@tonic-gate chroot_path[sizeof(chroot_path) - 1] = '\0';
32800Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
32810Sstevel@tonic-gate if (virtual_mode && strcmp(chroot_path, virtual_root) && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
32820Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
32830Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
32840Sstevel@tonic-gate remoteident, pw->pw_name);
32850Sstevel@tonic-gate #endif
32860Sstevel@tonic-gate reply(530, "Login incorrect.");
32870Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
32880Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
32890Sstevel@tonic-gate dologout(0);
32900Sstevel@tonic-gate }
32910Sstevel@tonic-gate goto bad;
32920Sstevel@tonic-gate }
32930Sstevel@tonic-gate #endif
32943232Sas198278 #if defined(SOLARIS_2)
32953232Sas198278 cleanup_nscd();
32963232Sas198278 #endif
32970Sstevel@tonic-gate if (chroot(pw->pw_dir) < 0 || chdir(++sp) < 0) {
32980Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
32990Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set guest privileges) for %s, %s",
33000Sstevel@tonic-gate remoteident, pw->pw_name);
33010Sstevel@tonic-gate #endif
33020Sstevel@tonic-gate reply(550, "Can't set guest privileges.");
33030Sstevel@tonic-gate goto bad;
33040Sstevel@tonic-gate }
33050Sstevel@tonic-gate #ifdef ALTERNATE_CD
33060Sstevel@tonic-gate home = sp;
33070Sstevel@tonic-gate #endif
33080Sstevel@tonic-gate }
33090Sstevel@tonic-gate slimy_hack:
33100Sstevel@tonic-gate /* shut up you stupid compiler! */ {
33110Sstevel@tonic-gate int i = 0;
33120Sstevel@tonic-gate i++;
33130Sstevel@tonic-gate }
33140Sstevel@tonic-gate }
33150Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CLOSED_VIRTUAL_SERVER)
33160Sstevel@tonic-gate else if (virtual_mode && !(AllowVirtualUser(pw->pw_name) && !DenyVirtualUser(pw->pw_name))) {
33170Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
33180Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (virtual host access denied) for %s, %s",
33190Sstevel@tonic-gate remoteident, pw->pw_name);
33200Sstevel@tonic-gate #endif
33210Sstevel@tonic-gate reply(530, "Login incorrect.");
33220Sstevel@tonic-gate if (++login_attempts >= lgi_failure_threshold) {
33230Sstevel@tonic-gate syslog(LOG_NOTICE, "repeated login failures from %s", remoteident);
33240Sstevel@tonic-gate dologout(0);
33250Sstevel@tonic-gate }
33260Sstevel@tonic-gate goto bad;
33270Sstevel@tonic-gate }
33280Sstevel@tonic-gate #endif
33290Sstevel@tonic-gate #ifdef AIX
33300Sstevel@tonic-gate {
33310Sstevel@tonic-gate /* AIX 3 lossage. Don't ask. It's undocumented. */
33320Sstevel@tonic-gate priv_t priv;
33330Sstevel@tonic-gate
33340Sstevel@tonic-gate priv.pv_priv[0] = 0;
33350Sstevel@tonic-gate priv.pv_priv[1] = 0;
33360Sstevel@tonic-gate /* setgroups(NULL, NULL); */
33370Sstevel@tonic-gate if (setpriv(PRIV_SET | PRIV_INHERITED | PRIV_EFFECTIVE | PRIV_BEQUEATH,
33380Sstevel@tonic-gate &priv, sizeof(priv_t)) < 0 ||
33390Sstevel@tonic-gate setuidx(ID_REAL | ID_EFFECTIVE, (uid_t) pw->pw_uid) < 0 ||
33400Sstevel@tonic-gate seteuid((uid_t) pw->pw_uid) < 0) {
33410Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
33420Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set uid) for %s, %s",
33430Sstevel@tonic-gate remoteident, pw->pw_name);
33440Sstevel@tonic-gate #endif
33450Sstevel@tonic-gate reply(530, "Can't set uid (AIX3).");
33460Sstevel@tonic-gate goto bad;
33470Sstevel@tonic-gate }
33480Sstevel@tonic-gate }
33490Sstevel@tonic-gate #ifdef UID_DEBUG
33500Sstevel@tonic-gate lreply(success_code, "ruid=%d, euid=%d, suid=%d, luid=%d", getuidx(ID_REAL),
33510Sstevel@tonic-gate getuidx(ID_EFFECTIVE), getuidx(ID_SAVED), getuidx(ID_LOGIN));
33520Sstevel@tonic-gate lreply(success_code, "rgid=%d, egid=%d, sgid=%d, lgid=%d", getgidx(ID_REAL),
33530Sstevel@tonic-gate getgidx(ID_EFFECTIVE), getgidx(ID_SAVED), getgidx(ID_LOGIN));
33540Sstevel@tonic-gate #endif
33550Sstevel@tonic-gate #else /* AIX */
33560Sstevel@tonic-gate #ifdef HAVE_SETREUID
33570Sstevel@tonic-gate if (setreuid(-1, (uid_t) pw->pw_uid) < 0) {
33580Sstevel@tonic-gate #else
33590Sstevel@tonic-gate if (seteuid((uid_t) pw->pw_uid) < 0) {
33600Sstevel@tonic-gate #endif
33610Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
33620Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot set uid) for %s, %s",
33630Sstevel@tonic-gate remoteident, pw->pw_name);
33640Sstevel@tonic-gate #endif
33650Sstevel@tonic-gate reply(530, "Can't set uid.");
33660Sstevel@tonic-gate goto bad;
33670Sstevel@tonic-gate }
33680Sstevel@tonic-gate #endif /* AIX */
33690Sstevel@tonic-gate if (!anonymous && !guest) {
33700Sstevel@tonic-gate if (chdir(pw->pw_dir) < 0) {
33710Sstevel@tonic-gate #ifdef PARANOID
33720Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
33730Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot chdir) for %s, %s",
33740Sstevel@tonic-gate remoteident, pw->pw_name);
33750Sstevel@tonic-gate #endif
33760Sstevel@tonic-gate reply(530, "User %s: can't change directory to %s.",
33770Sstevel@tonic-gate pw->pw_name, pw->pw_dir);
33780Sstevel@tonic-gate goto bad;
33790Sstevel@tonic-gate #else /* PARANOID */
33800Sstevel@tonic-gate if (restricted_user || chdir("/") < 0) {
33810Sstevel@tonic-gate #ifdef VERBOSE_ERROR_LOGING
33820Sstevel@tonic-gate syslog(LOG_NOTICE, "FTP LOGIN FAILED (cannot chdir) for %s, %s",
33830Sstevel@tonic-gate remoteident, pw->pw_name);
33840Sstevel@tonic-gate #endif
33850Sstevel@tonic-gate reply(530, "User %s: can't change directory to %s.",
33860Sstevel@tonic-gate pw->pw_name, pw->pw_dir);
33870Sstevel@tonic-gate goto bad;
33880Sstevel@tonic-gate }
33890Sstevel@tonic-gate else {
33900Sstevel@tonic-gate lreply(success_code, "No directory! Logging in with home=/");
33910Sstevel@tonic-gate #ifdef ALTERNATE_CD
33920Sstevel@tonic-gate home = defhome;
33930Sstevel@tonic-gate #endif
33940Sstevel@tonic-gate }
33950Sstevel@tonic-gate #endif /* PARANOID */
33960Sstevel@tonic-gate }
33970Sstevel@tonic-gate }
33980Sstevel@tonic-gate
33990Sstevel@tonic-gate if (passwarn) {
34000Sstevel@tonic-gate lreply(success_code, "The response '%s' is not valid", passwd);
34010Sstevel@tonic-gate lreply(success_code,
34020Sstevel@tonic-gate "Next time please use your e-mail address as your password");
34030Sstevel@tonic-gate lreply(success_code, " for example: %s@%s%s",
34040Sstevel@tonic-gate authenticated ? authuser : "joe", remotehost,
34050Sstevel@tonic-gate strchr(remotehost, '.') ? "" : ".network");
34060Sstevel@tonic-gate }
34070Sstevel@tonic-gate
34080Sstevel@tonic-gate login_attempts = 0; /* this time successful */
34090Sstevel@tonic-gate
34100Sstevel@tonic-gate /* following two lines were inside the next scope... */
34110Sstevel@tonic-gate
34120Sstevel@tonic-gate show_message(success_code, LOG_IN);
34130Sstevel@tonic-gate show_message(success_code, C_WD);
34140Sstevel@tonic-gate show_readme(success_code, LOG_IN);
34150Sstevel@tonic-gate show_readme(success_code, C_WD);
34160Sstevel@tonic-gate
34170Sstevel@tonic-gate #ifdef ULTRIX_AUTH
34180Sstevel@tonic-gate if (!anonymous && numfails > 0) {
34190Sstevel@tonic-gate lreply(success_code,
34200Sstevel@tonic-gate "There have been %d unsuccessful login attempts on your account",
34210Sstevel@tonic-gate numfails);
34220Sstevel@tonic-gate }
34230Sstevel@tonic-gate #endif /* ULTRIX_AUTH */
34240Sstevel@tonic-gate
34250Sstevel@tonic-gate (void) is_shutdown(0, 0); /* display any shutdown messages now */
34260Sstevel@tonic-gate
34270Sstevel@tonic-gate if (anonymous) {
34280Sstevel@tonic-gate
34290Sstevel@tonic-gate reply(success_code, "Guest login ok, access restrictions apply.");
34300Sstevel@tonic-gate sprintf(proctitle, "%s: anonymous/%.*s", remotehost,
34310Sstevel@tonic-gate (int) (sizeof(proctitle) - sizeof(remotehost) -
34320Sstevel@tonic-gate sizeof(": anonymous/")), passwd);
34330Sstevel@tonic-gate setproctitle("%s", proctitle);
34340Sstevel@tonic-gate if (logging)
34350Sstevel@tonic-gate syslog(LOG_INFO, "ANONYMOUS FTP LOGIN FROM %s, %s",
34360Sstevel@tonic-gate remoteident, passwd);
34370Sstevel@tonic-gate }
34380Sstevel@tonic-gate else {
34390Sstevel@tonic-gate reply(success_code, "User %s logged in.%s", pw->pw_name, guest ?
34400Sstevel@tonic-gate " Access restrictions apply." : "");
34410Sstevel@tonic-gate sprintf(proctitle, "%s: %s", remotehost, pw->pw_name);
34420Sstevel@tonic-gate setproctitle("%s", proctitle);
34430Sstevel@tonic-gate if (logging)
34440Sstevel@tonic-gate syslog(LOG_INFO, "FTP LOGIN FROM %s, %s", remoteident, pw->pw_name);
34450Sstevel@tonic-gate /* H* mod: if non-anonymous user, copy it to "authuser" so everyone can
34460Sstevel@tonic-gate see it, since whoever he was @foreign-host is now largely irrelevant.
34470Sstevel@tonic-gate NMM mod: no, it isn't! Think about accounting for the transfers from or
34480Sstevel@tonic-gate to a shared account. */
34490Sstevel@tonic-gate /* strcpy (authuser, pw->pw_name); */
34500Sstevel@tonic-gate } /* anonymous */
34510Sstevel@tonic-gate #ifdef ALTERNATE_CD
34520Sstevel@tonic-gate if (!home)
34530Sstevel@tonic-gate #endif
34540Sstevel@tonic-gate home = pw->pw_dir; /* home dir for globbing */
34550Sstevel@tonic-gate (void) umask(defumask);
34560Sstevel@tonic-gate time(&login_time);
34570Sstevel@tonic-gate {
34580Sstevel@tonic-gate struct aclmember *entry;
34590Sstevel@tonic-gate entry = NULL;
34600Sstevel@tonic-gate while (getaclentry("limit-time", &entry) && ARG0 && ARG1)
34610Sstevel@tonic-gate if ((anonymous && strcasecmp(ARG0, "anonymous") == 0)
34620Sstevel@tonic-gate || (guest && strcasecmp(ARG0, "guest") == 0)
34630Sstevel@tonic-gate || ((guest | anonymous) && strcmp(ARG0, "*") == 0))
34640Sstevel@tonic-gate limit_time = strtoul(ARG1, NULL, 0);
34650Sstevel@tonic-gate }
34660Sstevel@tonic-gate
34670Sstevel@tonic-gate /* Need to reset here as user type/class now known */
34680Sstevel@tonic-gate #ifdef INET6
34690Sstevel@tonic-gate /* IP_TOS is an IPv4 socket option */
34700Sstevel@tonic-gate if (SOCK_FAMILY(ctrl_addr) == AF_INET)
34710Sstevel@tonic-gate #endif
34720Sstevel@tonic-gate if ((cos = IPClassOfService("control")) >= 0) {
34730Sstevel@tonic-gate if (setsockopt(0, IPPROTO_IP, IP_TOS, (char *) &cos, sizeof(int)) < 0)
34740Sstevel@tonic-gate syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
34750Sstevel@tonic-gate }
34760Sstevel@tonic-gate
34770Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
34780Sstevel@tonic-gate audit_ftpd_success(the_user);
34790Sstevel@tonic-gate #endif
34800Sstevel@tonic-gate init_privs(pw->pw_name);
34810Sstevel@tonic-gate return;
34820Sstevel@tonic-gate bad:
34830Sstevel@tonic-gate /* Forget all about it... */
34840Sstevel@tonic-gate if (xferlog)
34850Sstevel@tonic-gate close(xferlog);
34860Sstevel@tonic-gate xferlog = 0;
34870Sstevel@tonic-gate acl_remove();
34880Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
34890Sstevel@tonic-gate audit_ftpd_failure(the_user);
34900Sstevel@tonic-gate #endif
34910Sstevel@tonic-gate end_login();
34920Sstevel@tonic-gate return;
34930Sstevel@tonic-gate }
34940Sstevel@tonic-gate
34950Sstevel@tonic-gate int restricteduid(uid_t uid)
34960Sstevel@tonic-gate {
34970Sstevel@tonic-gate return uid_match("restricted-uid", uid);
34980Sstevel@tonic-gate }
34990Sstevel@tonic-gate
35000Sstevel@tonic-gate int unrestricteduid(uid_t uid)
35010Sstevel@tonic-gate {
35020Sstevel@tonic-gate return uid_match("unrestricted-uid", uid);
35030Sstevel@tonic-gate }
35040Sstevel@tonic-gate
35050Sstevel@tonic-gate int restrictedgid(gid_t gid)
35060Sstevel@tonic-gate {
35070Sstevel@tonic-gate return gid_match("restricted-gid", gid, NULL);
35080Sstevel@tonic-gate }
35090Sstevel@tonic-gate
35100Sstevel@tonic-gate int unrestrictedgid(gid_t gid)
35110Sstevel@tonic-gate {
35120Sstevel@tonic-gate return gid_match("unrestricted-gid", gid, NULL);
35130Sstevel@tonic-gate }
35140Sstevel@tonic-gate
35150Sstevel@tonic-gate char *opt_string(int options)
35160Sstevel@tonic-gate {
35170Sstevel@tonic-gate static char buf[100];
35180Sstevel@tonic-gate char *ptr = buf;
35190Sstevel@tonic-gate
35200Sstevel@tonic-gate if ((options & O_COMPRESS) != 0) /* debian fixes: NULL -> 0 */
35210Sstevel@tonic-gate *ptr++ = 'C';
35220Sstevel@tonic-gate if ((options & O_TAR) != 0)
35230Sstevel@tonic-gate *ptr++ = 'T';
35240Sstevel@tonic-gate if ((options & O_UNCOMPRESS) != 0)
35250Sstevel@tonic-gate *ptr++ = 'U';
35260Sstevel@tonic-gate if (options == 0)
35270Sstevel@tonic-gate *ptr++ = '_';
35280Sstevel@tonic-gate *ptr++ = '\0';
35290Sstevel@tonic-gate return (buf);
35300Sstevel@tonic-gate }
35310Sstevel@tonic-gate
35320Sstevel@tonic-gate #ifdef INTERNAL_LS
35330Sstevel@tonic-gate char *rpad(char *s, unsigned int len)
35340Sstevel@tonic-gate {
35350Sstevel@tonic-gate char *a;
35360Sstevel@tonic-gate a = (char *) malloc(len + 1);
35370Sstevel@tonic-gate memset(a, ' ', len);
35380Sstevel@tonic-gate a[len] = 0;
35390Sstevel@tonic-gate if (strlen(s) <= len)
35400Sstevel@tonic-gate memcpy(a, s, strlen(s));
35410Sstevel@tonic-gate else
35420Sstevel@tonic-gate strncpy(a, s, len);
35430Sstevel@tonic-gate return a;
35440Sstevel@tonic-gate }
35450Sstevel@tonic-gate
35460Sstevel@tonic-gate char *ls_file(const char *file, int nameonly, char remove_path, char classify)
35470Sstevel@tonic-gate {
35480Sstevel@tonic-gate static const char month[12][4] =
35490Sstevel@tonic-gate {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
35500Sstevel@tonic-gate "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
35510Sstevel@tonic-gate
35520Sstevel@tonic-gate char *permissions;
35530Sstevel@tonic-gate struct stat s;
35540Sstevel@tonic-gate struct tm *t;
35550Sstevel@tonic-gate char *ls_entry;
35560Sstevel@tonic-gate char *owner, *ownerg;
35570Sstevel@tonic-gate char *rpowner, *rpownerg;
35580Sstevel@tonic-gate char *link;
35590Sstevel@tonic-gate #ifndef LS_NUMERIC_UIDS
35600Sstevel@tonic-gate struct passwd *pw;
35610Sstevel@tonic-gate struct group *gr;
35620Sstevel@tonic-gate #endif
35630Sstevel@tonic-gate link = NULL;
35640Sstevel@tonic-gate owner = NULL;
35650Sstevel@tonic-gate ownerg = NULL;
35660Sstevel@tonic-gate if (lstat(file, &s) != 0) /* File doesn't exist, or is not readable by user */
35670Sstevel@tonic-gate return NULL;
35680Sstevel@tonic-gate ls_entry = (char *) malloc(312);
35690Sstevel@tonic-gate memset(ls_entry, 0, 312);
35700Sstevel@tonic-gate permissions = strdup("----------");
35710Sstevel@tonic-gate if (S_ISLNK(s.st_mode)) {
35720Sstevel@tonic-gate permissions[0] = 'l';
35730Sstevel@tonic-gate if (classify)
35740Sstevel@tonic-gate classify = '@';
35750Sstevel@tonic-gate }
35760Sstevel@tonic-gate else if (S_ISDIR(s.st_mode)) {
35770Sstevel@tonic-gate permissions[0] = 'd';
35780Sstevel@tonic-gate if (classify)
35790Sstevel@tonic-gate classify = '/';
35800Sstevel@tonic-gate }
35810Sstevel@tonic-gate else if (S_ISBLK(s.st_mode))
35820Sstevel@tonic-gate permissions[0] = 'b';
35830Sstevel@tonic-gate else if (S_ISCHR(s.st_mode))
35840Sstevel@tonic-gate permissions[0] = 'c';
35850Sstevel@tonic-gate else if (S_ISFIFO(s.st_mode)) {
35860Sstevel@tonic-gate permissions[0] = 'p';
35870Sstevel@tonic-gate if (classify == 1)
35880Sstevel@tonic-gate classify = '=';
35890Sstevel@tonic-gate }
35900Sstevel@tonic-gate #ifdef S_ISSOCK
35910Sstevel@tonic-gate else if (S_ISSOCK(s.st_mode))
35920Sstevel@tonic-gate permissions[0] = 's';
35930Sstevel@tonic-gate #endif
35940Sstevel@tonic-gate if ((s.st_mode & S_IRUSR) == S_IRUSR)
35950Sstevel@tonic-gate permissions[1] = 'r';
35960Sstevel@tonic-gate if ((s.st_mode & S_IWUSR) == S_IWUSR)
35970Sstevel@tonic-gate permissions[2] = 'w';
35980Sstevel@tonic-gate if ((s.st_mode & S_IXUSR) == S_IXUSR) {
35990Sstevel@tonic-gate permissions[3] = 'x';
36000Sstevel@tonic-gate if (classify == 1)
36010Sstevel@tonic-gate classify = '*';
36020Sstevel@tonic-gate #ifndef HIDE_SETUID
36030Sstevel@tonic-gate if ((s.st_mode & S_ISUID) == S_ISUID)
36040Sstevel@tonic-gate permissions[3] = 's';
36050Sstevel@tonic-gate #endif
36060Sstevel@tonic-gate }
36070Sstevel@tonic-gate #ifndef HIDE_SETUID
36080Sstevel@tonic-gate else if ((s.st_mode & S_ISUID) == S_ISUID)
36090Sstevel@tonic-gate permissions[3] = 'S';
36100Sstevel@tonic-gate #endif
36110Sstevel@tonic-gate if ((s.st_mode & S_IRGRP) == S_IRGRP)
36120Sstevel@tonic-gate permissions[4] = 'r';
36130Sstevel@tonic-gate if ((s.st_mode & S_IWGRP) == S_IWGRP)
36140Sstevel@tonic-gate permissions[5] = 'w';
36150Sstevel@tonic-gate if ((s.st_mode & S_IXGRP) == S_IXGRP) {
36160Sstevel@tonic-gate permissions[6] = 'x';
36170Sstevel@tonic-gate if (classify == 1)
36180Sstevel@tonic-gate classify = '*';
36190Sstevel@tonic-gate #ifndef HIDE_SETUID
36200Sstevel@tonic-gate if ((s.st_mode & S_ISGID) == S_ISGID)
36210Sstevel@tonic-gate permissions[6] = 's';
36220Sstevel@tonic-gate #endif
36230Sstevel@tonic-gate }
36240Sstevel@tonic-gate #ifndef HIDE_SETUID
36250Sstevel@tonic-gate else if ((s.st_mode & S_ISGID) == S_ISGID)
36260Sstevel@tonic-gate permissions[6] = 'S';
36270Sstevel@tonic-gate #endif
36280Sstevel@tonic-gate if ((s.st_mode & S_IROTH) == S_IROTH)
36290Sstevel@tonic-gate permissions[7] = 'r';
36300Sstevel@tonic-gate if ((s.st_mode & S_IWOTH) == S_IWOTH)
36310Sstevel@tonic-gate permissions[8] = 'w';
36320Sstevel@tonic-gate if ((s.st_mode & S_IXOTH) == S_IXOTH) {
36330Sstevel@tonic-gate permissions[9] = 'x';
36340Sstevel@tonic-gate if (classify == 1)
36350Sstevel@tonic-gate classify = '*';
36360Sstevel@tonic-gate #ifndef HIDE_SETUID
36370Sstevel@tonic-gate if ((s.st_mode & S_ISVTX) == S_ISVTX)
36380Sstevel@tonic-gate permissions[9] = 't';
36390Sstevel@tonic-gate #endif
36400Sstevel@tonic-gate }
36410Sstevel@tonic-gate #ifndef HIDE_SETUID
36420Sstevel@tonic-gate else if ((s.st_mode & S_ISVTX) == S_ISVTX)
36430Sstevel@tonic-gate permissions[9] = 'T';
36440Sstevel@tonic-gate #endif
36450Sstevel@tonic-gate t = localtime(&s.st_mtime);
36460Sstevel@tonic-gate #ifndef LS_NUMERIC_UIDS
36470Sstevel@tonic-gate #ifdef OTHER_PASSWD
36480Sstevel@tonic-gate pw = bero_getpwuid(s.st_uid, _path_passwd);
36490Sstevel@tonic-gate #else
36500Sstevel@tonic-gate pw = getpwuid(s.st_uid);
36510Sstevel@tonic-gate #endif
36520Sstevel@tonic-gate if (pw != NULL)
36530Sstevel@tonic-gate owner = strdup(pw->pw_name);
36540Sstevel@tonic-gate gr = getgrgid(s.st_gid);
36550Sstevel@tonic-gate if (gr != NULL)
36560Sstevel@tonic-gate ownerg = strdup(gr->gr_name);
36570Sstevel@tonic-gate #endif
36580Sstevel@tonic-gate if (owner == NULL) { /* Can't figure out username (or don't want to) */
36590Sstevel@tonic-gate if (s.st_uid == 0)
36600Sstevel@tonic-gate owner = strdup("root");
36610Sstevel@tonic-gate else {
36620Sstevel@tonic-gate owner = (char *) malloc(9);
36630Sstevel@tonic-gate memset(owner, 0, 9);
36640Sstevel@tonic-gate #ifdef SOLARIS_2
36650Sstevel@tonic-gate snprintf(owner, 8, "%lu", s.st_uid);
36660Sstevel@tonic-gate #else
36670Sstevel@tonic-gate snprintf(owner, 8, "%u", s.st_uid);
36680Sstevel@tonic-gate #endif
36690Sstevel@tonic-gate }
36700Sstevel@tonic-gate }
36710Sstevel@tonic-gate if (ownerg == NULL) { /* Can't figure out groupname (or don't want to) */
36720Sstevel@tonic-gate if (s.st_gid == 0)
36730Sstevel@tonic-gate ownerg = strdup("root");
36740Sstevel@tonic-gate else {
36750Sstevel@tonic-gate ownerg = (char *) malloc(9);
36760Sstevel@tonic-gate memset(ownerg, 0, 9);
36770Sstevel@tonic-gate #ifdef SOLARIS_2
36780Sstevel@tonic-gate snprintf(ownerg, 8, "%lu", s.st_gid);
36790Sstevel@tonic-gate #else
36800Sstevel@tonic-gate snprintf(ownerg, 8, "%u", s.st_gid);
36810Sstevel@tonic-gate #endif
36820Sstevel@tonic-gate }
36830Sstevel@tonic-gate }
36840Sstevel@tonic-gate
36850Sstevel@tonic-gate #ifdef HAVE_LSTAT
3686871Scasper if (S_ISLNK(s.st_mode)) {
36870Sstevel@tonic-gate link = (char *) malloc(MAXPATHLEN);
36880Sstevel@tonic-gate memset(link, 0, MAXPATHLEN);
36890Sstevel@tonic-gate if (readlink(file, link, MAXPATHLEN) == -1) {
36900Sstevel@tonic-gate free(link);
36910Sstevel@tonic-gate link = NULL;
36920Sstevel@tonic-gate }
36930Sstevel@tonic-gate }
36940Sstevel@tonic-gate #endif
36950Sstevel@tonic-gate
36960Sstevel@tonic-gate if (remove_path != 0 && strchr(file, '/'))
36970Sstevel@tonic-gate file = strrchr(file, '/') + 1;
36980Sstevel@tonic-gate
36990Sstevel@tonic-gate rpowner = rpad(owner, 8);
37000Sstevel@tonic-gate rpownerg = rpad(ownerg, 8);
37010Sstevel@tonic-gate
37020Sstevel@tonic-gate #ifdef SOLARIS_2
37030Sstevel@tonic-gate #define N_FORMAT "lu"
37040Sstevel@tonic-gate #else
37050Sstevel@tonic-gate #if defined(__FreeBSD__) || defined(__bsdi__)
37060Sstevel@tonic-gate #define N_FORMAT "u"
37070Sstevel@tonic-gate #else
37080Sstevel@tonic-gate #define N_FORMAT "u"
37090Sstevel@tonic-gate #endif
37100Sstevel@tonic-gate #endif
37110Sstevel@tonic-gate
37120Sstevel@tonic-gate if (nameonly) {
37130Sstevel@tonic-gate sprintf(ls_entry, "%s", file);
37140Sstevel@tonic-gate if (link != NULL)
37150Sstevel@tonic-gate free(link);
37160Sstevel@tonic-gate }
37170Sstevel@tonic-gate else {
37180Sstevel@tonic-gate if ((time(NULL) - s.st_mtime) > 6307200) { /* File is older than 6 months */
37190Sstevel@tonic-gate if (link == NULL)
37200Sstevel@tonic-gate snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %5u %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, 1900 + t->tm_year, file);
37210Sstevel@tonic-gate else {
37220Sstevel@tonic-gate snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %5u %s -> %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, 1900 + t->tm_year, file, link);
37230Sstevel@tonic-gate free(link);
37240Sstevel@tonic-gate }
37250Sstevel@tonic-gate }
37260Sstevel@tonic-gate else if (link == NULL)
37270Sstevel@tonic-gate snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %02u:%02u %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, file);
37280Sstevel@tonic-gate else {
37290Sstevel@tonic-gate snprintf(ls_entry, 311, "%s %3" N_FORMAT " %s %s %8" L_FORMAT " %s %2u %02u:%02u %s -> %s", permissions, s.st_nlink, rpowner, rpownerg, s.st_size, month[t->tm_mon], t->tm_mday, t->tm_hour, t->tm_min, file, link);
37300Sstevel@tonic-gate free(link);
37310Sstevel@tonic-gate }
37320Sstevel@tonic-gate }
37330Sstevel@tonic-gate free(rpowner);
37340Sstevel@tonic-gate free(rpownerg);
37350Sstevel@tonic-gate free(owner);
37360Sstevel@tonic-gate free(ownerg);
37370Sstevel@tonic-gate if (classify > 1)
37380Sstevel@tonic-gate sprintf(ls_entry + strlen(ls_entry), "%c", classify);
37390Sstevel@tonic-gate strcat(ls_entry, "\r\n");
37400Sstevel@tonic-gate free(permissions);
37410Sstevel@tonic-gate return ls_entry;
37420Sstevel@tonic-gate }
37430Sstevel@tonic-gate
37440Sstevel@tonic-gate void ls_dir(char *d, char ls_a, char ls_F, char ls_l, char ls_R, char omit_total, FILE *out)
37450Sstevel@tonic-gate {
37460Sstevel@tonic-gate int total;
37470Sstevel@tonic-gate char *realdir; /* fixed up value to pass to glob() */
37480Sstevel@tonic-gate char **subdirs; /* Subdirs to be scanned for ls -R */
37490Sstevel@tonic-gate int numSubdirs = 0;
37500Sstevel@tonic-gate glob_t g;
37510Sstevel@tonic-gate char isDir; /* 0: d is a file; 1: d is some files; 2: d is dir */
37520Sstevel@tonic-gate struct stat s;
37530Sstevel@tonic-gate char *dirlist;
37540Sstevel@tonic-gate unsigned long dl_size, dl_used;
37550Sstevel@tonic-gate char *c;
37560Sstevel@tonic-gate char *lsentry;
37570Sstevel@tonic-gate int i;
37580Sstevel@tonic-gate #ifndef GLOB_PERIOD
37590Sstevel@tonic-gate char *dperiod;
37600Sstevel@tonic-gate #endif
37610Sstevel@tonic-gate
37620Sstevel@tonic-gate isDir = 0;
37630Sstevel@tonic-gate realdir = (char *) malloc(strlen(d) + 3);
37640Sstevel@tonic-gate memset(realdir, 0, strlen(d) + 3);
37650Sstevel@tonic-gate strcpy(realdir, d);
37660Sstevel@tonic-gate if (strcmp(realdir, ".") == 0)
37670Sstevel@tonic-gate realdir[0] = '*';
37680Sstevel@tonic-gate if (strcmp(realdir + strlen(realdir) - 2, "/.") == 0)
37690Sstevel@tonic-gate realdir[strlen(realdir) - 1] = '*';
37700Sstevel@tonic-gate if (realdir[strlen(realdir) - 1] == '/')
37710Sstevel@tonic-gate strcat(realdir, "*");
37720Sstevel@tonic-gate if (strchr(realdir, '*') || strchr(realdir, '?'))
37730Sstevel@tonic-gate isDir = 1;
37740Sstevel@tonic-gate if (strcmp(realdir, "*") == 0 || strcmp(realdir + strlen(realdir) - 2, "/*") == 0)
37750Sstevel@tonic-gate isDir = 2;
37760Sstevel@tonic-gate else {
37770Sstevel@tonic-gate if (lstat(realdir, &s) == 0) {
37780Sstevel@tonic-gate if (S_ISDIR(s.st_mode)) {
37790Sstevel@tonic-gate strcat(realdir, "/*");
37800Sstevel@tonic-gate isDir = 2;
37810Sstevel@tonic-gate }
37820Sstevel@tonic-gate }
37830Sstevel@tonic-gate }
37840Sstevel@tonic-gate
37850Sstevel@tonic-gate if (isDir == 0) {
37860Sstevel@tonic-gate if (ls_l) {
37870Sstevel@tonic-gate lsentry = ls_file(realdir, 0, 0, ls_F);
37880Sstevel@tonic-gate if (lsentry != NULL) {
37890Sstevel@tonic-gate if (draconian_FILE != NULL) {
37900Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
37910Sstevel@tonic-gate alarm(timeout_data);
37920Sstevel@tonic-gate #if defined(USE_GSS)
37930Sstevel@tonic-gate sec_fprintf(out, "%s", lsentry);
37940Sstevel@tonic-gate #else
37950Sstevel@tonic-gate fputs(lsentry, out);
37960Sstevel@tonic-gate #endif /* defined(USE_GSS) */
37970Sstevel@tonic-gate (void) signal(SIGALRM, SIG_DFL);
37980Sstevel@tonic-gate }
37990Sstevel@tonic-gate free(lsentry);
38000Sstevel@tonic-gate }
38010Sstevel@tonic-gate }
38020Sstevel@tonic-gate else {
38030Sstevel@tonic-gate if (draconian_FILE != NULL) {
38040Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
38050Sstevel@tonic-gate alarm(timeout_data);
38060Sstevel@tonic-gate #if defined(USE_GSS)
38070Sstevel@tonic-gate sec_fprintf(out, "%s", realdir);
38080Sstevel@tonic-gate #else
38090Sstevel@tonic-gate fputs(realdir, out);
38100Sstevel@tonic-gate #endif /* defined(USE_GSS) */
38110Sstevel@tonic-gate (void) signal(SIGALRM, SIG_DFL);
38120Sstevel@tonic-gate }
38130Sstevel@tonic-gate }
38140Sstevel@tonic-gate free(realdir);
38150Sstevel@tonic-gate }
38160Sstevel@tonic-gate else {
38170Sstevel@tonic-gate if (ls_R) {
38180Sstevel@tonic-gate numSubdirs = 0;
38190Sstevel@tonic-gate subdirs = (char **) malloc(200 * sizeof(char *));
38200Sstevel@tonic-gate memset(subdirs, 0, 200 * sizeof(char *));
38210Sstevel@tonic-gate }
38220Sstevel@tonic-gate
38230Sstevel@tonic-gate dl_size = 65536;
38240Sstevel@tonic-gate dirlist = (char *) malloc(65536);
38250Sstevel@tonic-gate memset(dirlist, 0, 65536);
38260Sstevel@tonic-gate dl_used = 0;
38270Sstevel@tonic-gate
38280Sstevel@tonic-gate total = 0;
38290Sstevel@tonic-gate memset(&g, 0, sizeof(g));
38300Sstevel@tonic-gate if (ls_a) {
38310Sstevel@tonic-gate #ifdef GLOB_PERIOD
38320Sstevel@tonic-gate if (glob(realdir, GLOB_ERR | GLOB_PERIOD, NULL, &g) != 0)
38330Sstevel@tonic-gate g.gl_pathc = 0;
38340Sstevel@tonic-gate #else
38350Sstevel@tonic-gate dperiod = (char *) malloc(strlen(realdir) + 2);
38360Sstevel@tonic-gate memset(dperiod, 0, strlen(realdir) + 2);
38370Sstevel@tonic-gate strcpy(dperiod, ".");
38380Sstevel@tonic-gate strcat(dperiod, realdir);
38390Sstevel@tonic-gate if (glob(dperiod, GLOB_ERR, NULL, &g) != 0)
38400Sstevel@tonic-gate g.gl_pathc = 0;
38410Sstevel@tonic-gate glob(realdir, GLOB_ERR | GLOB_APPEND, NULL, &g);
38420Sstevel@tonic-gate free(dperiod);
38430Sstevel@tonic-gate #endif
38440Sstevel@tonic-gate }
38450Sstevel@tonic-gate else if (glob(realdir, GLOB_ERR, NULL, &g) != 0)
38460Sstevel@tonic-gate g.gl_pathc = 0;
38470Sstevel@tonic-gate free(realdir);
38480Sstevel@tonic-gate for (i = 0; i < g.gl_pathc; i++) {
38490Sstevel@tonic-gate c = g.gl_pathv[i];
38500Sstevel@tonic-gate if (lstat(c, &s) != -1) {
38510Sstevel@tonic-gate if (ls_l) {
38520Sstevel@tonic-gate total += s.st_blocks;
38530Sstevel@tonic-gate lsentry = ls_file(c, 0, 1, ls_F);
38540Sstevel@tonic-gate if (lsentry != NULL) {
38550Sstevel@tonic-gate /* This can actually happen even though the lstat() worked -
38560Sstevel@tonic-gate if someone deletes the file between the lstat() and ls_file()
38570Sstevel@tonic-gate calls. Unlikely, but better safe than sorry... */
38580Sstevel@tonic-gate int flag = snprintf(dirlist + dl_used, dl_size - dl_used, "%s", lsentry);
38590Sstevel@tonic-gate dl_used += (flag == -1 ? dl_size - dl_used : flag);
38600Sstevel@tonic-gate free(lsentry);
38610Sstevel@tonic-gate }
38620Sstevel@tonic-gate }
38630Sstevel@tonic-gate else {
38640Sstevel@tonic-gate int flag;
38650Sstevel@tonic-gate lsentry = ls_file(c, 1, 1, ls_F);
38660Sstevel@tonic-gate if (lsentry != NULL) {
38670Sstevel@tonic-gate flag = snprintf(dirlist + dl_used, dl_size - dl_used, "%s", lsentry);
38680Sstevel@tonic-gate dl_used += (flag == -1 ? dl_size - dl_used : flag);
38690Sstevel@tonic-gate free(lsentry);
38700Sstevel@tonic-gate }
38710Sstevel@tonic-gate }
38720Sstevel@tonic-gate if ((ls_R != 0) && (S_ISDIR(s.st_mode))
38730Sstevel@tonic-gate && (strcmp(c, "..") != 0) && (strcmp(c, ".") != 0)
38740Sstevel@tonic-gate && !(strlen(c) > 3 && strcmp(c + strlen(c) - 3, "/..") == 0)
38750Sstevel@tonic-gate && !(strlen(c) > 2 && strcmp(c + strlen(c) - 2, "/.") == 0)) {
38760Sstevel@tonic-gate subdirs[numSubdirs++] = strdup(c);
38770Sstevel@tonic-gate if ((numSubdirs % 200) == 0)
38780Sstevel@tonic-gate subdirs = (char **) realloc(subdirs, (numSubdirs + 200) * sizeof(char *));
38790Sstevel@tonic-gate }
38800Sstevel@tonic-gate }
38810Sstevel@tonic-gate if (dl_used + 512 >= dl_size) {
38820Sstevel@tonic-gate dl_size += 65536;
38830Sstevel@tonic-gate dirlist = (char *) realloc(dirlist, dl_size);
38840Sstevel@tonic-gate }
38850Sstevel@tonic-gate }
38860Sstevel@tonic-gate globfree(&g);
38870Sstevel@tonic-gate if (ls_l && isDir == 2 && omit_total == 0) {
38880Sstevel@tonic-gate if (draconian_FILE != NULL) {
38890Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
38900Sstevel@tonic-gate alarm(timeout_data);
38910Sstevel@tonic-gate #if defined(USE_GSS)
38920Sstevel@tonic-gate sec_fprintf(out, "total %u\r\n", total);
38930Sstevel@tonic-gate #else
38940Sstevel@tonic-gate fprintf(out, "total %u\r\n", total);
38950Sstevel@tonic-gate #endif /* defined(USE_GSS) */
38960Sstevel@tonic-gate }
38970Sstevel@tonic-gate }
38980Sstevel@tonic-gate if (draconian_FILE != NULL) {
38990Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
39000Sstevel@tonic-gate alarm(timeout_data);
39010Sstevel@tonic-gate #if defined(USE_GSS)
39020Sstevel@tonic-gate sec_fprintf(out, "%s", dirlist);
39030Sstevel@tonic-gate #else
39040Sstevel@tonic-gate fputs(dirlist, out);
39050Sstevel@tonic-gate #endif /* defined(USE_GSS) */
39060Sstevel@tonic-gate }
39070Sstevel@tonic-gate free(dirlist);
39080Sstevel@tonic-gate if (ls_R) {
39090Sstevel@tonic-gate for (i = 0; i < numSubdirs; i++) {
39100Sstevel@tonic-gate if (draconian_FILE != NULL) {
39110Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
39120Sstevel@tonic-gate alarm(timeout_data);
39130Sstevel@tonic-gate #if defined(USE_GSS)
39140Sstevel@tonic-gate sec_fprintf(out, "\r\n%s:\r\n", subdirs[i]);
39150Sstevel@tonic-gate #else
39160Sstevel@tonic-gate fprintf(out, "\r\n%s:\r\n", subdirs[i]);
39170Sstevel@tonic-gate #endif /* defined(USE_GSS) */
39180Sstevel@tonic-gate ls_dir(subdirs[i], ls_a, ls_F, ls_l, ls_R, 0, out);
39190Sstevel@tonic-gate }
39200Sstevel@tonic-gate free(subdirs[i]);
39210Sstevel@tonic-gate }
39220Sstevel@tonic-gate free(subdirs);
39230Sstevel@tonic-gate }
39240Sstevel@tonic-gate }
39250Sstevel@tonic-gate }
39260Sstevel@tonic-gate
39270Sstevel@tonic-gate void ls(char *file, char nlst)
39280Sstevel@tonic-gate {
39290Sstevel@tonic-gate FILE *out;
39300Sstevel@tonic-gate char free_file = 0;
39310Sstevel@tonic-gate char ls_l = 0, ls_a = 0, ls_R = 0, ls_F = 0;
39320Sstevel@tonic-gate
39330Sstevel@tonic-gate if (nlst == 0)
39340Sstevel@tonic-gate ls_l = 1; /* LIST defaults to ls -l */
39350Sstevel@tonic-gate if (file == NULL) {
39360Sstevel@tonic-gate file = strdup(".");
39370Sstevel@tonic-gate free_file = 1;
39380Sstevel@tonic-gate }
39390Sstevel@tonic-gate if (strcmp(file, "*") == 0)
39400Sstevel@tonic-gate file[0] = '.';
39410Sstevel@tonic-gate
39420Sstevel@tonic-gate if (file[0] == '-') { /* options... */
39430Sstevel@tonic-gate if (strchr(file, ' ') == 0) {
39440Sstevel@tonic-gate if (strchr(file, 'l'))
39450Sstevel@tonic-gate ls_l = 1;
39460Sstevel@tonic-gate if (strchr(file, 'a'))
39470Sstevel@tonic-gate ls_a = 1;
39480Sstevel@tonic-gate if (strchr(file, 'R'))
39490Sstevel@tonic-gate ls_R = 1;
39500Sstevel@tonic-gate if (strchr(file, 'F'))
39510Sstevel@tonic-gate ls_F = 1;
39520Sstevel@tonic-gate file = strdup(".");
39530Sstevel@tonic-gate free_file = 1;
39540Sstevel@tonic-gate }
39550Sstevel@tonic-gate else {
39560Sstevel@tonic-gate if (strchr(file, 'l') != NULL && strchr(file, 'l') < strchr(file, ' '))
39570Sstevel@tonic-gate ls_l = 1;
39580Sstevel@tonic-gate if (strchr(file, 'a') != NULL && strchr(file, 'a') < strchr(file, ' '))
39590Sstevel@tonic-gate ls_a = 1;
39600Sstevel@tonic-gate if (strchr(file, 'R') != NULL && strchr(file, 'R') < strchr(file, ' '))
39610Sstevel@tonic-gate ls_R = 1;
39620Sstevel@tonic-gate if (strchr(file, 'F') != NULL && strchr(file, 'F') < strchr(file, ' '))
39630Sstevel@tonic-gate ls_F = 1;
39640Sstevel@tonic-gate file = strchr(file, ' ');
39650Sstevel@tonic-gate }
39660Sstevel@tonic-gate }
39670Sstevel@tonic-gate while (file[0] == ' ') /* ignore additional whitespaces between parameters */
39680Sstevel@tonic-gate file++;
39690Sstevel@tonic-gate if (strlen(file) == 0) {
39700Sstevel@tonic-gate file = strdup(".");
39710Sstevel@tonic-gate free_file = 1;
39720Sstevel@tonic-gate }
39730Sstevel@tonic-gate
39740Sstevel@tonic-gate out = dataconn("directory listing", -1, "w");
39750Sstevel@tonic-gate draconian_FILE = out;
39760Sstevel@tonic-gate
39770Sstevel@tonic-gate transflag++;
39780Sstevel@tonic-gate
39790Sstevel@tonic-gate fixpath(file);
39800Sstevel@tonic-gate if (file[0] == '\0') {
39810Sstevel@tonic-gate if (free_file != 0)
39820Sstevel@tonic-gate free(file);
39830Sstevel@tonic-gate file = strdup(".");
39840Sstevel@tonic-gate free_file = 1;
39850Sstevel@tonic-gate }
39860Sstevel@tonic-gate
39870Sstevel@tonic-gate ls_dir(file, ls_a, ls_F, ls_l, ls_R, 0, out);
39880Sstevel@tonic-gate data = -1;
39890Sstevel@tonic-gate pdata = -1;
39900Sstevel@tonic-gate if (draconian_FILE != NULL) {
39910Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
39920Sstevel@tonic-gate alarm(timeout_data);
39930Sstevel@tonic-gate #if defined(USE_GSS)
39940Sstevel@tonic-gate if (sec_fflush(out) < 0) {
39950Sstevel@tonic-gate draconian_FILE = NULL;
39960Sstevel@tonic-gate alarm(0);
39970Sstevel@tonic-gate transflag = 0;
39980Sstevel@tonic-gate perror_reply(550, "Data connection");
39990Sstevel@tonic-gate fclose(out);
40000Sstevel@tonic-gate goto ls_done;
40010Sstevel@tonic-gate }
40020Sstevel@tonic-gate #else
40030Sstevel@tonic-gate fflush(out);
40040Sstevel@tonic-gate #endif /* defined(USE_GSS) */
40050Sstevel@tonic-gate }
40060Sstevel@tonic-gate if (draconian_FILE != NULL) {
40070Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
40080Sstevel@tonic-gate alarm(timeout_data);
40090Sstevel@tonic-gate socket_flush_wait(out);
40100Sstevel@tonic-gate }
40110Sstevel@tonic-gate if (draconian_FILE != NULL) {
40120Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
40130Sstevel@tonic-gate alarm(timeout_data);
40140Sstevel@tonic-gate fclose(out);
40150Sstevel@tonic-gate draconian_FILE = NULL;
40160Sstevel@tonic-gate }
40170Sstevel@tonic-gate alarm(0);
40180Sstevel@tonic-gate transflag = 0;
40190Sstevel@tonic-gate reply(226, "Transfer complete.");
40200Sstevel@tonic-gate ls_done:
40210Sstevel@tonic-gate if (free_file != 0)
40220Sstevel@tonic-gate free(file);
40230Sstevel@tonic-gate }
40240Sstevel@tonic-gate #endif /* INTERNAL_LS */
40250Sstevel@tonic-gate
40260Sstevel@tonic-gate void retrieve(char *cmd, char *name)
40270Sstevel@tonic-gate {
40280Sstevel@tonic-gate FILE *fin = NULL, *dout;
40290Sstevel@tonic-gate struct stat st, junk;
40300Sstevel@tonic-gate int (*closefunc) () = NULL;
40310Sstevel@tonic-gate int options = 0;
40320Sstevel@tonic-gate int ThisRetrieveIsData = retrieve_is_data;
40330Sstevel@tonic-gate time_t start_time = time(NULL);
40340Sstevel@tonic-gate char *logname;
40350Sstevel@tonic-gate char namebuf[MAXPATHLEN];
40360Sstevel@tonic-gate char fnbuf[MAXPATHLEN];
40370Sstevel@tonic-gate static int TransferComplete; /* static as retrieve can call itself */
40380Sstevel@tonic-gate struct convert *cptr;
40390Sstevel@tonic-gate char realname[MAXPATHLEN];
40400Sstevel@tonic-gate int stat_ret = -1;
40410Sstevel@tonic-gate size_t buffersize;
40420Sstevel@tonic-gate
40430Sstevel@tonic-gate TransferComplete = 0;
40440Sstevel@tonic-gate wu_realpath(name, realname, chroot_path);
40450Sstevel@tonic-gate
40460Sstevel@tonic-gate if (cmd == NULL && (stat_ret = stat(name, &st)) == 0)
40470Sstevel@tonic-gate /* there isn't a command and the file exists */
40480Sstevel@tonic-gate if (use_accessfile && checknoretrieve(name)) { /* see above. _H */
40490Sstevel@tonic-gate if (log_security)
40500Sstevel@tonic-gate if (anonymous)
40510Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (noretrieve)",
40520Sstevel@tonic-gate guestpw, remoteident, realname);
40530Sstevel@tonic-gate else
40540Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to download %s (noretrieve)",
40550Sstevel@tonic-gate pw->pw_name, remoteident, realname);
40560Sstevel@tonic-gate return;
40570Sstevel@tonic-gate }
40580Sstevel@tonic-gate
40590Sstevel@tonic-gate #ifdef TRANSFER_COUNT
40600Sstevel@tonic-gate #ifdef TRANSFER_LIMIT
40610Sstevel@tonic-gate if (retrieve_is_data)
40620Sstevel@tonic-gate if (((file_limit_data_out > 0) && (file_count_out >= file_limit_data_out))
40630Sstevel@tonic-gate || ((file_limit_data_total > 0) && (file_count_total >= file_limit_data_total))
40640Sstevel@tonic-gate || ((data_limit_data_out > 0) && (data_count_out >= data_limit_data_out))
40650Sstevel@tonic-gate || ((data_limit_data_total > 0) && (data_count_total >= data_limit_data_total))) {
40660Sstevel@tonic-gate if (log_security)
40670Sstevel@tonic-gate if (anonymous)
40680Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to retrieve %s (Transfer limits exceeded)",
40690Sstevel@tonic-gate guestpw, remoteident, realname);
40700Sstevel@tonic-gate else
40710Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to retrieve %s (Transfer limits exceeded)",
40720Sstevel@tonic-gate pw->pw_name, remoteident, realname);
40730Sstevel@tonic-gate reply(553, "Permission denied on server. (Transfer limits exceeded)");
40740Sstevel@tonic-gate return;
40750Sstevel@tonic-gate }
40760Sstevel@tonic-gate if (((file_limit_raw_out > 0) && (xfer_count_out >= file_limit_raw_out))
40770Sstevel@tonic-gate || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
40780Sstevel@tonic-gate || ((data_limit_raw_out > 0) && (byte_count_out >= data_limit_raw_out))
40790Sstevel@tonic-gate || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
40800Sstevel@tonic-gate if (log_security)
40810Sstevel@tonic-gate if (anonymous)
40820Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to retrieve %s (Transfer limits exceeded)",
40830Sstevel@tonic-gate guestpw, remoteident, realname);
40840Sstevel@tonic-gate else
40850Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to retrieve %s (Transfer limits exceeded)",
40860Sstevel@tonic-gate pw->pw_name, remoteident, realname);
40870Sstevel@tonic-gate reply(553, "Permission denied on server. (Transfer limits exceeded)");
40880Sstevel@tonic-gate return;
40890Sstevel@tonic-gate }
40900Sstevel@tonic-gate #ifdef RATIO
40910Sstevel@tonic-gate if (retrieve_is_data && (upload_download_rate > 0) )
40920Sstevel@tonic-gate if( freefile = is_downloadfree(name) ) {
40930Sstevel@tonic-gate syslog(LOG_INFO, "%s is download free.", name );
40940Sstevel@tonic-gate }
40950Sstevel@tonic-gate else {
40960Sstevel@tonic-gate if ((cmd == NULL) && ((data_count_in * upload_download_rate) < (data_count_out - total_free_dl))) {
40970Sstevel@tonic-gate reply(550, "%s: Upload/Download ratio exceeded", name);
40980Sstevel@tonic-gate goto done;
40990Sstevel@tonic-gate }
41000Sstevel@tonic-gate }
41010Sstevel@tonic-gate #endif /* RATIO */
41020Sstevel@tonic-gate #endif
41030Sstevel@tonic-gate #endif
41040Sstevel@tonic-gate
41050Sstevel@tonic-gate logname = (char *) NULL;
41060Sstevel@tonic-gate if (cmd == NULL && stat_ret != 0) { /* file does not exist */
41070Sstevel@tonic-gate char *ptr;
41080Sstevel@tonic-gate
41090Sstevel@tonic-gate for (cptr = cvtptr; cptr != NULL; cptr = cptr->next) {
41100Sstevel@tonic-gate if (!(mangleopts & O_COMPRESS) && (cptr->options & O_COMPRESS))
41110Sstevel@tonic-gate continue;
41120Sstevel@tonic-gate if (!(mangleopts & O_UNCOMPRESS) && (cptr->options & O_UNCOMPRESS))
41130Sstevel@tonic-gate continue;
41140Sstevel@tonic-gate if (!(mangleopts & O_TAR) && (cptr->options & O_TAR))
41150Sstevel@tonic-gate continue;
41160Sstevel@tonic-gate
41170Sstevel@tonic-gate if ((cptr->stripfix) && (cptr->postfix)) {
41180Sstevel@tonic-gate int pfxlen = strlen(cptr->postfix);
41190Sstevel@tonic-gate int sfxlen = strlen(cptr->stripfix);
41200Sstevel@tonic-gate int namelen = strlen(name);
41210Sstevel@tonic-gate
41220Sstevel@tonic-gate if (namelen <= pfxlen)
41230Sstevel@tonic-gate continue;
41240Sstevel@tonic-gate if (((namelen - pfxlen + sfxlen) >= sizeof(fnbuf)) ||
41250Sstevel@tonic-gate (namelen >= sizeof(fnbuf)))
41260Sstevel@tonic-gate continue;
41270Sstevel@tonic-gate
41280Sstevel@tonic-gate (void) strcpy(fnbuf, name);
41290Sstevel@tonic-gate if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
41300Sstevel@tonic-gate continue;
41310Sstevel@tonic-gate *(fnbuf + namelen - pfxlen) = '\0';
41320Sstevel@tonic-gate (void) strcat(fnbuf, cptr->stripfix);
41330Sstevel@tonic-gate if (stat(fnbuf, &st) != 0)
41340Sstevel@tonic-gate continue;
41350Sstevel@tonic-gate }
41360Sstevel@tonic-gate else if (cptr->postfix) {
41370Sstevel@tonic-gate int pfxlen = strlen(cptr->postfix);
41380Sstevel@tonic-gate int namelen = strlen(name);
41390Sstevel@tonic-gate
41400Sstevel@tonic-gate if ((namelen <= pfxlen) || (namelen >= sizeof(fnbuf)))
41410Sstevel@tonic-gate continue;
41420Sstevel@tonic-gate (void) strcpy(fnbuf, name);
41430Sstevel@tonic-gate if (strcmp(fnbuf + namelen - pfxlen, cptr->postfix))
41440Sstevel@tonic-gate continue;
41450Sstevel@tonic-gate *(fnbuf + namelen - pfxlen) = (char) NULL;
41460Sstevel@tonic-gate if (stat(fnbuf, &st) != 0)
41470Sstevel@tonic-gate continue;
41480Sstevel@tonic-gate }
41490Sstevel@tonic-gate else if (cptr->stripfix) {
41500Sstevel@tonic-gate if (strlen(name) + strlen(cptr->stripfix) >= sizeof(fnbuf))
41510Sstevel@tonic-gate continue;
41520Sstevel@tonic-gate (void) strcpy(fnbuf, name);
41530Sstevel@tonic-gate (void) strcat(fnbuf, cptr->stripfix);
41540Sstevel@tonic-gate if (stat(fnbuf, &st) != 0)
41550Sstevel@tonic-gate continue;
41560Sstevel@tonic-gate }
41570Sstevel@tonic-gate else {
41580Sstevel@tonic-gate continue;
41590Sstevel@tonic-gate }
41600Sstevel@tonic-gate
41610Sstevel@tonic-gate if (S_ISDIR(st.st_mode)) {
41620Sstevel@tonic-gate if (!cptr->types || !(cptr->types & T_DIR)) {
41630Sstevel@tonic-gate reply(550, "Cannot %s directories.", cptr->name);
41640Sstevel@tonic-gate return;
41650Sstevel@tonic-gate }
41660Sstevel@tonic-gate if ((cptr->options & O_TAR)) {
41670Sstevel@tonic-gate strcpy(namebuf, fnbuf);
41680Sstevel@tonic-gate if (strlcat(namebuf, "/.notar", sizeof(namebuf)) >=
41690Sstevel@tonic-gate sizeof(namebuf))
41700Sstevel@tonic-gate continue;
41710Sstevel@tonic-gate if (stat(namebuf, &junk) == 0) {
41720Sstevel@tonic-gate if (log_security)
41730Sstevel@tonic-gate if (anonymous)
41740Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to tar %s (.notar)",
41750Sstevel@tonic-gate guestpw, remoteident, realname);
41760Sstevel@tonic-gate else
41770Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to tar %s (.notar)",
41780Sstevel@tonic-gate pw->pw_name, remoteident, realname);
41790Sstevel@tonic-gate reply(550, "Sorry, you may not TAR that directory.");
41800Sstevel@tonic-gate return;
41810Sstevel@tonic-gate }
41820Sstevel@tonic-gate }
41830Sstevel@tonic-gate }
41840Sstevel@tonic-gate /* XXX: checknoretrieve() test is weak in that if I can't get /etc/passwd
41850Sstevel@tonic-gate but I can tar /etc or /, I still win. Be careful out there... _H*
41860Sstevel@tonic-gate but you could put .notar in / and /etc and stop that ! */
41870Sstevel@tonic-gate if (use_accessfile && checknoretrieve(fnbuf)) {
41880Sstevel@tonic-gate if (log_security)
41890Sstevel@tonic-gate if (anonymous)
41900Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (noretrieve)",
41910Sstevel@tonic-gate guestpw, remoteident, realname);
41920Sstevel@tonic-gate else
41930Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to download %s (noretrieve)",
41940Sstevel@tonic-gate pw->pw_name, remoteident, realname);
41950Sstevel@tonic-gate return;
41960Sstevel@tonic-gate }
41970Sstevel@tonic-gate
41980Sstevel@tonic-gate if (S_ISREG(st.st_mode) && (!cptr->types || (cptr->types & T_REG) == 0)) {
41990Sstevel@tonic-gate reply(550, "Cannot %s plain files.", cptr->name);
42000Sstevel@tonic-gate return;
42010Sstevel@tonic-gate }
42020Sstevel@tonic-gate if (S_ISREG(st.st_mode) != 0 && S_ISDIR(st.st_mode) != 0) {
42030Sstevel@tonic-gate reply(550, "Cannot %s special files.", cptr->name);
42040Sstevel@tonic-gate return;
42050Sstevel@tonic-gate }
42060Sstevel@tonic-gate if ((!cptr->types || !(cptr->types & T_ASCII)) && deny_badasciixfer(550, ""))
42070Sstevel@tonic-gate return;
42080Sstevel@tonic-gate
42090Sstevel@tonic-gate logname = &fnbuf[0];
42100Sstevel@tonic-gate options |= cptr->options;
42110Sstevel@tonic-gate
42120Sstevel@tonic-gate strcpy(namebuf, cptr->external_cmd);
42130Sstevel@tonic-gate if ((ptr = strchr(namebuf, ' ')) != NULL)
42140Sstevel@tonic-gate *ptr = '\0';
42150Sstevel@tonic-gate if (stat(namebuf, &junk) != 0) {
42160Sstevel@tonic-gate syslog(LOG_ERR, "external command %s not found", namebuf);
42170Sstevel@tonic-gate reply(550,
42180Sstevel@tonic-gate "Local error: conversion program not found. Cannot %s file.",
42190Sstevel@tonic-gate cptr->name);
42200Sstevel@tonic-gate return;
42210Sstevel@tonic-gate }
42220Sstevel@tonic-gate (void) retrieve(cptr->external_cmd, logname);
42230Sstevel@tonic-gate
42240Sstevel@tonic-gate goto logresults; /* transfer of converted file completed */
42250Sstevel@tonic-gate }
42260Sstevel@tonic-gate }
42270Sstevel@tonic-gate
42280Sstevel@tonic-gate if (cmd == NULL) { /* no command */
42290Sstevel@tonic-gate fin = fopen(name, "r"), closefunc = fclose;
42300Sstevel@tonic-gate st.st_size = 0;
42310Sstevel@tonic-gate }
42320Sstevel@tonic-gate else { /* run command */
42330Sstevel@tonic-gate static char line[BUFSIZ];
42340Sstevel@tonic-gate
42350Sstevel@tonic-gate (void) snprintf(line, sizeof(line), cmd, name), name = line;
42360Sstevel@tonic-gate fin = ftpd_popen(line, "r", 1), closefunc = ftpd_pclose;
42370Sstevel@tonic-gate st.st_size = -1;
42380Sstevel@tonic-gate #ifdef HAVE_ST_BLKSIZE
42390Sstevel@tonic-gate st.st_blksize = BUFSIZ;
42400Sstevel@tonic-gate #endif
42410Sstevel@tonic-gate }
42420Sstevel@tonic-gate if (fin == NULL) {
42430Sstevel@tonic-gate if (errno != 0)
42440Sstevel@tonic-gate perror_reply(550, name);
42450Sstevel@tonic-gate if ((errno == EACCES) || (errno == EPERM))
42460Sstevel@tonic-gate if (log_security)
42470Sstevel@tonic-gate if (anonymous)
42480Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to download %s (file permissions)",
42490Sstevel@tonic-gate guestpw, remoteident, realname);
42500Sstevel@tonic-gate else
42510Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to download %s (file permissions)",
42520Sstevel@tonic-gate pw->pw_name, remoteident, realname);
42530Sstevel@tonic-gate return;
42540Sstevel@tonic-gate }
42550Sstevel@tonic-gate if (cmd == NULL &&
42560Sstevel@tonic-gate (fstat(fileno(fin), &st) < 0 || (st.st_mode & S_IFMT) != S_IFREG)) {
42570Sstevel@tonic-gate reply(550, "%s: not a plain file.", name);
42580Sstevel@tonic-gate goto done;
42590Sstevel@tonic-gate }
42600Sstevel@tonic-gate if (restart_point) {
42610Sstevel@tonic-gate if (type == TYPE_A) {
42620Sstevel@tonic-gate register int c;
42630Sstevel@tonic-gate off_t i;
42640Sstevel@tonic-gate
42650Sstevel@tonic-gate i = 0;
42660Sstevel@tonic-gate while (i++ < restart_point) {
42670Sstevel@tonic-gate if ((c = getc(fin)) == EOF) {
42680Sstevel@tonic-gate perror_reply(550, name);
42690Sstevel@tonic-gate goto done;
42700Sstevel@tonic-gate }
42710Sstevel@tonic-gate if (c == '\n')
42720Sstevel@tonic-gate i++;
42730Sstevel@tonic-gate }
42740Sstevel@tonic-gate }
42750Sstevel@tonic-gate else if (lseek(fileno(fin), restart_point, SEEK_SET) < 0) {
42760Sstevel@tonic-gate perror_reply(550, name);
42770Sstevel@tonic-gate goto done;
42780Sstevel@tonic-gate }
42790Sstevel@tonic-gate }
42800Sstevel@tonic-gate
42810Sstevel@tonic-gate dout = dataconn(name, st.st_size, "w");
42820Sstevel@tonic-gate if (dout == NULL)
42830Sstevel@tonic-gate goto done;
42840Sstevel@tonic-gate
42850Sstevel@tonic-gate if (sendbufsz > 0) {
42860Sstevel@tonic-gate buffersize = sendbufsz;
42870Sstevel@tonic-gate }
42880Sstevel@tonic-gate else {
42890Sstevel@tonic-gate #ifdef BUFFER_SIZE
42900Sstevel@tonic-gate buffersize = BUFFER_SIZE;
42910Sstevel@tonic-gate #elif HAVE_ST_BLKSIZE
42920Sstevel@tonic-gate buffersize = st.st_blksize * 2;
42930Sstevel@tonic-gate #else
42940Sstevel@tonic-gate buffersize = BUFSIZ * 16;
42950Sstevel@tonic-gate #endif
42960Sstevel@tonic-gate }
42970Sstevel@tonic-gate
42980Sstevel@tonic-gate #ifdef THROUGHPUT
42990Sstevel@tonic-gate TransferComplete = send_data(name, fin, dout, buffersize);
43000Sstevel@tonic-gate #else
43010Sstevel@tonic-gate TransferComplete = send_data(fin, dout, buffersize);
43020Sstevel@tonic-gate #endif
43030Sstevel@tonic-gate #ifdef SIGPIPE
43040Sstevel@tonic-gate (void) signal(SIGPIPE, SIG_IGN);
43050Sstevel@tonic-gate #endif
43060Sstevel@tonic-gate (void) fclose(dout);
43070Sstevel@tonic-gate #ifdef SIGPIPE
43080Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn);
43090Sstevel@tonic-gate #endif
43100Sstevel@tonic-gate
43110Sstevel@tonic-gate logresults:
43120Sstevel@tonic-gate if (ThisRetrieveIsData)
43130Sstevel@tonic-gate fb_realpath((logname != NULL) ? logname : name, LastFileTransferred);
43140Sstevel@tonic-gate
43150Sstevel@tonic-gate if (log_outbound_xfers && (xferlog || syslogmsg) && (cmd == NULL)) {
43160Sstevel@tonic-gate char msg[MAXXFERSTRLEN]; /* see extensions.h */
43170Sstevel@tonic-gate int xfertime = time(NULL) - start_time;
43180Sstevel@tonic-gate size_t msglen;
43190Sstevel@tonic-gate
43200Sstevel@tonic-gate if (!xfertime)
43210Sstevel@tonic-gate xfertime++;
43220Sstevel@tonic-gate
43230Sstevel@tonic-gate /* Gather transfer statistics */
43240Sstevel@tonic-gate xfervalues.filename = (logname != NULL) ? logname : name;
43250Sstevel@tonic-gate xfervalues.filesize = st.st_size;
43260Sstevel@tonic-gate xfervalues.transfer_bytes = byte_count;
43270Sstevel@tonic-gate xfervalues.transfer_direction = 'o';
43280Sstevel@tonic-gate xfervalues.transfer_type = (type == TYPE_A) ? 'a' : 'b';
43290Sstevel@tonic-gate xfervalues.transfer_time = xfertime;
43300Sstevel@tonic-gate xfervalues.restart_offset = restart_point;
43310Sstevel@tonic-gate strlcpy(xfervalues.special_action, opt_string(options), MAXSPACTCHARS);
43320Sstevel@tonic-gate xfervalues.access_mode = anonymous ? 'a' : (guest ? 'g' : 'r');
43330Sstevel@tonic-gate xfervalues.auth = authenticated;
43340Sstevel@tonic-gate xfervalues.completion = TransferComplete ? 'c' : 'i';
43350Sstevel@tonic-gate
43360Sstevel@tonic-gate xferdone = 1;
43370Sstevel@tonic-gate msg_massage(xferlog_format, msg, sizeof(msg));
43380Sstevel@tonic-gate xferdone = 0;
43390Sstevel@tonic-gate
43400Sstevel@tonic-gate /* Ensure msg always ends with '\n' */
43410Sstevel@tonic-gate msglen = strlen(msg);
43420Sstevel@tonic-gate if (msglen == sizeof(msg) - 1) {
43430Sstevel@tonic-gate msg[sizeof(msg) - 2] = '\n';
43440Sstevel@tonic-gate msg[sizeof(msg) - 1] = '\0';
43450Sstevel@tonic-gate }
43460Sstevel@tonic-gate else {
43470Sstevel@tonic-gate msg[msglen] = '\n';
43480Sstevel@tonic-gate msg[msglen + 1] = '\0';
43490Sstevel@tonic-gate }
43500Sstevel@tonic-gate
43510Sstevel@tonic-gate if (syslogmsg != 1)
43520Sstevel@tonic-gate write(xferlog, msg, strlen(msg));
43530Sstevel@tonic-gate if (syslogmsg != 0) {
43540Sstevel@tonic-gate char *msgp = msg;
43550Sstevel@tonic-gate /*
43560Sstevel@tonic-gate * To preserve the behavior when the xferlog format was fixed, skip
43570Sstevel@tonic-gate * over the time string if the message starts with the local time.
43580Sstevel@tonic-gate */
43590Sstevel@tonic-gate if (strncmp(xferlog_format, "%T ", 3) == 0)
43600Sstevel@tonic-gate msgp += 25;
43610Sstevel@tonic-gate syslog(LOG_INFO, "xferlog (send): %s", msgp);
43620Sstevel@tonic-gate }
43630Sstevel@tonic-gate }
43640Sstevel@tonic-gate data = -1;
43650Sstevel@tonic-gate pdata = -1;
43660Sstevel@tonic-gate done:
43670Sstevel@tonic-gate if (closefunc)
43680Sstevel@tonic-gate (*closefunc) (fin);
43690Sstevel@tonic-gate }
43700Sstevel@tonic-gate
43710Sstevel@tonic-gate void store(char *name, char *mode, int unique)
43720Sstevel@tonic-gate {
43730Sstevel@tonic-gate FILE *fout, *din;
43740Sstevel@tonic-gate struct stat st;
43750Sstevel@tonic-gate int TransferIncomplete = 1;
43760Sstevel@tonic-gate char *gunique(char *local);
43770Sstevel@tonic-gate time_t start_time = time(NULL);
43780Sstevel@tonic-gate
43790Sstevel@tonic-gate struct aclmember *entry = NULL;
43800Sstevel@tonic-gate
43810Sstevel@tonic-gate int fdout;
43820Sstevel@tonic-gate char realname[MAXPATHLEN];
43830Sstevel@tonic-gate
43840Sstevel@tonic-gate #ifdef OVERWRITE
43850Sstevel@tonic-gate int overwrite = 1;
43860Sstevel@tonic-gate int exists = 0;
43870Sstevel@tonic-gate
43880Sstevel@tonic-gate #endif /* OVERWRITE */
43890Sstevel@tonic-gate
43900Sstevel@tonic-gate int open_flags = 0;
43910Sstevel@tonic-gate
43920Sstevel@tonic-gate #ifdef UPLOAD
43930Sstevel@tonic-gate mode_t oldmask;
43940Sstevel@tonic-gate uid_t uid;
43950Sstevel@tonic-gate gid_t gid;
43960Sstevel@tonic-gate uid_t oldid;
43970Sstevel@tonic-gate int f_mode = -1, match_value = -1;
43980Sstevel@tonic-gate int valid = 0;
43990Sstevel@tonic-gate int ret, serrno;
44000Sstevel@tonic-gate open_flags = (O_RDWR | O_CREAT |
44010Sstevel@tonic-gate ((mode != NULL && *mode == 'a') ? O_APPEND : O_TRUNC));
44020Sstevel@tonic-gate #endif /* UPLOAD */
44030Sstevel@tonic-gate
44040Sstevel@tonic-gate wu_realpath(name, realname, chroot_path);
44050Sstevel@tonic-gate
44060Sstevel@tonic-gate #ifdef TRANSFER_COUNT
44070Sstevel@tonic-gate #ifdef TRANSFER_LIMIT
44080Sstevel@tonic-gate if (((file_limit_data_in > 0) && (file_count_in >= file_limit_data_in))
44090Sstevel@tonic-gate || ((file_limit_data_total > 0) && (file_count_total >= file_limit_data_total))
44100Sstevel@tonic-gate || ((data_limit_data_in > 0) && (data_count_in >= data_limit_data_in))
44110Sstevel@tonic-gate || ((data_limit_data_total > 0) && (data_count_total >= data_limit_data_total))) {
44120Sstevel@tonic-gate if (log_security)
44130Sstevel@tonic-gate if (anonymous)
44140Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (Transfer limits exceeded)",
44150Sstevel@tonic-gate guestpw, remoteident, realname);
44160Sstevel@tonic-gate else
44170Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to upload %s (Transfer limits exceeded)",
44180Sstevel@tonic-gate pw->pw_name, remoteident, realname);
44190Sstevel@tonic-gate reply(553, "Permission denied on server. (Transfer limits exceeded)");
44200Sstevel@tonic-gate return;
44210Sstevel@tonic-gate }
44220Sstevel@tonic-gate if (((file_limit_raw_in > 0) && (xfer_count_in >= file_limit_raw_in))
44230Sstevel@tonic-gate || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
44240Sstevel@tonic-gate || ((data_limit_raw_in > 0) && (byte_count_in >= data_limit_raw_in))
44250Sstevel@tonic-gate || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
44260Sstevel@tonic-gate if (log_security)
44270Sstevel@tonic-gate if (anonymous)
44280Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (Transfer limits exceeded)",
44290Sstevel@tonic-gate guestpw, remoteident, realname);
44300Sstevel@tonic-gate else
44310Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to upload %s (Transfer limits exceeded)",
44320Sstevel@tonic-gate pw->pw_name, remoteident, realname);
44330Sstevel@tonic-gate reply(553, "Permission denied on server. (Transfer limits exceeded)");
44340Sstevel@tonic-gate return;
44350Sstevel@tonic-gate }
44360Sstevel@tonic-gate #endif
44370Sstevel@tonic-gate #endif
44380Sstevel@tonic-gate
44390Sstevel@tonic-gate if (unique && stat(name, &st) == 0 &&
44400Sstevel@tonic-gate (name = gunique(name)) == NULL)
44410Sstevel@tonic-gate return;
44420Sstevel@tonic-gate
44430Sstevel@tonic-gate /*
44440Sstevel@tonic-gate * check the filename, is it legal?
44450Sstevel@tonic-gate */
44460Sstevel@tonic-gate if ((fn_check(name)) <= 0) {
44470Sstevel@tonic-gate if (log_security)
44480Sstevel@tonic-gate if (anonymous)
44490Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload \"%s\" (path-filter)",
44500Sstevel@tonic-gate guestpw, remoteident, realname);
44510Sstevel@tonic-gate else
44520Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to upload \"%s\" (path-filter)",
44530Sstevel@tonic-gate pw->pw_name, remoteident, realname);
44540Sstevel@tonic-gate return;
44550Sstevel@tonic-gate }
44560Sstevel@tonic-gate
44570Sstevel@tonic-gate #ifdef OVERWRITE
44580Sstevel@tonic-gate /* if overwrite permission denied and file exists... then deny the user
44590Sstevel@tonic-gate * permission to write the file. */
44600Sstevel@tonic-gate while (getaclentry("overwrite", &entry) && ARG0 && ARG1 != NULL) {
44610Sstevel@tonic-gate if (type_match(ARG1))
44620Sstevel@tonic-gate if (strcasecmp(ARG0, "yes") != 0) {
44630Sstevel@tonic-gate overwrite = 0;
44640Sstevel@tonic-gate open_flags |= O_EXCL;
44650Sstevel@tonic-gate }
44660Sstevel@tonic-gate }
44670Sstevel@tonic-gate
44680Sstevel@tonic-gate #ifdef PARANOID
44690Sstevel@tonic-gate overwrite = 0;
44700Sstevel@tonic-gate #endif
44710Sstevel@tonic-gate if (!stat(name, &st))
44720Sstevel@tonic-gate exists = 1;
44730Sstevel@tonic-gate
44740Sstevel@tonic-gate if (!overwrite && exists) {
44750Sstevel@tonic-gate if (log_security)
44760Sstevel@tonic-gate if (anonymous)
44770Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to overwrite %s",
44780Sstevel@tonic-gate guestpw, remoteident, realname);
44790Sstevel@tonic-gate else
44800Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to overwrite %s",
44810Sstevel@tonic-gate pw->pw_name, remoteident, realname);
44820Sstevel@tonic-gate reply(553, "%s: Permission denied on server. (Overwrite)", name);
44830Sstevel@tonic-gate return;
44840Sstevel@tonic-gate }
44850Sstevel@tonic-gate #endif /* OVERWRITE */
44860Sstevel@tonic-gate
44870Sstevel@tonic-gate #ifdef UPLOAD
44880Sstevel@tonic-gate if ((match_value = upl_check(name, &uid, &gid, &f_mode, &valid)) < 0) {
44890Sstevel@tonic-gate if (log_security)
44900Sstevel@tonic-gate if (anonymous)
44910Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (upload denied)",
44920Sstevel@tonic-gate guestpw, remoteident, realname);
44930Sstevel@tonic-gate else
44940Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to upload %s (upload denied)",
44950Sstevel@tonic-gate pw->pw_name, remoteident, realname);
44960Sstevel@tonic-gate return;
44970Sstevel@tonic-gate }
44980Sstevel@tonic-gate
44990Sstevel@tonic-gate /* do not truncate the file if we are restarting */
45000Sstevel@tonic-gate if (restart_point)
45010Sstevel@tonic-gate open_flags &= ~O_TRUNC;
45020Sstevel@tonic-gate
45030Sstevel@tonic-gate /* if the user has an explicit new file mode, than open the file using
45040Sstevel@tonic-gate * that mode. We must take care to not let the umask affect the file
45050Sstevel@tonic-gate * mode.
45060Sstevel@tonic-gate *
45070Sstevel@tonic-gate * else open the file and let the default umask determine the file mode. */
45080Sstevel@tonic-gate if (f_mode >= 0) {
45090Sstevel@tonic-gate oldmask = umask(0000);
45100Sstevel@tonic-gate fdout = open(name, open_flags, f_mode);
45110Sstevel@tonic-gate umask(oldmask);
45120Sstevel@tonic-gate }
45130Sstevel@tonic-gate else
45140Sstevel@tonic-gate fdout = open(name, open_flags, 0666);
45150Sstevel@tonic-gate
45160Sstevel@tonic-gate if (fdout < 0) {
45170Sstevel@tonic-gate if (log_security)
45180Sstevel@tonic-gate if (anonymous)
45190Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (permissions)",
45200Sstevel@tonic-gate guestpw, remoteident, realname);
45210Sstevel@tonic-gate else
45220Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to upload %s (permissions)",
45230Sstevel@tonic-gate pw->pw_name, remoteident, realname);
45240Sstevel@tonic-gate perror_reply(553, name);
45250Sstevel@tonic-gate return;
45260Sstevel@tonic-gate }
45270Sstevel@tonic-gate /* if we have a uid and gid, then use them. */
45280Sstevel@tonic-gate
45290Sstevel@tonic-gate #ifdef OVERWRITE
45300Sstevel@tonic-gate if (!exists)
45310Sstevel@tonic-gate #endif
45320Sstevel@tonic-gate if (valid > 0) {
45330Sstevel@tonic-gate oldid = geteuid();
45340Sstevel@tonic-gate if (uid != 0)
45350Sstevel@tonic-gate (void) seteuid((uid_t) uid);
45360Sstevel@tonic-gate if ((uid == 0) || ((fchown(fdout, uid, gid)) < 0)) {
45370Sstevel@tonic-gate chown_priv_on(0);
45380Sstevel@tonic-gate ret = fchown(fdout, uid, gid);
45390Sstevel@tonic-gate serrno = errno;
45400Sstevel@tonic-gate chown_priv_off(oldid);
45410Sstevel@tonic-gate if (ret < 0) {
45420Sstevel@tonic-gate errno = serrno;
45430Sstevel@tonic-gate perror_reply(550, "fchown");
45440Sstevel@tonic-gate return;
45450Sstevel@tonic-gate }
45460Sstevel@tonic-gate }
45470Sstevel@tonic-gate else
45480Sstevel@tonic-gate (void) seteuid(oldid);
45490Sstevel@tonic-gate }
45500Sstevel@tonic-gate #endif /* UPLOAD */
45510Sstevel@tonic-gate
45520Sstevel@tonic-gate if (restart_point && (open_flags & O_APPEND) == 0)
45530Sstevel@tonic-gate mode = "r+";
45540Sstevel@tonic-gate
45550Sstevel@tonic-gate #ifdef UPLOAD
45560Sstevel@tonic-gate fout = fdopen(fdout, mode);
45570Sstevel@tonic-gate #else
45580Sstevel@tonic-gate fout = fopen(name, mode);
45590Sstevel@tonic-gate #endif /* UPLOAD */
45600Sstevel@tonic-gate
45610Sstevel@tonic-gate if (fout == NULL) {
45620Sstevel@tonic-gate if (log_security)
45630Sstevel@tonic-gate if (anonymous)
45640Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to upload %s (permissions)",
45650Sstevel@tonic-gate guestpw, remoteident, realname);
45660Sstevel@tonic-gate else
45670Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to upload %s (permissions)",
45680Sstevel@tonic-gate pw->pw_name, remoteident, realname);
45690Sstevel@tonic-gate perror_reply(553, name);
45700Sstevel@tonic-gate return;
45710Sstevel@tonic-gate }
45720Sstevel@tonic-gate if (restart_point && (open_flags & O_APPEND) == 0) {
45730Sstevel@tonic-gate if (type == TYPE_A) {
45740Sstevel@tonic-gate register int c;
45750Sstevel@tonic-gate off_t i;
45760Sstevel@tonic-gate
45770Sstevel@tonic-gate i = 0;
45780Sstevel@tonic-gate while (i++ < restart_point) {
45790Sstevel@tonic-gate if ((c = getc(fout)) == EOF) {
45800Sstevel@tonic-gate perror_reply(550, name);
45810Sstevel@tonic-gate goto done;
45820Sstevel@tonic-gate }
45830Sstevel@tonic-gate if (c == '\n')
45840Sstevel@tonic-gate i++;
45850Sstevel@tonic-gate }
45860Sstevel@tonic-gate /* We must do this seek to "current" position because we are
45870Sstevel@tonic-gate * changing from reading to writing. */
45880Sstevel@tonic-gate #if _FILE_OFFSET_BITS == 64
45890Sstevel@tonic-gate if (fseeko(fout, 0L, SEEK_CUR) < 0) {
45900Sstevel@tonic-gate #else
45910Sstevel@tonic-gate if (fseek(fout, 0L, SEEK_CUR) < 0) {
45920Sstevel@tonic-gate #endif
45930Sstevel@tonic-gate perror_reply(550, name);
45940Sstevel@tonic-gate goto done;
45950Sstevel@tonic-gate }
45960Sstevel@tonic-gate }
45970Sstevel@tonic-gate else if (lseek(fileno(fout), restart_point, SEEK_SET) < 0) {
45980Sstevel@tonic-gate perror_reply(550, name);
45990Sstevel@tonic-gate goto done;
46000Sstevel@tonic-gate }
46010Sstevel@tonic-gate }
46020Sstevel@tonic-gate din = dataconn(name, (off_t) - 1, "r");
46030Sstevel@tonic-gate if (din == NULL)
46040Sstevel@tonic-gate goto done;
46050Sstevel@tonic-gate TransferIncomplete = receive_data(din, fout);
46060Sstevel@tonic-gate
46070Sstevel@tonic-gate if (fstat(fileno(fout), &st) != 0) {
46080Sstevel@tonic-gate /* shouldn't fail, but just in case */
46090Sstevel@tonic-gate st.st_size = -1;
46100Sstevel@tonic-gate }
46110Sstevel@tonic-gate (void) fclose(din);
46120Sstevel@tonic-gate if (TransferIncomplete == 0) {
46130Sstevel@tonic-gate if (unique)
46140Sstevel@tonic-gate reply(226, "Transfer complete (unique file name:%s).", name);
46150Sstevel@tonic-gate else
46160Sstevel@tonic-gate reply(226, "Transfer complete.");
46170Sstevel@tonic-gate }
46180Sstevel@tonic-gate
46190Sstevel@tonic-gate fb_realpath(name, LastFileTransferred);
46200Sstevel@tonic-gate
46210Sstevel@tonic-gate #ifdef MAIL_ADMIN
46220Sstevel@tonic-gate if (anonymous && incmails > 0) {
46230Sstevel@tonic-gate FILE *sck = NULL;
46240Sstevel@tonic-gate
46250Sstevel@tonic-gate unsigned char temp = 0, temp2 = 0;
46260Sstevel@tonic-gate char pathname[MAXPATHLEN];
46270Sstevel@tonic-gate while ((temp < mailservers) && (sck == NULL))
46280Sstevel@tonic-gate sck = SockOpen(mailserver[temp++], 25);
46290Sstevel@tonic-gate if (sck == NULL) {
46300Sstevel@tonic-gate syslog(LOG_ERR, "Can't connect to a mailserver.");
46310Sstevel@tonic-gate goto mailfail;
46320Sstevel@tonic-gate }
46330Sstevel@tonic-gate if (Reply(sck) != 220) {
46340Sstevel@tonic-gate syslog(LOG_ERR, "Mailserver failed to initiate contact.");
46350Sstevel@tonic-gate goto mailfail;
46360Sstevel@tonic-gate }
46370Sstevel@tonic-gate if (Send(sck, "HELO localhost\r\n") != 250) {
46380Sstevel@tonic-gate syslog(LOG_ERR, "Mailserver doesn't understand HELO.");
46390Sstevel@tonic-gate goto mailfail;
46400Sstevel@tonic-gate }
46410Sstevel@tonic-gate if (Send(sck, "MAIL FROM: <%s>\r\n", email(mailfrom)) != 250) {
46420Sstevel@tonic-gate syslog(LOG_ERR, "Mailserver didn't accept MAIL FROM.");
46430Sstevel@tonic-gate goto mailfail;
46440Sstevel@tonic-gate }
46450Sstevel@tonic-gate for (temp = 0; temp < incmails; temp++) {
46460Sstevel@tonic-gate if (Send(sck, "RCPT TO: <%s>\r\n", email(incmail[temp])) == 250)
46470Sstevel@tonic-gate temp2++;
46480Sstevel@tonic-gate }
46490Sstevel@tonic-gate if (temp2 == 0) {
46500Sstevel@tonic-gate syslog(LOG_ERR, "Mailserver didn't accept any RCPT TO.");
46510Sstevel@tonic-gate goto mailfail;
46520Sstevel@tonic-gate }
46530Sstevel@tonic-gate if (Send(sck, "DATA\r\n") != 354) {
46540Sstevel@tonic-gate syslog(LOG_ERR, "Mailserver didn't accept DATA.");
46550Sstevel@tonic-gate goto mailfail;
46560Sstevel@tonic-gate }
46570Sstevel@tonic-gate SockPrintf(sck, "From: wu-ftpd <%s>\r\n", mailfrom);
46580Sstevel@tonic-gate SockPrintf(sck, "Subject: New file uploaded: %s\r\n\r\n", name);
46590Sstevel@tonic-gate fb_realpath(name, pathname);
46600Sstevel@tonic-gate SockPrintf(sck, "%s uploaded %s from %s.\r\nFile size is %" L_FORMAT ".\r\nPlease move the file where it belongs.\r\n", guestpw, pathname, remotehost, st.st_size);
46610Sstevel@tonic-gate if (Send(sck, ".\r\n") != 250)
46620Sstevel@tonic-gate syslog(LOG_ERR, "Message rejected by mailserver.");
46630Sstevel@tonic-gate if (Send(sck, "QUIT\r\n") != 221)
46640Sstevel@tonic-gate syslog(LOG_ERR, "Mailserver didn't accept QUIT.");
46650Sstevel@tonic-gate mailfail:
46660Sstevel@tonic-gate if (sck != NULL)
46670Sstevel@tonic-gate fclose(sck);
46680Sstevel@tonic-gate }
46690Sstevel@tonic-gate #endif /* MAIL_ADMIN */
46700Sstevel@tonic-gate
46710Sstevel@tonic-gate if (log_incoming_xfers && (xferlog || syslogmsg)) {
46720Sstevel@tonic-gate char msg[MAXXFERSTRLEN]; /* see extensions.h */
46730Sstevel@tonic-gate int xfertime = time(NULL) - start_time;
46740Sstevel@tonic-gate size_t msglen;
46750Sstevel@tonic-gate
46760Sstevel@tonic-gate if (!xfertime)
46770Sstevel@tonic-gate xfertime++;
46780Sstevel@tonic-gate
46790Sstevel@tonic-gate /* Gather transfer statistics */
46800Sstevel@tonic-gate xfervalues.filename = name;
46810Sstevel@tonic-gate xfervalues.filesize = st.st_size;
46820Sstevel@tonic-gate xfervalues.transfer_bytes = byte_count;
46830Sstevel@tonic-gate xfervalues.transfer_direction = 'i';
46840Sstevel@tonic-gate xfervalues.transfer_type = (type == TYPE_A) ? 'a' : 'b';
46850Sstevel@tonic-gate xfervalues.transfer_time = xfertime;
46860Sstevel@tonic-gate xfervalues.restart_offset = restart_point;
46870Sstevel@tonic-gate strlcpy(xfervalues.special_action, opt_string(0), MAXSPACTCHARS);
46880Sstevel@tonic-gate xfervalues.access_mode = anonymous ? 'a' : (guest ? 'g' : 'r');
46890Sstevel@tonic-gate xfervalues.auth = authenticated;
46900Sstevel@tonic-gate xfervalues.completion = TransferIncomplete ? 'i' : 'c';
46910Sstevel@tonic-gate
46920Sstevel@tonic-gate xferdone = 1;
46930Sstevel@tonic-gate msg_massage(xferlog_format, msg, sizeof(msg));
46940Sstevel@tonic-gate xferdone = 0;
46950Sstevel@tonic-gate
46960Sstevel@tonic-gate /* Ensure msg always ends with '\n' */
46970Sstevel@tonic-gate msglen = strlen(msg);
46980Sstevel@tonic-gate if (msglen == sizeof(msg) - 1) {
46990Sstevel@tonic-gate msg[sizeof(msg) - 2] = '\n';
47000Sstevel@tonic-gate msg[sizeof(msg) - 1] = '\0';
47010Sstevel@tonic-gate }
47020Sstevel@tonic-gate else {
47030Sstevel@tonic-gate msg[msglen] = '\n';
47040Sstevel@tonic-gate msg[msglen + 1] = '\0';
47050Sstevel@tonic-gate }
47060Sstevel@tonic-gate
47070Sstevel@tonic-gate if (syslogmsg != 1)
47080Sstevel@tonic-gate write(xferlog, msg, strlen(msg));
47090Sstevel@tonic-gate if (syslogmsg != 0) {
47100Sstevel@tonic-gate char *msgp = msg;
47110Sstevel@tonic-gate /*
47120Sstevel@tonic-gate * To preserve the behavior when the xferlog format was fixed, skip
47130Sstevel@tonic-gate * over the time string if the message starts with the local time.
47140Sstevel@tonic-gate */
47150Sstevel@tonic-gate if (strncmp(xferlog_format, "%T ", 3) == 0)
47160Sstevel@tonic-gate msgp += 25;
47170Sstevel@tonic-gate syslog(LOG_INFO, "xferlog (recv): %s", msgp);
47180Sstevel@tonic-gate }
47190Sstevel@tonic-gate }
47200Sstevel@tonic-gate data = -1;
47210Sstevel@tonic-gate pdata = -1;
47220Sstevel@tonic-gate done:
47230Sstevel@tonic-gate (void) fclose(fout);
47240Sstevel@tonic-gate }
47250Sstevel@tonic-gate
47260Sstevel@tonic-gate FILE *getdatasock(char *mode)
47270Sstevel@tonic-gate {
47280Sstevel@tonic-gate int s, on = 1, tries;
47290Sstevel@tonic-gate
47300Sstevel@tonic-gate if (data >= 0)
47310Sstevel@tonic-gate return (fdopen(data, mode));
47320Sstevel@tonic-gate port_priv_on(0);
47330Sstevel@tonic-gate s = socket(SOCK_FAMILY(data_dest), SOCK_STREAM, 0);
47340Sstevel@tonic-gate if (s < 0)
47350Sstevel@tonic-gate goto bad;
47360Sstevel@tonic-gate if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
47370Sstevel@tonic-gate (char *) &on, sizeof(on)) < 0)
47380Sstevel@tonic-gate goto bad;
47390Sstevel@tonic-gate if (keepalive)
47400Sstevel@tonic-gate (void) setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
47410Sstevel@tonic-gate if (TCPwindowsize)
47420Sstevel@tonic-gate (void) setsockopt(s, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
47430Sstevel@tonic-gate (char *) &TCPwindowsize, sizeof(TCPwindowsize));
47440Sstevel@tonic-gate /* anchor socket to avoid multi-homing problems */
47450Sstevel@tonic-gate #ifdef INET6
47460Sstevel@tonic-gate if (SOCK_FAMILY(data_dest) == SOCK_FAMILY(ctrl_addr))
47470Sstevel@tonic-gate data_source = ctrl_addr;
47480Sstevel@tonic-gate else if ((SOCK_FAMILY(data_dest) == AF_INET) && ctrl_v4mapped) {
47490Sstevel@tonic-gate struct sockaddr_in6 *ctrl_sin6 = (struct sockaddr_in6 *)&ctrl_addr;
47500Sstevel@tonic-gate struct sockaddr_in *data_sin = (struct sockaddr_in *)&data_source;
47510Sstevel@tonic-gate
47520Sstevel@tonic-gate SET_SOCK_FAMILY(data_source, AF_INET);
47530Sstevel@tonic-gate memcpy(&data_sin->sin_addr, &ctrl_sin6->sin6_addr.s6_addr[12],
47540Sstevel@tonic-gate sizeof(struct in_addr));
47550Sstevel@tonic-gate }
47560Sstevel@tonic-gate else {
47570Sstevel@tonic-gate memset(&data_source, 0, sizeof(struct sockaddr_in6));
47580Sstevel@tonic-gate SET_SOCK_FAMILY(data_source, SOCK_FAMILY(data_dest));
47590Sstevel@tonic-gate SET_SOCK_ADDR_ANY(data_source);
47600Sstevel@tonic-gate }
47610Sstevel@tonic-gate #else
47620Sstevel@tonic-gate data_source = ctrl_addr;
47630Sstevel@tonic-gate #endif
47640Sstevel@tonic-gate SET_SOCK_PORT(data_source, data_port);
47650Sstevel@tonic-gate
47660Sstevel@tonic-gate #if defined(VIRTUAL) && defined(CANT_BIND) /* can't bind to virtual address */
47670Sstevel@tonic-gate SET_SOCK_ADDR_ANY(data_source);
47680Sstevel@tonic-gate #endif
47690Sstevel@tonic-gate for (tries = 1;; tries++) {
47700Sstevel@tonic-gate if (bind(s, (struct sockaddr *) &data_source, SOCK_LEN(data_source)) >= 0)
47710Sstevel@tonic-gate break;
47720Sstevel@tonic-gate if (errno != EADDRINUSE || tries > 10)
47730Sstevel@tonic-gate goto bad;
47740Sstevel@tonic-gate sleep(tries);
47750Sstevel@tonic-gate }
47760Sstevel@tonic-gate #if defined(M_UNIX) && !defined(_M_UNIX) /* bug in old TCP/IP release */
47770Sstevel@tonic-gate {
47780Sstevel@tonic-gate struct linger li;
47790Sstevel@tonic-gate li.l_onoff = 1;
47800Sstevel@tonic-gate li.l_linger = 900;
47810Sstevel@tonic-gate if (setsockopt(s, SOL_SOCKET, SO_LINGER,
47820Sstevel@tonic-gate (char *) &li, sizeof(struct linger)) < 0) {
47830Sstevel@tonic-gate syslog(LOG_WARNING, "setsockopt (SO_LINGER): %m");
47840Sstevel@tonic-gate goto bad;
47850Sstevel@tonic-gate }
47860Sstevel@tonic-gate }
47870Sstevel@tonic-gate #endif
47880Sstevel@tonic-gate port_priv_off((uid_t) pw->pw_uid);
47890Sstevel@tonic-gate
47900Sstevel@tonic-gate #ifdef INET6
47910Sstevel@tonic-gate /* IP_TOS is an IPv4 socket option */
47920Sstevel@tonic-gate if (SOCK_FAMILY(data_source) == AF_INET)
47930Sstevel@tonic-gate #endif
47940Sstevel@tonic-gate if ((on = IPClassOfService("data")) >= 0) {
47950Sstevel@tonic-gate if (setsockopt(s, IPPROTO_IP, IP_TOS, (char *) &on, sizeof(int)) < 0)
47960Sstevel@tonic-gate syslog(LOG_WARNING, "setsockopt (IP_TOS): %m");
47970Sstevel@tonic-gate }
47980Sstevel@tonic-gate #ifdef TCP_NOPUSH
47990Sstevel@tonic-gate /*
48000Sstevel@tonic-gate * Turn off push flag to keep sender TCP from sending short packets
48010Sstevel@tonic-gate * at the boundaries of each write(). Should probably do a SO_SNDBUF
48020Sstevel@tonic-gate * to set the send buffer size as well, but that may not be desirable
48030Sstevel@tonic-gate * in heavy-load situations.
48040Sstevel@tonic-gate */
48050Sstevel@tonic-gate on = 1;
48060Sstevel@tonic-gate if (setsockopt(s, IPPROTO_TCP, TCP_NOPUSH, (char *) &on, sizeof on) < 0)
48070Sstevel@tonic-gate syslog(LOG_WARNING, "setsockopt (TCP_NOPUSH): %m");
48080Sstevel@tonic-gate #endif
48090Sstevel@tonic-gate
48100Sstevel@tonic-gate return (fdopen(s, mode));
48110Sstevel@tonic-gate bad:
48120Sstevel@tonic-gate on = errno; /* hold errno for return */
48130Sstevel@tonic-gate port_priv_off((uid_t) pw->pw_uid);
48140Sstevel@tonic-gate if (s != -1)
48150Sstevel@tonic-gate (void) close(s);
48160Sstevel@tonic-gate errno = on;
48170Sstevel@tonic-gate return (NULL);
48180Sstevel@tonic-gate }
48190Sstevel@tonic-gate
48200Sstevel@tonic-gate FILE *dataconn(char *name, off_t size, char *mode)
48210Sstevel@tonic-gate {
48220Sstevel@tonic-gate char sizebuf[32];
48230Sstevel@tonic-gate FILE *file;
48240Sstevel@tonic-gate int retry = 0;
48250Sstevel@tonic-gate int on = 1;
48260Sstevel@tonic-gate int cval, serrno;
48270Sstevel@tonic-gate int cos;
48280Sstevel@tonic-gate #ifdef THROUGHPUT
48290Sstevel@tonic-gate int bps;
48300Sstevel@tonic-gate double bpsmult;
48310Sstevel@tonic-gate #endif
48320Sstevel@tonic-gate
48330Sstevel@tonic-gate file_size = size;
48340Sstevel@tonic-gate byte_count = 0;
48350Sstevel@tonic-gate if (size != (off_t) - 1)
48360Sstevel@tonic-gate (void) sprintf(sizebuf, " (%" L_FORMAT " bytes)", size);
48370Sstevel@tonic-gate else
48380Sstevel@tonic-gate (void) strcpy(sizebuf, "");
48390Sstevel@tonic-gate if (pdata >= 0) {
48400Sstevel@tonic-gate struct SOCKSTORAGE from;
48410Sstevel@tonic-gate char dataaddr[MAXHOSTNAMELEN];
48420Sstevel@tonic-gate #if defined(UNIXWARE) || defined(AIX)
48430Sstevel@tonic-gate size_t fromlen = sizeof(from);
48440Sstevel@tonic-gate #else
48450Sstevel@tonic-gate int fromlen = sizeof(from);
48460Sstevel@tonic-gate #endif
48470Sstevel@tonic-gate int s;
48480Sstevel@tonic-gate #ifdef FD_ZERO
48490Sstevel@tonic-gate int rv;
48500Sstevel@tonic-gate #endif
48510Sstevel@tonic-gate
48520Sstevel@tonic-gate if (keepalive)
48530Sstevel@tonic-gate (void) setsockopt(pdata, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
48540Sstevel@tonic-gate if (TCPwindowsize)
48550Sstevel@tonic-gate (void) setsockopt(pdata, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
48560Sstevel@tonic-gate (char *) &TCPwindowsize, sizeof(TCPwindowsize));
48570Sstevel@tonic-gate #ifdef FD_ZERO
48580Sstevel@tonic-gate do {
48590Sstevel@tonic-gate struct timeval timeout;
48600Sstevel@tonic-gate fd_set set;
48610Sstevel@tonic-gate
48620Sstevel@tonic-gate FD_ZERO(&set);
48630Sstevel@tonic-gate FD_SET(pdata, &set);
48640Sstevel@tonic-gate
48650Sstevel@tonic-gate timeout.tv_usec = 0;
48660Sstevel@tonic-gate timeout.tv_sec = timeout_accept;
48670Sstevel@tonic-gate #ifdef HPUX_SELECT
48680Sstevel@tonic-gate rv = select(pdata + 1, (int *) &set, NULL, NULL, &timeout);
48690Sstevel@tonic-gate #else
48700Sstevel@tonic-gate rv = select(pdata + 1, &set, (fd_set *) 0, (fd_set *) 0,
48710Sstevel@tonic-gate (struct timeval *) &timeout);
48720Sstevel@tonic-gate #endif
48730Sstevel@tonic-gate } while ((rv == -1) && (errno == EINTR));
48740Sstevel@tonic-gate if ((rv != -1) && (rv != 0))
48750Sstevel@tonic-gate s = accept(pdata, (struct sockaddr *) &from, &fromlen);
48760Sstevel@tonic-gate else
48770Sstevel@tonic-gate s = -1;
48780Sstevel@tonic-gate #else /* FD_ZERO */
48790Sstevel@tonic-gate (void) signal(SIGALRM, alarm_signal);
48800Sstevel@tonic-gate alarm(timeout_accept);
48810Sstevel@tonic-gate s = accept(pdata, (struct sockaddr *) &from, &fromlen);
48820Sstevel@tonic-gate alarm(0);
48830Sstevel@tonic-gate #endif
48840Sstevel@tonic-gate if (s == -1) {
48850Sstevel@tonic-gate reply(425, "Can't open data connection.");
48860Sstevel@tonic-gate (void) close(pdata);
48870Sstevel@tonic-gate pdata = -1;
48880Sstevel@tonic-gate return (NULL);
48890Sstevel@tonic-gate }
48900Sstevel@tonic-gate (void) close(pdata);
48910Sstevel@tonic-gate pdata = s;
48920Sstevel@tonic-gate #ifdef INET6
48930Sstevel@tonic-gate /* IP_TOS is an IPv4 socket option */
48940Sstevel@tonic-gate if (SOCK_FAMILY(from) == AF_INET)
48950Sstevel@tonic-gate #endif
48960Sstevel@tonic-gate if ((cos = IPClassOfService("data")) >= 0)
48970Sstevel@tonic-gate (void) setsockopt(s, IPPROTO_IP, IP_TOS, (char *)&cos, sizeof(int));
48980Sstevel@tonic-gate (void) strncpy(dataaddr, inet_stop(&from), sizeof(dataaddr));
48990Sstevel@tonic-gate if (!pasv_allowed(dataaddr))
49000Sstevel@tonic-gate if (strcasecmp(dataaddr, remoteaddr) != 0) {
49010Sstevel@tonic-gate /*
49020Sstevel@tonic-gate * This will log when data connection comes from an address different
49030Sstevel@tonic-gate * than the control connection.
49040Sstevel@tonic-gate */
49050Sstevel@tonic-gate #ifdef FIGHT_PASV_PORT_RACE
49060Sstevel@tonic-gate syslog(LOG_ERR, "%s of %s: data connect from %s for %s%s",
49070Sstevel@tonic-gate anonymous ? guestpw : pw->pw_name, remoteident,
49080Sstevel@tonic-gate dataaddr, name, sizebuf);
49090Sstevel@tonic-gate reply(425, "Possible PASV port theft, cannot open data connection.");
49100Sstevel@tonic-gate (void) close(pdata);
49110Sstevel@tonic-gate pdata = -1;
49120Sstevel@tonic-gate return (NULL);
49130Sstevel@tonic-gate #else
49140Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s: data connect from %s for %s%s",
49150Sstevel@tonic-gate anonymous ? guestpw : pw->pw_name, remoteident,
49160Sstevel@tonic-gate dataaddr, name, sizebuf);
49170Sstevel@tonic-gate #endif
49180Sstevel@tonic-gate }
49190Sstevel@tonic-gate #ifdef THROUGHPUT
49200Sstevel@tonic-gate throughput_calc(name, &bps, &bpsmult);
49210Sstevel@tonic-gate if (bps != -1) {
49220Sstevel@tonic-gate lreply(150, "Opening %s mode data connection for %s%s.",
49230Sstevel@tonic-gate type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
49240Sstevel@tonic-gate reply(150, "Restricting network throughput to %d bytes/s.", bps);
49250Sstevel@tonic-gate }
49260Sstevel@tonic-gate else
49270Sstevel@tonic-gate #endif
49280Sstevel@tonic-gate reply(150, "Opening %s mode data connection for %s%s.",
49290Sstevel@tonic-gate type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
49300Sstevel@tonic-gate return (fdopen(pdata, mode));
49310Sstevel@tonic-gate }
49320Sstevel@tonic-gate if (data >= 0) {
49330Sstevel@tonic-gate reply(125, "Using existing data connection for %s%s.",
49340Sstevel@tonic-gate name, sizebuf);
49350Sstevel@tonic-gate usedefault = 1;
49360Sstevel@tonic-gate return (fdopen(data, mode));
49370Sstevel@tonic-gate }
49380Sstevel@tonic-gate if (usedefault)
49390Sstevel@tonic-gate data_dest = his_addr;
49400Sstevel@tonic-gate if (SOCK_PORT(data_dest) == 0) {
49410Sstevel@tonic-gate reply(500, "Can't build data connection: no PORT specified");
49420Sstevel@tonic-gate return (NULL);
49430Sstevel@tonic-gate }
49440Sstevel@tonic-gate usedefault = 1;
49450Sstevel@tonic-gate do {
49460Sstevel@tonic-gate file = getdatasock(mode);
49470Sstevel@tonic-gate if (file == NULL) {
49480Sstevel@tonic-gate reply(425, "Can't create data socket (%s,%d): %s.",
49490Sstevel@tonic-gate inet_stop(&data_source), ntohs(SOCK_PORT(data_source)),
49500Sstevel@tonic-gate strerror(errno));
49510Sstevel@tonic-gate return (NULL);
49520Sstevel@tonic-gate }
49530Sstevel@tonic-gate data = fileno(file);
49540Sstevel@tonic-gate (void) signal(SIGALRM, alarm_signal);
49550Sstevel@tonic-gate alarm(timeout_connect);
49560Sstevel@tonic-gate cval = connect(data, (struct sockaddr *) &data_dest,
49570Sstevel@tonic-gate SOCK_LEN(data_dest));
49580Sstevel@tonic-gate serrno = errno;
49590Sstevel@tonic-gate alarm(0);
49600Sstevel@tonic-gate if (cval == -1) {
49610Sstevel@tonic-gate /*
49620Sstevel@tonic-gate * When connect fails, the state of the socket is unspecified so
49630Sstevel@tonic-gate * it should be closed and a new socket created for each connection
49640Sstevel@tonic-gate * attempt. This also prevents denial of service problems when
49650Sstevel@tonic-gate * running on operating systems that only allow one non-connected
49660Sstevel@tonic-gate * socket bound to the same local address.
49670Sstevel@tonic-gate */
49680Sstevel@tonic-gate (void) fclose(file);
49690Sstevel@tonic-gate data = -1;
49700Sstevel@tonic-gate errno = serrno;
49710Sstevel@tonic-gate if ((errno == EADDRINUSE || errno == EINTR) && retry < swaitmax) {
49720Sstevel@tonic-gate sleep((unsigned) swaitint);
49730Sstevel@tonic-gate retry += swaitint;
49740Sstevel@tonic-gate }
49750Sstevel@tonic-gate else {
49760Sstevel@tonic-gate perror_reply(425, "Can't build data connection");
49770Sstevel@tonic-gate return (NULL);
49780Sstevel@tonic-gate }
49790Sstevel@tonic-gate }
49800Sstevel@tonic-gate } while (cval == -1);
49810Sstevel@tonic-gate if (keepalive)
49820Sstevel@tonic-gate (void) setsockopt(data, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
49830Sstevel@tonic-gate if (TCPwindowsize)
49840Sstevel@tonic-gate (void) setsockopt(data, SOL_SOCKET, (*mode == 'w' ? SO_SNDBUF : SO_RCVBUF),
49850Sstevel@tonic-gate (char *) &TCPwindowsize, sizeof(TCPwindowsize));
49860Sstevel@tonic-gate #ifdef THROUGHPUT
49870Sstevel@tonic-gate throughput_calc(name, &bps, &bpsmult);
49880Sstevel@tonic-gate if (bps != -1) {
49890Sstevel@tonic-gate lreply(150, "Opening %s mode data connection for %s%s.",
49900Sstevel@tonic-gate type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
49910Sstevel@tonic-gate reply(150, "Restricting network throughput to %d bytes/s.", bps);
49920Sstevel@tonic-gate }
49930Sstevel@tonic-gate else
49940Sstevel@tonic-gate #endif
49950Sstevel@tonic-gate reply(150, "Opening %s mode data connection for %s%s.",
49960Sstevel@tonic-gate type == TYPE_A ? "ASCII" : "BINARY", name, sizebuf);
49970Sstevel@tonic-gate return (file);
49980Sstevel@tonic-gate }
49990Sstevel@tonic-gate
50000Sstevel@tonic-gate /* Tranfer the contents of "instr" to "outstr" peer using the appropriate
50010Sstevel@tonic-gate * encapsulation of the data subject to Mode, Structure, and Type.
50020Sstevel@tonic-gate *
50030Sstevel@tonic-gate * NB: Form isn't handled. */
50040Sstevel@tonic-gate
50050Sstevel@tonic-gate int
50060Sstevel@tonic-gate #ifdef THROUGHPUT
50070Sstevel@tonic-gate send_data(char *name, FILE *instr, FILE *outstr, size_t blksize)
50080Sstevel@tonic-gate #else
50090Sstevel@tonic-gate send_data(FILE *instr, FILE *outstr, size_t blksize)
50100Sstevel@tonic-gate #endif
50110Sstevel@tonic-gate {
50120Sstevel@tonic-gate register int c, cnt = 0;
50130Sstevel@tonic-gate static char *buf;
50140Sstevel@tonic-gate int netfd, filefd;
50150Sstevel@tonic-gate #ifdef THROUGHPUT
50160Sstevel@tonic-gate int bps;
50170Sstevel@tonic-gate double bpsmult;
50180Sstevel@tonic-gate time_t t1, t2;
50190Sstevel@tonic-gate #endif
50200Sstevel@tonic-gate #ifdef SENDFILE
50210Sstevel@tonic-gate int use_sf = 0;
50220Sstevel@tonic-gate size_t xferred;
50230Sstevel@tonic-gate struct stat st;
50240Sstevel@tonic-gate struct sendfilevec sfv;
50250Sstevel@tonic-gate #endif
50260Sstevel@tonic-gate
50270Sstevel@tonic-gate buf = NULL;
50280Sstevel@tonic-gate if (wu_setjmp(urgcatch)) {
50290Sstevel@tonic-gate draconian_FILE = NULL;
50300Sstevel@tonic-gate alarm(0);
50310Sstevel@tonic-gate transflag = 0;
50320Sstevel@tonic-gate #ifdef SIGPIPE
50330Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn);
50340Sstevel@tonic-gate #endif
50350Sstevel@tonic-gate if (buf)
50360Sstevel@tonic-gate (void) free(buf);
50370Sstevel@tonic-gate retrieve_is_data = 1;
50380Sstevel@tonic-gate return (0);
50390Sstevel@tonic-gate }
50400Sstevel@tonic-gate transflag++;
50410Sstevel@tonic-gate
50420Sstevel@tonic-gate #ifdef THROUGHPUT
50430Sstevel@tonic-gate throughput_calc(name, &bps, &bpsmult);
50440Sstevel@tonic-gate #endif
50450Sstevel@tonic-gate
50460Sstevel@tonic-gate switch (type) {
50470Sstevel@tonic-gate
50480Sstevel@tonic-gate case TYPE_A:
50490Sstevel@tonic-gate #ifdef SIGPIPE
50500Sstevel@tonic-gate /*
50510Sstevel@tonic-gate * Ignore SIGPIPE while sending data, necessary so lostconn() isn't
50520Sstevel@tonic-gate * called if we write to the data connection after the client has
50530Sstevel@tonic-gate * closed it.
50540Sstevel@tonic-gate */
50550Sstevel@tonic-gate (void) signal(SIGPIPE, SIG_IGN);
50560Sstevel@tonic-gate #endif
50570Sstevel@tonic-gate draconian_FILE = outstr;
50580Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
50590Sstevel@tonic-gate alarm(timeout_data);
50600Sstevel@tonic-gate #ifdef THROUGHPUT
50610Sstevel@tonic-gate if (bps != -1)
50620Sstevel@tonic-gate t1 = time(NULL);
50630Sstevel@tonic-gate #endif
50640Sstevel@tonic-gate while ((draconian_FILE != NULL) && ((c = getc(instr)) != EOF)) {
50650Sstevel@tonic-gate if (++byte_count % 4096 == 0)
50660Sstevel@tonic-gate alarm(timeout_data);
50670Sstevel@tonic-gate if (c == '\n') {
50680Sstevel@tonic-gate if (ferror(outstr))
50690Sstevel@tonic-gate goto data_err;
50700Sstevel@tonic-gate if (++byte_count % 4096 == 0)
50710Sstevel@tonic-gate alarm(timeout_data);
50720Sstevel@tonic-gate #if defined(USE_GSS)
50730Sstevel@tonic-gate if (sec_putc('\r', outstr) != '\r')
50740Sstevel@tonic-gate goto data_err;
50750Sstevel@tonic-gate #else
50760Sstevel@tonic-gate (void) putc('\r', outstr);
50770Sstevel@tonic-gate #endif
50780Sstevel@tonic-gate #ifdef TRANSFER_COUNT
50790Sstevel@tonic-gate if (retrieve_is_data) {
50800Sstevel@tonic-gate data_count_total++;
50810Sstevel@tonic-gate data_count_out++;
50820Sstevel@tonic-gate }
50830Sstevel@tonic-gate byte_count_total++;
50840Sstevel@tonic-gate byte_count_out++;
50850Sstevel@tonic-gate #endif
50860Sstevel@tonic-gate }
50870Sstevel@tonic-gate #if defined(USE_GSS)
50880Sstevel@tonic-gate if (sec_putc(c, outstr) != c)
50890Sstevel@tonic-gate goto data_err;
50900Sstevel@tonic-gate #else
50910Sstevel@tonic-gate (void) putc(c, outstr);
50920Sstevel@tonic-gate #endif
50930Sstevel@tonic-gate
50940Sstevel@tonic-gate #ifdef TRANSFER_COUNT
50950Sstevel@tonic-gate if (retrieve_is_data) {
50960Sstevel@tonic-gate data_count_total++;
50970Sstevel@tonic-gate data_count_out++;
50980Sstevel@tonic-gate }
50990Sstevel@tonic-gate byte_count_total++;
51000Sstevel@tonic-gate byte_count_out++;
51010Sstevel@tonic-gate #endif
51020Sstevel@tonic-gate #ifdef THROUGHPUT
51030Sstevel@tonic-gate if (bps > 0 && (byte_count % bps) == 0) {
51040Sstevel@tonic-gate t2 = time(NULL);
51050Sstevel@tonic-gate if (t2 == t1)
51060Sstevel@tonic-gate sleep(1);
51070Sstevel@tonic-gate t1 = time(NULL);
51080Sstevel@tonic-gate }
51090Sstevel@tonic-gate #endif
51100Sstevel@tonic-gate }
51110Sstevel@tonic-gate #ifdef THROUGHPUT
51120Sstevel@tonic-gate if (bps != -1)
51130Sstevel@tonic-gate throughput_adjust(name);
51140Sstevel@tonic-gate #endif
51150Sstevel@tonic-gate if (draconian_FILE != NULL) {
51160Sstevel@tonic-gate alarm(timeout_data);
51170Sstevel@tonic-gate #if defined(USE_GSS)
51180Sstevel@tonic-gate if (sec_fflush(outstr) < 0)
51190Sstevel@tonic-gate goto data_err;
51200Sstevel@tonic-gate #else
51210Sstevel@tonic-gate fflush(outstr);
51220Sstevel@tonic-gate #endif /* defined(USE_GSS) */
51230Sstevel@tonic-gate }
51240Sstevel@tonic-gate if (draconian_FILE != NULL) {
51250Sstevel@tonic-gate alarm(timeout_data);
51260Sstevel@tonic-gate socket_flush_wait(outstr);
51270Sstevel@tonic-gate }
51280Sstevel@tonic-gate transflag = 0;
51290Sstevel@tonic-gate if (ferror(instr))
51300Sstevel@tonic-gate goto file_err;
51310Sstevel@tonic-gate if ((draconian_FILE == NULL) || ferror(outstr))
51320Sstevel@tonic-gate goto data_err;
51330Sstevel@tonic-gate draconian_FILE = NULL;
51340Sstevel@tonic-gate alarm(0);
51350Sstevel@tonic-gate #ifdef SIGPIPE
51360Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn);
51370Sstevel@tonic-gate #endif
51380Sstevel@tonic-gate reply(226, "Transfer complete.");
51390Sstevel@tonic-gate #ifdef TRANSFER_COUNT
51400Sstevel@tonic-gate if (retrieve_is_data) {
51410Sstevel@tonic-gate file_count_total++;
51420Sstevel@tonic-gate file_count_out++;
51430Sstevel@tonic-gate }
51440Sstevel@tonic-gate xfer_count_total++;
51450Sstevel@tonic-gate xfer_count_out++;
51460Sstevel@tonic-gate #endif
51470Sstevel@tonic-gate retrieve_is_data = 1;
51480Sstevel@tonic-gate return (1);
51490Sstevel@tonic-gate
51500Sstevel@tonic-gate case TYPE_I:
51510Sstevel@tonic-gate case TYPE_L:
51520Sstevel@tonic-gate #ifdef THROUGHPUT
51530Sstevel@tonic-gate if (bps != -1)
51540Sstevel@tonic-gate blksize = bps;
51550Sstevel@tonic-gate #endif
51560Sstevel@tonic-gate netfd = fileno(outstr);
51570Sstevel@tonic-gate filefd = fileno(instr);
51580Sstevel@tonic-gate #ifdef SENDFILE
51590Sstevel@tonic-gate /* check the input file is a regular file */
51600Sstevel@tonic-gate if ((fstat(filefd, &st) == 0) && ((st.st_mode & S_IFMT) == S_IFREG)) {
51610Sstevel@tonic-gate #if defined(USE_GSS)
51620Sstevel@tonic-gate if (gss_info.data_prot == PROT_C || !IS_GSSAUTH(cur_auth_type) ||
51630Sstevel@tonic-gate !(gss_info.authstate & GSS_ADAT_DONE))
51640Sstevel@tonic-gate #endif /* defined(USE_GSS) */
51650Sstevel@tonic-gate {
51660Sstevel@tonic-gate use_sf = 1;
51670Sstevel@tonic-gate /*
51680Sstevel@tonic-gate * Use a private sfv_flag SFV_NOWAIT to tell sendfilev(),
51690Sstevel@tonic-gate * when zero-copy is enabled, not to wait for all data to be
51700Sstevel@tonic-gate * ACKed before returning. This is important for throughput
51710Sstevel@tonic-gate * performance when sendfilev() is called to send small piece
51720Sstevel@tonic-gate * at a time.
51730Sstevel@tonic-gate */
51740Sstevel@tonic-gate sfv.sfv_flag = SFV_NOWAIT;
51750Sstevel@tonic-gate sfv.sfv_fd = filefd;
51760Sstevel@tonic-gate sfv.sfv_off = restart_point;
51770Sstevel@tonic-gate sfv.sfv_len = blksize;
51780Sstevel@tonic-gate }
51790Sstevel@tonic-gate }
51800Sstevel@tonic-gate if (use_sf == 0)
51810Sstevel@tonic-gate #endif
51820Sstevel@tonic-gate if ((buf = (char *) malloc(blksize)) == NULL) {
51830Sstevel@tonic-gate transflag = 0;
51840Sstevel@tonic-gate perror_reply(451, "Local resource failure: malloc");
51850Sstevel@tonic-gate retrieve_is_data = 1;
51860Sstevel@tonic-gate return (0);
51870Sstevel@tonic-gate }
51880Sstevel@tonic-gate #ifdef SIGPIPE
51890Sstevel@tonic-gate /*
51900Sstevel@tonic-gate * Ignore SIGPIPE while sending data, necessary so lostconn() isn't
51910Sstevel@tonic-gate * called if we write to the data connection after the client has
51920Sstevel@tonic-gate * closed it.
51930Sstevel@tonic-gate */
51940Sstevel@tonic-gate (void) signal(SIGPIPE, SIG_IGN);
51950Sstevel@tonic-gate #endif
51960Sstevel@tonic-gate draconian_FILE = outstr;
51970Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
51980Sstevel@tonic-gate alarm(timeout_data);
51990Sstevel@tonic-gate #ifdef THROUGHPUT
52000Sstevel@tonic-gate if (bps != -1)
52010Sstevel@tonic-gate t1 = time(NULL);
52020Sstevel@tonic-gate #endif
52030Sstevel@tonic-gate while ((draconian_FILE != NULL) && (
52040Sstevel@tonic-gate #ifdef SENDFILE
52050Sstevel@tonic-gate (use_sf && (cnt = sendfilev(netfd, &sfv, 1, &xferred)) > 0)
52060Sstevel@tonic-gate || (!use_sf &&
52070Sstevel@tonic-gate #endif
52080Sstevel@tonic-gate ((cnt = read(filefd, buf, blksize)) > 0 &&
52090Sstevel@tonic-gate #if defined(USE_GSS)
52100Sstevel@tonic-gate sec_write(netfd, buf, cnt) == cnt)
52110Sstevel@tonic-gate #else
52120Sstevel@tonic-gate write(netfd, buf, cnt) == cnt)
52130Sstevel@tonic-gate #endif /* defined(USE_GSS) */
52140Sstevel@tonic-gate #ifdef SENDFILE
52150Sstevel@tonic-gate )
52160Sstevel@tonic-gate #endif
52170Sstevel@tonic-gate )) {
52180Sstevel@tonic-gate alarm(timeout_data);
52190Sstevel@tonic-gate #ifdef SENDFILE
52200Sstevel@tonic-gate sfv.sfv_off += cnt;
52210Sstevel@tonic-gate #endif
52220Sstevel@tonic-gate byte_count += cnt;
52230Sstevel@tonic-gate #ifdef TRANSFER_COUNT
52240Sstevel@tonic-gate if (retrieve_is_data) {
52250Sstevel@tonic-gate #ifdef RATIO
52260Sstevel@tonic-gate if( freefile ) {
52270Sstevel@tonic-gate total_free_dl += cnt;
52280Sstevel@tonic-gate }
52290Sstevel@tonic-gate #endif /* RATIO */
52300Sstevel@tonic-gate data_count_total += cnt;
52310Sstevel@tonic-gate data_count_out += cnt;
52320Sstevel@tonic-gate }
52330Sstevel@tonic-gate byte_count_total += cnt;
52340Sstevel@tonic-gate byte_count_out += cnt;
52350Sstevel@tonic-gate #endif
52360Sstevel@tonic-gate #ifdef THROUGHPUT
52370Sstevel@tonic-gate if (bps != -1) {
52380Sstevel@tonic-gate t2 = time(NULL);
52390Sstevel@tonic-gate if (t2 == t1)
52400Sstevel@tonic-gate sleep(1);
52410Sstevel@tonic-gate t1 = time(NULL);
52420Sstevel@tonic-gate }
52430Sstevel@tonic-gate #endif /* THROUGHPUT */
52440Sstevel@tonic-gate }
52450Sstevel@tonic-gate #ifdef THROUGHPUT
52460Sstevel@tonic-gate if (bps != -1)
52470Sstevel@tonic-gate throughput_adjust(name);
52480Sstevel@tonic-gate #endif
52490Sstevel@tonic-gate #if defined(USE_GSS)
52500Sstevel@tonic-gate if (sec_fflush(outstr) < 0)
52510Sstevel@tonic-gate goto data_err;
52520Sstevel@tonic-gate #endif
52530Sstevel@tonic-gate transflag = 0;
52540Sstevel@tonic-gate if (buf)
52550Sstevel@tonic-gate (void) free(buf);
52560Sstevel@tonic-gate if (draconian_FILE != NULL) {
52570Sstevel@tonic-gate alarm(timeout_data);
52580Sstevel@tonic-gate socket_flush_wait(outstr);
52590Sstevel@tonic-gate }
52600Sstevel@tonic-gate if (cnt != 0) {
52610Sstevel@tonic-gate #ifdef SENDFILE
52620Sstevel@tonic-gate if (use_sf && cnt < 0 && errno == EPIPE)
52630Sstevel@tonic-gate goto data_err;
52640Sstevel@tonic-gate #endif
52650Sstevel@tonic-gate if (cnt < 0)
52660Sstevel@tonic-gate goto file_err;
52670Sstevel@tonic-gate goto data_err;
52680Sstevel@tonic-gate }
52690Sstevel@tonic-gate if (draconian_FILE == NULL)
52700Sstevel@tonic-gate goto data_err;
52710Sstevel@tonic-gate draconian_FILE = NULL;
52720Sstevel@tonic-gate alarm(0);
52730Sstevel@tonic-gate #ifdef SIGPIPE
52740Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn);
52750Sstevel@tonic-gate #endif
52760Sstevel@tonic-gate reply(226, "Transfer complete.");
52770Sstevel@tonic-gate #ifdef TRANSFER_COUNT
52780Sstevel@tonic-gate if (retrieve_is_data) {
52790Sstevel@tonic-gate file_count_total++;
52800Sstevel@tonic-gate file_count_out++;
52810Sstevel@tonic-gate }
52820Sstevel@tonic-gate xfer_count_total++;
52830Sstevel@tonic-gate xfer_count_out++;
52840Sstevel@tonic-gate #endif
52850Sstevel@tonic-gate retrieve_is_data = 1;
52860Sstevel@tonic-gate return (1);
52870Sstevel@tonic-gate default:
52880Sstevel@tonic-gate transflag = 0;
52890Sstevel@tonic-gate reply(550, "Unimplemented TYPE %d in send_data", type);
52900Sstevel@tonic-gate retrieve_is_data = 1;
52910Sstevel@tonic-gate return (0);
52920Sstevel@tonic-gate }
52930Sstevel@tonic-gate
52940Sstevel@tonic-gate data_err:
52950Sstevel@tonic-gate draconian_FILE = NULL;
52960Sstevel@tonic-gate alarm(0);
52970Sstevel@tonic-gate transflag = 0;
52980Sstevel@tonic-gate #ifdef SIGPIPE
52990Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn);
53000Sstevel@tonic-gate #endif
53010Sstevel@tonic-gate perror_reply(426, "Data connection");
53020Sstevel@tonic-gate retrieve_is_data = 1;
53030Sstevel@tonic-gate return (0);
53040Sstevel@tonic-gate
53050Sstevel@tonic-gate file_err:
53060Sstevel@tonic-gate draconian_FILE = NULL;
53070Sstevel@tonic-gate alarm(0);
53080Sstevel@tonic-gate transflag = 0;
53090Sstevel@tonic-gate #ifdef SIGPIPE
53100Sstevel@tonic-gate (void) signal(SIGPIPE, lostconn);
53110Sstevel@tonic-gate #endif
53120Sstevel@tonic-gate perror_reply(551, "Error on input file");
53130Sstevel@tonic-gate retrieve_is_data = 1;
53140Sstevel@tonic-gate return (0);
53150Sstevel@tonic-gate }
53160Sstevel@tonic-gate
53170Sstevel@tonic-gate /* Transfer data from peer to "outstr" using the appropriate encapulation of
53180Sstevel@tonic-gate * the data subject to Mode, Structure, and Type.
53190Sstevel@tonic-gate *
53200Sstevel@tonic-gate * N.B.: Form isn't handled. */
53210Sstevel@tonic-gate
53220Sstevel@tonic-gate int receive_data(FILE *instr, FILE *outstr)
53230Sstevel@tonic-gate {
53240Sstevel@tonic-gate register int c;
53250Sstevel@tonic-gate int rcnt = 0, n = 0, bare_lfs = 0;
53260Sstevel@tonic-gate static char *buf;
53270Sstevel@tonic-gate int netfd, filefd, wcnt;
53280Sstevel@tonic-gate #ifdef BUFFER_SIZE
53290Sstevel@tonic-gate size_t buffer_size = BUFFER_SIZE;
53300Sstevel@tonic-gate #else
53310Sstevel@tonic-gate size_t buffer_size = BUFSIZ * 16;
53320Sstevel@tonic-gate #endif
53330Sstevel@tonic-gate
53340Sstevel@tonic-gate buf = NULL;
53350Sstevel@tonic-gate if (wu_setjmp(urgcatch)) {
53360Sstevel@tonic-gate alarm(0);
53370Sstevel@tonic-gate transflag = 0;
53380Sstevel@tonic-gate if (buf)
53390Sstevel@tonic-gate (void) free(buf);
53400Sstevel@tonic-gate return (-1);
53410Sstevel@tonic-gate }
53420Sstevel@tonic-gate transflag++;
53430Sstevel@tonic-gate switch (type) {
53440Sstevel@tonic-gate
53450Sstevel@tonic-gate case TYPE_I:
53460Sstevel@tonic-gate case TYPE_L:
53470Sstevel@tonic-gate #if defined(USE_GSS)
53480Sstevel@tonic-gate if (GSSUSERAUTH_OK(gss_info))
53490Sstevel@tonic-gate buffer_size = gss_getinbufsz();
53500Sstevel@tonic-gate else
53510Sstevel@tonic-gate #endif
53520Sstevel@tonic-gate if (recvbufsz > 0)
53530Sstevel@tonic-gate buffer_size = recvbufsz;
53540Sstevel@tonic-gate if ((buf = (char *) malloc(buffer_size)) == NULL) {
53550Sstevel@tonic-gate transflag = 0;
53560Sstevel@tonic-gate perror_reply(451, "Local resource failure: malloc");
53570Sstevel@tonic-gate return (-1);
53580Sstevel@tonic-gate }
53590Sstevel@tonic-gate netfd = fileno(instr);
53600Sstevel@tonic-gate filefd = fileno(outstr);
53610Sstevel@tonic-gate draconian_FILE = instr;
53620Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
53630Sstevel@tonic-gate alarm(timeout_data);
53640Sstevel@tonic-gate
53650Sstevel@tonic-gate while ((draconian_FILE != NULL) &&
53660Sstevel@tonic-gate #if defined(USE_GSS)
53670Sstevel@tonic-gate (rcnt = sec_read(netfd, buf, buffer_size)) > 0) {
53680Sstevel@tonic-gate #else
53690Sstevel@tonic-gate (rcnt = read(netfd, buf, buffer_size)) > 0) {
53700Sstevel@tonic-gate #endif
53710Sstevel@tonic-gate for (wcnt = 0; wcnt < rcnt; wcnt += n) {
53720Sstevel@tonic-gate if ((n = write(filefd, &buf[wcnt], rcnt - wcnt)) == -1)
53730Sstevel@tonic-gate break;
53740Sstevel@tonic-gate }
53750Sstevel@tonic-gate byte_count += wcnt;
53760Sstevel@tonic-gate #ifdef TRANSFER_COUNT
53770Sstevel@tonic-gate data_count_total += wcnt;
53780Sstevel@tonic-gate data_count_in += wcnt;
53790Sstevel@tonic-gate byte_count_total += wcnt;
53800Sstevel@tonic-gate byte_count_in += wcnt;
53810Sstevel@tonic-gate #endif
53820Sstevel@tonic-gate if (n == -1)
53830Sstevel@tonic-gate break;
53840Sstevel@tonic-gate alarm(timeout_data);
53850Sstevel@tonic-gate }
53860Sstevel@tonic-gate transflag = 0;
53870Sstevel@tonic-gate (void) free(buf);
53880Sstevel@tonic-gate if ((rcnt == -1) || (draconian_FILE == NULL))
53890Sstevel@tonic-gate goto data_err;
53900Sstevel@tonic-gate if (n == -1)
53910Sstevel@tonic-gate goto file_err;
53920Sstevel@tonic-gate draconian_FILE = NULL;
53930Sstevel@tonic-gate alarm(0);
53940Sstevel@tonic-gate #ifdef TRANSFER_COUNT
53950Sstevel@tonic-gate file_count_total++;
53960Sstevel@tonic-gate file_count_in++;
53970Sstevel@tonic-gate xfer_count_total++;
53980Sstevel@tonic-gate xfer_count_in++;
53990Sstevel@tonic-gate #endif
54000Sstevel@tonic-gate return (0);
54010Sstevel@tonic-gate
54020Sstevel@tonic-gate case TYPE_E:
54030Sstevel@tonic-gate reply(553, "TYPE E not implemented.");
54040Sstevel@tonic-gate transflag = 0;
54050Sstevel@tonic-gate return (-1);
54060Sstevel@tonic-gate
54070Sstevel@tonic-gate case TYPE_A:
54080Sstevel@tonic-gate draconian_FILE = instr;
54090Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
54100Sstevel@tonic-gate alarm(timeout_data);
54110Sstevel@tonic-gate while ((draconian_FILE != NULL) &&
54120Sstevel@tonic-gate #if defined(USE_GSS)
54130Sstevel@tonic-gate ((c = sec_getc(instr)) != EOF)
54140Sstevel@tonic-gate #else
54150Sstevel@tonic-gate ((c = getc(instr)) != EOF)
54160Sstevel@tonic-gate #endif
54170Sstevel@tonic-gate ) {
54180Sstevel@tonic-gate if (++byte_count % 4096 == 0)
54190Sstevel@tonic-gate alarm(timeout_data);
54200Sstevel@tonic-gate if (c == '\n')
54210Sstevel@tonic-gate bare_lfs++;
54220Sstevel@tonic-gate while (c == '\r') {
54230Sstevel@tonic-gate if (ferror(outstr))
54240Sstevel@tonic-gate goto file_err;
54250Sstevel@tonic-gate alarm(timeout_data);
54260Sstevel@tonic-gate if (draconian_FILE != NULL) {
54270Sstevel@tonic-gate #if defined(USE_GSS)
54280Sstevel@tonic-gate if ((c = sec_getc(instr)) != '\n')
54290Sstevel@tonic-gate #else
54300Sstevel@tonic-gate if ((c = getc(instr)) != '\n')
54310Sstevel@tonic-gate #endif
54320Sstevel@tonic-gate (void) putc('\r', outstr);
54330Sstevel@tonic-gate #ifdef TRANSFER_COUNT
54340Sstevel@tonic-gate data_count_total++;
54350Sstevel@tonic-gate data_count_in++;
54360Sstevel@tonic-gate byte_count_total++;
54370Sstevel@tonic-gate byte_count_in++;
54380Sstevel@tonic-gate #endif
54390Sstevel@tonic-gate if (c == EOF) /* null byte fix, noid@cyborg.larc.nasa.gov */
54400Sstevel@tonic-gate goto contin2;
54410Sstevel@tonic-gate if (++byte_count % 4096 == 0)
54420Sstevel@tonic-gate alarm(timeout_data);
54430Sstevel@tonic-gate }
54440Sstevel@tonic-gate }
54450Sstevel@tonic-gate (void) putc(c, outstr);
54460Sstevel@tonic-gate #ifdef TRANSFER_COUNT
54470Sstevel@tonic-gate data_count_total++;
54480Sstevel@tonic-gate data_count_in++;
54490Sstevel@tonic-gate byte_count_total++;
54500Sstevel@tonic-gate byte_count_in++;
54510Sstevel@tonic-gate #endif
54520Sstevel@tonic-gate contin2:;
54530Sstevel@tonic-gate }
54540Sstevel@tonic-gate fflush(outstr);
54550Sstevel@tonic-gate if ((draconian_FILE == NULL) || ferror(instr))
54560Sstevel@tonic-gate goto data_err;
54570Sstevel@tonic-gate if (ferror(outstr))
54580Sstevel@tonic-gate goto file_err;
54590Sstevel@tonic-gate transflag = 0;
54600Sstevel@tonic-gate draconian_FILE = NULL;
54610Sstevel@tonic-gate alarm(0);
54620Sstevel@tonic-gate if (bare_lfs) {
54630Sstevel@tonic-gate lreply(226, "WARNING! %d bare linefeeds received in ASCII mode", bare_lfs);
54640Sstevel@tonic-gate lreply(0, " File may not have transferred correctly.");
54650Sstevel@tonic-gate }
54660Sstevel@tonic-gate #ifdef TRANSFER_COUNT
54670Sstevel@tonic-gate file_count_total++;
54680Sstevel@tonic-gate file_count_in++;
54690Sstevel@tonic-gate xfer_count_total++;
54700Sstevel@tonic-gate xfer_count_in++;
54710Sstevel@tonic-gate #endif
54720Sstevel@tonic-gate return (0);
54730Sstevel@tonic-gate default:
54740Sstevel@tonic-gate reply(550, "Unimplemented TYPE %d in receive_data", type);
54750Sstevel@tonic-gate transflag = 0;
54760Sstevel@tonic-gate return (-1);
54770Sstevel@tonic-gate }
54780Sstevel@tonic-gate
54790Sstevel@tonic-gate data_err:
54800Sstevel@tonic-gate draconian_FILE = NULL;
54810Sstevel@tonic-gate alarm(0);
54820Sstevel@tonic-gate transflag = 0;
54830Sstevel@tonic-gate perror_reply(426, "Data Connection");
54840Sstevel@tonic-gate return (-1);
54850Sstevel@tonic-gate
54860Sstevel@tonic-gate file_err:
54870Sstevel@tonic-gate draconian_FILE = NULL;
54880Sstevel@tonic-gate alarm(0);
54890Sstevel@tonic-gate transflag = 0;
54900Sstevel@tonic-gate perror_reply(452, "Error writing file");
54910Sstevel@tonic-gate return (-1);
54920Sstevel@tonic-gate }
54930Sstevel@tonic-gate
54940Sstevel@tonic-gate void statfilecmd(char *filename)
54950Sstevel@tonic-gate {
54960Sstevel@tonic-gate #ifndef INTERNAL_LS
54970Sstevel@tonic-gate char line[BUFSIZ * 2], *ptr;
54980Sstevel@tonic-gate FILE *fin;
54990Sstevel@tonic-gate int c;
55000Sstevel@tonic-gate #endif /* ! INTERNAL_LS */
55010Sstevel@tonic-gate
55020Sstevel@tonic-gate fixpath(filename);
55030Sstevel@tonic-gate if (filename[0] == '\0')
55040Sstevel@tonic-gate filename = ".";
55050Sstevel@tonic-gate #ifndef INTERNAL_LS
55060Sstevel@tonic-gate if (anonymous && dolreplies)
55070Sstevel@tonic-gate (void) snprintf(line, sizeof(line), ls_long, filename);
55080Sstevel@tonic-gate else
55090Sstevel@tonic-gate (void) snprintf(line, sizeof(line), ls_short, filename);
55100Sstevel@tonic-gate fin = ftpd_popen(line, "r", 0);
55110Sstevel@tonic-gate #endif /* ! INTERNAL_LS */
55120Sstevel@tonic-gate lreply(213, "status of %s:", filename);
55130Sstevel@tonic-gate #ifndef INTERNAL_LS
55140Sstevel@tonic-gate /*
55150Sstevel@tonic-gate while ((c = getc(fin)) != EOF) {
55160Sstevel@tonic-gate if (c == '\n') {
55170Sstevel@tonic-gate if (ferror(stdout)) {
55180Sstevel@tonic-gate perror_reply(421, "control connection");
55190Sstevel@tonic-gate (void) ftpd_pclose(fin);
55200Sstevel@tonic-gate dologout(1);
55210Sstevel@tonic-gate / * NOTREACHED * /
55220Sstevel@tonic-gate }
55230Sstevel@tonic-gate if (ferror(fin)) {
55240Sstevel@tonic-gate perror_reply(551, filename);
55250Sstevel@tonic-gate (void) ftpd_pclose(fin);
55260Sstevel@tonic-gate return;
55270Sstevel@tonic-gate }
55280Sstevel@tonic-gate (void) putc('\r', stdout);
55290Sstevel@tonic-gate }
55300Sstevel@tonic-gate (void) putc(c, stdout);
55310Sstevel@tonic-gate }
55320Sstevel@tonic-gate */
55330Sstevel@tonic-gate while (fgets(line, sizeof(line), fin) != NULL) {
55340Sstevel@tonic-gate if ((ptr = strchr(line, '\n'))) /* clip out unnecessary newline */
55350Sstevel@tonic-gate *ptr = '\0';
55360Sstevel@tonic-gate lreply(0, "%s", line);
55370Sstevel@tonic-gate }
55380Sstevel@tonic-gate (void) ftpd_pclose(fin);
55390Sstevel@tonic-gate #else /* INTERNAL_LS */
55400Sstevel@tonic-gate ls_dir(filename, 1, 0, 1, 0, 1, stdout);
55410Sstevel@tonic-gate #endif /* INTERNAL_LS */
55420Sstevel@tonic-gate reply(213, "End of Status");
55430Sstevel@tonic-gate }
55440Sstevel@tonic-gate
55450Sstevel@tonic-gate void statcmd(void)
55460Sstevel@tonic-gate {
55470Sstevel@tonic-gate struct SOCKSTORAGE *sin;
55480Sstevel@tonic-gate u_char *a, *p;
55490Sstevel@tonic-gate unsigned short port;
55500Sstevel@tonic-gate #ifdef INET6
55510Sstevel@tonic-gate int isv4 = 0;
55520Sstevel@tonic-gate #endif
55530Sstevel@tonic-gate
55540Sstevel@tonic-gate lreply(211, "%s FTP server status:", hostname);
55550Sstevel@tonic-gate lreply(0, " %s", version);
55560Sstevel@tonic-gate if (nameserved)
55570Sstevel@tonic-gate lreply(0, " Connected to %s (%s)", remotehost, remoteaddr);
55580Sstevel@tonic-gate else
55590Sstevel@tonic-gate lreply(0, " Connected to %s", remotehost);
55600Sstevel@tonic-gate
55610Sstevel@tonic-gate if (logged_in) {
55620Sstevel@tonic-gate if (anonymous)
55630Sstevel@tonic-gate lreply(0, " Logged in anonymously");
55640Sstevel@tonic-gate else
55650Sstevel@tonic-gate lreply(0, " Logged in as %s", pw->pw_name);
55660Sstevel@tonic-gate }
55670Sstevel@tonic-gate else if (askpasswd)
55680Sstevel@tonic-gate lreply(0, " Waiting for password");
55690Sstevel@tonic-gate else
55700Sstevel@tonic-gate lreply(0, " Waiting for user name");
55710Sstevel@tonic-gate
55720Sstevel@tonic-gate if (type == TYPE_L)
55730Sstevel@tonic-gate #ifdef NBBY
55740Sstevel@tonic-gate lreply(0, " TYPE: %s %d; STRUcture: %s; transfer MODE: %s",
55750Sstevel@tonic-gate typenames[type], NBBY, strunames[stru], modenames[mode]);
55760Sstevel@tonic-gate #else
55770Sstevel@tonic-gate lreply(0, " TYPE: %s %d; STRUcture: %s; transfer MODE: %s",
55780Sstevel@tonic-gate typenames[type], bytesize, strunames[stru], modenames[mode]);
55790Sstevel@tonic-gate #endif /* NBBY */
55800Sstevel@tonic-gate else
55810Sstevel@tonic-gate lreply(0, " TYPE: %s%s%s; STRUcture: %s; transfer MODE: %s",
55820Sstevel@tonic-gate typenames[type], (type == TYPE_A || type == TYPE_E) ?
55830Sstevel@tonic-gate ", FORM: " : "", (type == TYPE_A || type == TYPE_E) ?
55840Sstevel@tonic-gate formnames[form] : "", strunames[stru], modenames[mode]);
55850Sstevel@tonic-gate if (data != -1)
55860Sstevel@tonic-gate lreply(0, " Data connection open");
55870Sstevel@tonic-gate else if (pdata != -1 || usedefault == 0) {
55880Sstevel@tonic-gate if (usedefault == 0) {
55890Sstevel@tonic-gate sin = &data_dest;
55900Sstevel@tonic-gate port = SOCK_PORT(data_dest);
55910Sstevel@tonic-gate }
55920Sstevel@tonic-gate else {
55930Sstevel@tonic-gate port = SOCK_PORT(pasv_addr);
55940Sstevel@tonic-gate if (route_vectored)
55950Sstevel@tonic-gate sin = &vect_addr;
55960Sstevel@tonic-gate else
55970Sstevel@tonic-gate sin = &pasv_addr;
55980Sstevel@tonic-gate }
55990Sstevel@tonic-gate a = (u_char *) SOCK_ADDR(*sin);
56000Sstevel@tonic-gate p = (u_char *) &port;
56010Sstevel@tonic-gate #define UC(b) (((int) b) & 0xff)
56020Sstevel@tonic-gate #ifdef INET6
56030Sstevel@tonic-gate if (SOCK_FAMILY(*sin) == AF_INET)
56040Sstevel@tonic-gate isv4 = 1;
56050Sstevel@tonic-gate else if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sin)->sin6_addr))
56060Sstevel@tonic-gate {
56070Sstevel@tonic-gate isv4 = 1;
56080Sstevel@tonic-gate a += 12; /* move to the IPv4 part of an IPv4-mapped IPv6 address */
56090Sstevel@tonic-gate }
56100Sstevel@tonic-gate if (epsv_all)
56110Sstevel@tonic-gate lreply(0, " EPSV only mode (EPSV ALL)");
56120Sstevel@tonic-gate if (isv4 && !epsv_all)
56130Sstevel@tonic-gate #endif
56140Sstevel@tonic-gate lreply(0, " %s (%d,%d,%d,%d,%d,%d)",
56150Sstevel@tonic-gate usedefault == 0 ? "PORT" : "PASV",
56160Sstevel@tonic-gate UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
56170Sstevel@tonic-gate #ifdef INET6
56180Sstevel@tonic-gate lreply(0, " %s (|%d|%s|%d|)", usedefault == 0 ? "EPRT" : "EPSV",
56190Sstevel@tonic-gate isv4 ? 1 : 2, inet_stop(sin), ntohs(port));
56200Sstevel@tonic-gate if (!epsv_all)
56210Sstevel@tonic-gate if (isv4)
56220Sstevel@tonic-gate lreply(0, " %s (4,4,%d,%d,%d,%d,2,%d,%d)",
56230Sstevel@tonic-gate usedefault == 0 ? "LPRT" : "LPSV",
56240Sstevel@tonic-gate UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
56250Sstevel@tonic-gate UC(p[0]), UC(p[1]));
56260Sstevel@tonic-gate else
56270Sstevel@tonic-gate lreply(0, " %s (6,16,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
56280Sstevel@tonic-gate "%d,%d,%d,%d,2,%d,%d)",
56290Sstevel@tonic-gate usedefault == 0 ? "LPRT" : "LPSV",
56300Sstevel@tonic-gate UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
56310Sstevel@tonic-gate UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
56320Sstevel@tonic-gate UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
56330Sstevel@tonic-gate UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
56340Sstevel@tonic-gate UC(p[0]), UC(p[1]));
56350Sstevel@tonic-gate #endif
56360Sstevel@tonic-gate #undef UC
56370Sstevel@tonic-gate }
56380Sstevel@tonic-gate else
56390Sstevel@tonic-gate lreply(0, " No data connection");
56400Sstevel@tonic-gate #ifdef TRANSFER_COUNT
56410Sstevel@tonic-gate lreply(0, " %" L_FORMAT " data bytes received in %d files", data_count_in, file_count_in);
56420Sstevel@tonic-gate lreply(0, " %" L_FORMAT " data bytes transmitted in %d files", data_count_out, file_count_out);
56430Sstevel@tonic-gate lreply(0, " %" L_FORMAT " data bytes total in %d files", data_count_total, file_count_total);
56440Sstevel@tonic-gate lreply(0, " %" L_FORMAT " traffic bytes received in %d transfers", byte_count_in, xfer_count_in);
56450Sstevel@tonic-gate lreply(0, " %" L_FORMAT " traffic bytes transmitted in %d transfers", byte_count_out, xfer_count_out);
56460Sstevel@tonic-gate lreply(0, " %" L_FORMAT " traffic bytes total in %d transfers", byte_count_total, xfer_count_total);
56470Sstevel@tonic-gate #endif
56480Sstevel@tonic-gate reply(211, "End of status");
56490Sstevel@tonic-gate }
56500Sstevel@tonic-gate
56510Sstevel@tonic-gate void fatal(char *s)
56520Sstevel@tonic-gate {
56530Sstevel@tonic-gate reply(451, "Error in server: %s\n", s);
56540Sstevel@tonic-gate reply(221, "Closing connection due to server error.");
56550Sstevel@tonic-gate dologout(0);
56560Sstevel@tonic-gate /* NOTREACHED */
56570Sstevel@tonic-gate }
56580Sstevel@tonic-gate
56590Sstevel@tonic-gate #define USE_REPLY_NOTFMT (1<<1) /* fmt is not a printf fmt (KLUDGE) */
56600Sstevel@tonic-gate #define USE_REPLY_LONG (1<<2) /* this is a long reply; use a - */
56610Sstevel@tonic-gate
56620Sstevel@tonic-gate void vreply(long flags, int n, char *fmt, va_list ap)
56630Sstevel@tonic-gate {
56640Sstevel@tonic-gate char buf[BUFSIZ * 16];
56650Sstevel@tonic-gate
56660Sstevel@tonic-gate flags &= USE_REPLY_NOTFMT | USE_REPLY_LONG;
56670Sstevel@tonic-gate
56680Sstevel@tonic-gate if (n) /* if numeric is 0, don't output one; use n==0 in place of printf's */
56690Sstevel@tonic-gate sprintf(buf, "%03d%c", n, flags & USE_REPLY_LONG ? '-' : ' ');
56700Sstevel@tonic-gate
56710Sstevel@tonic-gate /* This is somewhat of a kludge for autospout. I personally think that
56720Sstevel@tonic-gate * autospout should be done differently, but that's not my department. -Kev
56730Sstevel@tonic-gate */
56740Sstevel@tonic-gate if (flags & USE_REPLY_NOTFMT)
56750Sstevel@tonic-gate snprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), "%s", fmt);
56760Sstevel@tonic-gate else
56770Sstevel@tonic-gate vsnprintf(buf + (n ? 4 : 0), n ? sizeof(buf) - 4 : sizeof(buf), fmt, ap);
56780Sstevel@tonic-gate
56790Sstevel@tonic-gate #if defined(USE_GSS)
56800Sstevel@tonic-gate if (IS_GSSAUTH(cur_auth_type) &&
56810Sstevel@tonic-gate (gss_info.authstate & GSS_ADAT_DONE) &&
56820Sstevel@tonic-gate gss_info.ctrl_prot != PROT_C) {
56830Sstevel@tonic-gate if (buf[strlen(buf)-1] != '\n')
56840Sstevel@tonic-gate strlcat(buf, "\r\n", sizeof(buf));
56850Sstevel@tonic-gate (void) sec_reply(buf, sizeof(buf), n);
56860Sstevel@tonic-gate }
56870Sstevel@tonic-gate #endif
56880Sstevel@tonic-gate
56890Sstevel@tonic-gate if (debug) /* debugging output :) */
56900Sstevel@tonic-gate syslog(LOG_DEBUG, "<--- %s", buf);
56910Sstevel@tonic-gate
56920Sstevel@tonic-gate /* Yes, you want the debugging output before the client output; wrapping
56930Sstevel@tonic-gate * stuff goes here, you see, and you want to log the cleartext and send
56940Sstevel@tonic-gate * the wrapped text to the client.
56950Sstevel@tonic-gate */
56960Sstevel@tonic-gate
56970Sstevel@tonic-gate printf("%s\r\n", buf); /* and send it to the client */
56980Sstevel@tonic-gate #ifdef TRANSFER_COUNT
56990Sstevel@tonic-gate byte_count_total += strlen(buf);
57000Sstevel@tonic-gate byte_count_out += strlen(buf);
57010Sstevel@tonic-gate #endif
57020Sstevel@tonic-gate /*
57030Sstevel@tonic-gate * We dont need to worry about "sec_fflush" here since "sec_reply"
57040Sstevel@tonic-gate * already wrapped the reply if necessary.
57050Sstevel@tonic-gate */
57060Sstevel@tonic-gate fflush(stdout);
57070Sstevel@tonic-gate }
57080Sstevel@tonic-gate
57090Sstevel@tonic-gate void reply(int n, char *fmt,...)
57100Sstevel@tonic-gate {
57110Sstevel@tonic-gate VA_LOCAL_DECL
57120Sstevel@tonic-gate
57130Sstevel@tonic-gate if (autospout != NULL) { /* deal with the autospout stuff... */
57140Sstevel@tonic-gate char *p, *ptr = autospout;
57150Sstevel@tonic-gate
57160Sstevel@tonic-gate while (*ptr) {
57170Sstevel@tonic-gate if ((p = strchr(ptr, '\n')) != NULL) /* step through line by line */
57180Sstevel@tonic-gate *p = '\0';
57190Sstevel@tonic-gate
57200Sstevel@tonic-gate /* send a line...(note that this overrides dolreplies!) */
57210Sstevel@tonic-gate vreply(USE_REPLY_LONG | USE_REPLY_NOTFMT, n, ptr, ap);
57220Sstevel@tonic-gate
57230Sstevel@tonic-gate if (p)
57240Sstevel@tonic-gate ptr = p + 1; /* set to the next line... (\0 is handled in the while) */
57250Sstevel@tonic-gate else
57260Sstevel@tonic-gate break; /* oh, we're done; drop out of the loop */
57270Sstevel@tonic-gate }
57280Sstevel@tonic-gate
57290Sstevel@tonic-gate if (autospout_free) { /* free autospout if necessary */
57300Sstevel@tonic-gate (void) free(autospout);
57310Sstevel@tonic-gate autospout_free = 0;
57320Sstevel@tonic-gate }
57330Sstevel@tonic-gate autospout = 0; /* clear the autospout */
57340Sstevel@tonic-gate }
57350Sstevel@tonic-gate
57360Sstevel@tonic-gate VA_START(fmt);
57370Sstevel@tonic-gate
57380Sstevel@tonic-gate /* send the reply */
57390Sstevel@tonic-gate vreply(0L, n, fmt, ap);
57400Sstevel@tonic-gate
57410Sstevel@tonic-gate VA_END;
57420Sstevel@tonic-gate }
57430Sstevel@tonic-gate
57440Sstevel@tonic-gate void lreply(int n, char *fmt,...)
57450Sstevel@tonic-gate {
57460Sstevel@tonic-gate VA_LOCAL_DECL
57470Sstevel@tonic-gate
57480Sstevel@tonic-gate if (!dolreplies) /* prohibited from doing long replies? */
57490Sstevel@tonic-gate return;
57500Sstevel@tonic-gate
57510Sstevel@tonic-gate VA_START(fmt);
57520Sstevel@tonic-gate
57530Sstevel@tonic-gate /* send the reply */
57540Sstevel@tonic-gate vreply(USE_REPLY_LONG, n, fmt, ap);
57550Sstevel@tonic-gate
57560Sstevel@tonic-gate VA_END;
57570Sstevel@tonic-gate }
57580Sstevel@tonic-gate
57590Sstevel@tonic-gate void ack(char *s)
57600Sstevel@tonic-gate {
57610Sstevel@tonic-gate reply(250, "%s command successful.", s);
57620Sstevel@tonic-gate }
57630Sstevel@tonic-gate
57640Sstevel@tonic-gate void nack(char *s)
57650Sstevel@tonic-gate {
57660Sstevel@tonic-gate reply(502, "%s command not implemented.", s);
57670Sstevel@tonic-gate }
57680Sstevel@tonic-gate
57690Sstevel@tonic-gate void yyerror(char *s)
57700Sstevel@tonic-gate {
57710Sstevel@tonic-gate char *cp;
57720Sstevel@tonic-gate if (s == NULL || yyerrorcalled != 0)
57730Sstevel@tonic-gate return;
57740Sstevel@tonic-gate if ((cp = strchr(cbuf, '\n')) != NULL)
57750Sstevel@tonic-gate *cp = '\0';
57760Sstevel@tonic-gate reply(500, "'%s': command not understood.", cbuf);
57770Sstevel@tonic-gate yyerrorcalled = 1;
57780Sstevel@tonic-gate return;
57790Sstevel@tonic-gate }
57800Sstevel@tonic-gate
57810Sstevel@tonic-gate void delete(char *name)
57820Sstevel@tonic-gate {
57830Sstevel@tonic-gate struct stat st;
57840Sstevel@tonic-gate char realname[MAXPATHLEN];
57850Sstevel@tonic-gate
57860Sstevel@tonic-gate /*
57870Sstevel@tonic-gate * delete permission?
57880Sstevel@tonic-gate */
57890Sstevel@tonic-gate
57900Sstevel@tonic-gate wu_realpath(name, realname, chroot_path);
57910Sstevel@tonic-gate
57920Sstevel@tonic-gate if ((del_check(name)) == 0) {
57930Sstevel@tonic-gate if (log_security)
57940Sstevel@tonic-gate if (anonymous)
57950Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete %s",
57960Sstevel@tonic-gate guestpw, remoteident, realname);
57970Sstevel@tonic-gate else
57980Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to delete %s",
57990Sstevel@tonic-gate pw->pw_name, remoteident, realname);
58000Sstevel@tonic-gate return;
58010Sstevel@tonic-gate }
58020Sstevel@tonic-gate
58030Sstevel@tonic-gate if (lstat(name, &st) < 0) {
58040Sstevel@tonic-gate perror_reply(550, name);
58050Sstevel@tonic-gate return;
58060Sstevel@tonic-gate }
58070Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFDIR) {
58080Sstevel@tonic-gate uid_t uid;
58090Sstevel@tonic-gate gid_t gid;
58100Sstevel@tonic-gate int d_mode;
58110Sstevel@tonic-gate int valid;
58120Sstevel@tonic-gate
58130Sstevel@tonic-gate /*
58140Sstevel@tonic-gate * check the directory, can we rmdir here?
58150Sstevel@tonic-gate */
58160Sstevel@tonic-gate if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
58170Sstevel@tonic-gate if (log_security)
58180Sstevel@tonic-gate if (anonymous)
58190Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete directory %s",
58200Sstevel@tonic-gate guestpw, remoteident, realname);
58210Sstevel@tonic-gate else
58220Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to delete directory %s",
58230Sstevel@tonic-gate pw->pw_name, remoteident, realname);
58240Sstevel@tonic-gate return;
58250Sstevel@tonic-gate }
58260Sstevel@tonic-gate
58270Sstevel@tonic-gate if (rmdir(name) < 0) {
58280Sstevel@tonic-gate if (log_security)
58290Sstevel@tonic-gate if (anonymous)
58300Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete directory %s (permissions)",
58310Sstevel@tonic-gate guestpw, remoteident, realname);
58320Sstevel@tonic-gate else
58330Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to delete directory %s (permissions)",
58340Sstevel@tonic-gate pw->pw_name, remoteident, realname);
58350Sstevel@tonic-gate perror_reply(550, name);
58360Sstevel@tonic-gate return;
58370Sstevel@tonic-gate }
58380Sstevel@tonic-gate goto done;
58390Sstevel@tonic-gate }
58400Sstevel@tonic-gate if (unlink(name) < 0) {
58410Sstevel@tonic-gate if (log_security)
58420Sstevel@tonic-gate if (anonymous)
58430Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to delete %s (permissions)",
58440Sstevel@tonic-gate guestpw, remoteident, realname);
58450Sstevel@tonic-gate else
58460Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to delete %s (permissions)",
58470Sstevel@tonic-gate pw->pw_name, remoteident, realname);
58480Sstevel@tonic-gate perror_reply(550, name);
58490Sstevel@tonic-gate return;
58500Sstevel@tonic-gate }
58510Sstevel@tonic-gate done:
58520Sstevel@tonic-gate {
58530Sstevel@tonic-gate char path[MAXPATHLEN];
58540Sstevel@tonic-gate
58550Sstevel@tonic-gate wu_realpath(name, path, chroot_path);
58560Sstevel@tonic-gate
58570Sstevel@tonic-gate if (log_security)
58580Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFDIR)
58590Sstevel@tonic-gate if (anonymous) {
58600Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s deleted directory %s", guestpw, remoteident, path);
58610Sstevel@tonic-gate }
58620Sstevel@tonic-gate else {
58630Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s deleted directory %s", pw->pw_name,
58640Sstevel@tonic-gate remoteident, path);
58650Sstevel@tonic-gate }
58660Sstevel@tonic-gate else if (anonymous) {
58670Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s deleted %s", guestpw,
58680Sstevel@tonic-gate remoteident, path);
58690Sstevel@tonic-gate }
58700Sstevel@tonic-gate else {
58710Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s deleted %s", pw->pw_name,
58720Sstevel@tonic-gate remoteident, path);
58730Sstevel@tonic-gate }
58740Sstevel@tonic-gate }
58750Sstevel@tonic-gate
58760Sstevel@tonic-gate ack("DELE");
58770Sstevel@tonic-gate }
58780Sstevel@tonic-gate
58790Sstevel@tonic-gate void cwd(char *path)
58800Sstevel@tonic-gate {
58810Sstevel@tonic-gate struct aclmember *entry = NULL;
58820Sstevel@tonic-gate char cdpath[MAXPATHLEN];
58830Sstevel@tonic-gate
58840Sstevel@tonic-gate if (chdir(path) < 0) {
58850Sstevel@tonic-gate /* alias checking */
58860Sstevel@tonic-gate while (getaclentry("alias", &entry) && ARG0 && ARG1 != NULL) {
58870Sstevel@tonic-gate if (!strcasecmp(ARG0, path)) {
58880Sstevel@tonic-gate if (chdir(ARG1) < 0)
58890Sstevel@tonic-gate perror_reply(550, path);
58900Sstevel@tonic-gate else {
58910Sstevel@tonic-gate show_message(250, C_WD);
58920Sstevel@tonic-gate show_readme(250, C_WD);
58930Sstevel@tonic-gate ack("CWD");
58940Sstevel@tonic-gate }
58950Sstevel@tonic-gate return;
58960Sstevel@tonic-gate }
58970Sstevel@tonic-gate }
58980Sstevel@tonic-gate /* check for "cdpath" directories. */
58990Sstevel@tonic-gate entry = (struct aclmember *) NULL;
59000Sstevel@tonic-gate while (getaclentry("cdpath", &entry) && ARG0 != NULL) {
59010Sstevel@tonic-gate snprintf(cdpath, sizeof cdpath, "%s/%s", ARG0, path);
59020Sstevel@tonic-gate if (chdir(cdpath) >= 0) {
59030Sstevel@tonic-gate show_message(250, C_WD);
59040Sstevel@tonic-gate show_readme(250, C_WD);
59050Sstevel@tonic-gate ack("CWD");
59060Sstevel@tonic-gate return;
59070Sstevel@tonic-gate }
59080Sstevel@tonic-gate }
59090Sstevel@tonic-gate perror_reply(550, path);
59100Sstevel@tonic-gate }
59110Sstevel@tonic-gate else {
59120Sstevel@tonic-gate show_message(250, C_WD);
59130Sstevel@tonic-gate show_readme(250, C_WD);
59140Sstevel@tonic-gate ack("CWD");
59150Sstevel@tonic-gate }
59160Sstevel@tonic-gate }
59170Sstevel@tonic-gate
59180Sstevel@tonic-gate void makedir(char *name)
59190Sstevel@tonic-gate {
59200Sstevel@tonic-gate uid_t uid;
59210Sstevel@tonic-gate gid_t gid;
59220Sstevel@tonic-gate int d_mode;
59230Sstevel@tonic-gate mode_t oldumask;
59240Sstevel@tonic-gate int valid;
59250Sstevel@tonic-gate int ret, serrno;
59260Sstevel@tonic-gate uid_t oldid;
59270Sstevel@tonic-gate char path[MAXPATHLEN + 1]; /* for realpath() later - cky */
59280Sstevel@tonic-gate char realname[MAXPATHLEN];
59290Sstevel@tonic-gate
59300Sstevel@tonic-gate wu_realpath(name, realname, chroot_path);
59310Sstevel@tonic-gate /*
59320Sstevel@tonic-gate * check the directory, can we mkdir here?
59330Sstevel@tonic-gate */
59340Sstevel@tonic-gate if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
59350Sstevel@tonic-gate if (log_security)
59360Sstevel@tonic-gate if (anonymous)
59370Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s",
59380Sstevel@tonic-gate guestpw, remoteident, realname);
59390Sstevel@tonic-gate else
59400Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to create directory %s",
59410Sstevel@tonic-gate pw->pw_name, remoteident, realname);
59420Sstevel@tonic-gate return;
59430Sstevel@tonic-gate }
59440Sstevel@tonic-gate
59450Sstevel@tonic-gate /*
59460Sstevel@tonic-gate * check the filename, is it legal?
59470Sstevel@tonic-gate */
59480Sstevel@tonic-gate if ((fn_check(name)) <= 0) {
59490Sstevel@tonic-gate if (log_security)
59500Sstevel@tonic-gate if (anonymous)
59510Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (path-filter)",
59520Sstevel@tonic-gate guestpw, remoteident, realname);
59530Sstevel@tonic-gate else
59540Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to create directory %s (path-filter)",
59550Sstevel@tonic-gate pw->pw_name, remoteident, realname);
59560Sstevel@tonic-gate return;
59570Sstevel@tonic-gate }
59580Sstevel@tonic-gate
59590Sstevel@tonic-gate oldumask = umask(0000);
59600Sstevel@tonic-gate if (valid <= 0) {
59610Sstevel@tonic-gate d_mode = 0777;
59620Sstevel@tonic-gate umask(oldumask);
59630Sstevel@tonic-gate }
59640Sstevel@tonic-gate
59650Sstevel@tonic-gate if (mkdir(name, d_mode) < 0) {
59660Sstevel@tonic-gate if (errno == EEXIST) {
59670Sstevel@tonic-gate if (log_security)
59680Sstevel@tonic-gate if (anonymous)
59690Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (exists)",
59700Sstevel@tonic-gate guestpw, remoteident, realname);
59710Sstevel@tonic-gate else
59720Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to create directory %s (exists)",
59730Sstevel@tonic-gate pw->pw_name, remoteident, realname);
59740Sstevel@tonic-gate fb_realpath(name, path);
59750Sstevel@tonic-gate reply(521, "\"%s\" directory exists", path);
59760Sstevel@tonic-gate }
59770Sstevel@tonic-gate else {
59780Sstevel@tonic-gate if (log_security)
59790Sstevel@tonic-gate if (anonymous)
59800Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to create directory %s (permissions)",
59810Sstevel@tonic-gate guestpw, remoteident, realname);
59820Sstevel@tonic-gate else
59830Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to create directory %s (permissions)",
59840Sstevel@tonic-gate pw->pw_name, remoteident, realname);
59850Sstevel@tonic-gate perror_reply(550, name);
59860Sstevel@tonic-gate }
59870Sstevel@tonic-gate umask(oldumask);
59880Sstevel@tonic-gate return;
59890Sstevel@tonic-gate }
59900Sstevel@tonic-gate umask(oldumask);
59910Sstevel@tonic-gate if (valid > 0) {
59920Sstevel@tonic-gate oldid = geteuid();
59930Sstevel@tonic-gate if (uid != 0)
59940Sstevel@tonic-gate (void) seteuid((uid_t) uid);
59950Sstevel@tonic-gate if ((uid == 0) || ((chown(name, uid, gid)) < 0)) {
59960Sstevel@tonic-gate chown_priv_on(0);
59970Sstevel@tonic-gate ret = chown(name, uid, gid);
59980Sstevel@tonic-gate serrno = errno;
59990Sstevel@tonic-gate chown_priv_off(oldid);
60000Sstevel@tonic-gate if (ret < 0) {
60010Sstevel@tonic-gate errno = serrno;
60020Sstevel@tonic-gate perror_reply(550, "chown");
60030Sstevel@tonic-gate return;
60040Sstevel@tonic-gate }
60050Sstevel@tonic-gate }
60060Sstevel@tonic-gate else
60070Sstevel@tonic-gate (void) seteuid(oldid);
60080Sstevel@tonic-gate }
60090Sstevel@tonic-gate wu_realpath(name, path, chroot_path);
60100Sstevel@tonic-gate if (log_security)
60110Sstevel@tonic-gate if (anonymous) {
60120Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s created directory %s", guestpw, remoteident, path);
60130Sstevel@tonic-gate }
60140Sstevel@tonic-gate else {
60150Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s created directory %s", pw->pw_name,
60160Sstevel@tonic-gate remoteident, path);
60170Sstevel@tonic-gate }
60180Sstevel@tonic-gate fb_realpath(name, path);
60190Sstevel@tonic-gate /* According to RFC 959:
60200Sstevel@tonic-gate * The 257 reply to the MKD command must always contain the
60210Sstevel@tonic-gate * absolute pathname of the created directory.
60220Sstevel@tonic-gate * This is implemented here using similar code to the PWD command.
60230Sstevel@tonic-gate * XXX - still need to do `quote-doubling'.
60240Sstevel@tonic-gate */
60250Sstevel@tonic-gate reply(257, "\"%s\" new directory created.", path);
60260Sstevel@tonic-gate }
60270Sstevel@tonic-gate
60280Sstevel@tonic-gate void removedir(char *name)
60290Sstevel@tonic-gate {
60300Sstevel@tonic-gate uid_t uid;
60310Sstevel@tonic-gate gid_t gid;
60320Sstevel@tonic-gate int d_mode;
60330Sstevel@tonic-gate int valid;
60340Sstevel@tonic-gate char realname[MAXPATHLEN];
60350Sstevel@tonic-gate
60360Sstevel@tonic-gate wu_realpath(name, realname, chroot_path);
60370Sstevel@tonic-gate
60380Sstevel@tonic-gate /*
60390Sstevel@tonic-gate * delete permission?
60400Sstevel@tonic-gate */
60410Sstevel@tonic-gate
60420Sstevel@tonic-gate if ((del_check(name)) == 0)
60430Sstevel@tonic-gate return;
60440Sstevel@tonic-gate /*
60450Sstevel@tonic-gate * check the directory, can we rmdir here?
60460Sstevel@tonic-gate */
60470Sstevel@tonic-gate if ((dir_check(name, &uid, &gid, &d_mode, &valid)) <= 0) {
60480Sstevel@tonic-gate if (log_security)
60490Sstevel@tonic-gate if (anonymous)
60500Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to remove directory %s",
60510Sstevel@tonic-gate guestpw, remoteident, realname);
60520Sstevel@tonic-gate else
60530Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to remove directory %s",
60540Sstevel@tonic-gate pw->pw_name, remoteident, realname);
60550Sstevel@tonic-gate return;
60560Sstevel@tonic-gate }
60570Sstevel@tonic-gate
60580Sstevel@tonic-gate if (rmdir(name) < 0) {
60590Sstevel@tonic-gate if (errno == EBUSY)
60600Sstevel@tonic-gate perror_reply(450, name);
60610Sstevel@tonic-gate else {
60620Sstevel@tonic-gate if (log_security)
60630Sstevel@tonic-gate if (anonymous)
60640Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to remove directory %s (permissions)",
60650Sstevel@tonic-gate guestpw, remoteident, realname);
60660Sstevel@tonic-gate else
60670Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to remove directory %s (permissions)",
60680Sstevel@tonic-gate pw->pw_name, remoteident, realname);
60690Sstevel@tonic-gate perror_reply(550, name);
60700Sstevel@tonic-gate }
60710Sstevel@tonic-gate }
60720Sstevel@tonic-gate else {
60730Sstevel@tonic-gate char path[MAXPATHLEN];
60740Sstevel@tonic-gate
60750Sstevel@tonic-gate wu_realpath(name, path, chroot_path);
60760Sstevel@tonic-gate
60770Sstevel@tonic-gate if (log_security)
60780Sstevel@tonic-gate if (anonymous) {
60790Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s deleted directory %s", guestpw, remoteident, path);
60800Sstevel@tonic-gate }
60810Sstevel@tonic-gate else {
60820Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s deleted directory %s", pw->pw_name,
60830Sstevel@tonic-gate remoteident, path);
60840Sstevel@tonic-gate }
60850Sstevel@tonic-gate ack("RMD");
60860Sstevel@tonic-gate }
60870Sstevel@tonic-gate }
60880Sstevel@tonic-gate
60890Sstevel@tonic-gate void pwd(void)
60900Sstevel@tonic-gate {
60910Sstevel@tonic-gate char path[MAXPATHLEN + 1];
60920Sstevel@tonic-gate char rhome[MAXPATHLEN + 1];
60930Sstevel@tonic-gate char *rpath = path; /* Path to return to client */
60940Sstevel@tonic-gate int pathlen;
60950Sstevel@tonic-gate #ifndef MAPPING_CHDIR
60960Sstevel@tonic-gate #ifdef HAVE_GETCWD
60970Sstevel@tonic-gate extern char *getcwd();
60980Sstevel@tonic-gate #else
60990Sstevel@tonic-gate extern char *getwd(char *);
61000Sstevel@tonic-gate #endif
61010Sstevel@tonic-gate #endif /* MAPPING_CHDIR */
61020Sstevel@tonic-gate
61030Sstevel@tonic-gate #ifdef HAVE_GETCWD
61040Sstevel@tonic-gate if (getcwd(path, MAXPATHLEN) == (char *) NULL)
61050Sstevel@tonic-gate #else
61060Sstevel@tonic-gate if (getwd(path) == (char *) NULL)
61070Sstevel@tonic-gate #endif
61080Sstevel@tonic-gate /* Dink! If you couldn't get the path and the buffer is now likely to
61090Sstevel@tonic-gate be undefined, why are you trying to PRINT it?! _H*
61100Sstevel@tonic-gate reply(550, "%s.", path); */
61110Sstevel@tonic-gate {
61120Sstevel@tonic-gate fb_realpath(".", path); /* realpath_on_steroids can deal */
61130Sstevel@tonic-gate }
61140Sstevel@tonic-gate /* relative to home directory if restricted_user */
61150Sstevel@tonic-gate if (restricted_user) {
61163486Sjs198686 /*
61173486Sjs198686 * Re-adjust real path because previous call to getXwd() did
61183486Sjs198686 * not resolve symlink.
61193486Sjs198686 */
61203486Sjs198686 fb_realpath(".", path);
61210Sstevel@tonic-gate fb_realpath(home, rhome);
61220Sstevel@tonic-gate pathlen = strlen(rhome);
61230Sstevel@tonic-gate if (pathlen && rhome[pathlen - 1] == '/')
61240Sstevel@tonic-gate pathlen--;
61250Sstevel@tonic-gate rpath = rpath + pathlen;
61260Sstevel@tonic-gate if (!*rpath)
61270Sstevel@tonic-gate strcpy(rpath, "/");
61280Sstevel@tonic-gate }
61290Sstevel@tonic-gate reply(257, "\"%s\" is current directory.", rpath);
61300Sstevel@tonic-gate }
61310Sstevel@tonic-gate
61320Sstevel@tonic-gate char *renamefrom(char *name)
61330Sstevel@tonic-gate {
61340Sstevel@tonic-gate struct stat st;
61350Sstevel@tonic-gate
61360Sstevel@tonic-gate if (lstat(name, &st) < 0) {
61370Sstevel@tonic-gate perror_reply(550, name);
61380Sstevel@tonic-gate return ((char *) 0);
61390Sstevel@tonic-gate }
61400Sstevel@tonic-gate reply(350, "File exists, ready for destination name");
61410Sstevel@tonic-gate return (name);
61420Sstevel@tonic-gate }
61430Sstevel@tonic-gate
61440Sstevel@tonic-gate void renamecmd(char *from, char *to)
61450Sstevel@tonic-gate {
61460Sstevel@tonic-gate int allowed = (anonymous ? 0 : 1);
61470Sstevel@tonic-gate char realfrom[MAXPATHLEN];
61480Sstevel@tonic-gate char realto[MAXPATHLEN];
61490Sstevel@tonic-gate struct aclmember *entry = NULL;
61500Sstevel@tonic-gate #ifdef PARANOID
61510Sstevel@tonic-gate struct stat st;
61520Sstevel@tonic-gate #endif
61530Sstevel@tonic-gate wu_realpath(from, realfrom, chroot_path);
61540Sstevel@tonic-gate wu_realpath(to, realto, chroot_path);
61550Sstevel@tonic-gate /*
61560Sstevel@tonic-gate * check the filename, is it legal?
61570Sstevel@tonic-gate */
61580Sstevel@tonic-gate if ((fn_check(to)) == 0) {
61590Sstevel@tonic-gate if (log_security)
61600Sstevel@tonic-gate if (anonymous)
61610Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to \"%s\" (path-filter)",
61620Sstevel@tonic-gate guestpw, remoteident, realfrom, realto);
61630Sstevel@tonic-gate else
61640Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to rename %s to \"%s\" (path-filter)",
61650Sstevel@tonic-gate pw->pw_name, remoteident, realfrom, realto);
61660Sstevel@tonic-gate return;
61670Sstevel@tonic-gate }
61680Sstevel@tonic-gate
61690Sstevel@tonic-gate /*
61700Sstevel@tonic-gate * if rename permission denied and file exists... then deny the user
61710Sstevel@tonic-gate * permission to rename the file.
61720Sstevel@tonic-gate */
61730Sstevel@tonic-gate while (getaclentry("rename", &entry) && ARG0 && ARG1 != NULL) {
61740Sstevel@tonic-gate if (type_match(ARG1))
61750Sstevel@tonic-gate if (anonymous) {
61760Sstevel@tonic-gate if (*ARG0 == 'y')
61770Sstevel@tonic-gate allowed = 1;
61780Sstevel@tonic-gate }
61790Sstevel@tonic-gate else if (*ARG0 == 'n')
61800Sstevel@tonic-gate allowed = 0;
61810Sstevel@tonic-gate }
61820Sstevel@tonic-gate if (!allowed) {
61830Sstevel@tonic-gate if (log_security)
61840Sstevel@tonic-gate if (anonymous)
61850Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
61860Sstevel@tonic-gate guestpw, remoteident, realfrom, realto);
61870Sstevel@tonic-gate else
61880Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
61890Sstevel@tonic-gate pw->pw_name, remoteident, realfrom, realto);
61900Sstevel@tonic-gate reply(553, "%s: Permission denied on server. (rename)", from);
61910Sstevel@tonic-gate return;
61920Sstevel@tonic-gate }
61930Sstevel@tonic-gate
61940Sstevel@tonic-gate
61950Sstevel@tonic-gate #ifdef PARANOID
61960Sstevel@tonic-gate /* Almost forgot about this. Don't allow renaming TO existing files --
61970Sstevel@tonic-gate otherwise someone can rename "trivial" to "warez", and "warez" is gone!
61980Sstevel@tonic-gate XXX: This part really should do the same "overwrite" check as store(). */
61990Sstevel@tonic-gate if (!stat(to, &st)) {
62000Sstevel@tonic-gate if (log_security)
62010Sstevel@tonic-gate if (anonymous)
62020Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
62030Sstevel@tonic-gate guestpw, remoteident, realfrom, realto);
62040Sstevel@tonic-gate else
62050Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
62060Sstevel@tonic-gate pw->pw_name, remoteident, realfrom, realto);
62070Sstevel@tonic-gate reply(550, "%s: Permission denied on server. (rename)", to);
62080Sstevel@tonic-gate return;
62090Sstevel@tonic-gate }
62100Sstevel@tonic-gate #endif
62110Sstevel@tonic-gate if (rename(from, to) < 0) {
62120Sstevel@tonic-gate if (log_security)
62130Sstevel@tonic-gate if (anonymous)
62140Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to rename %s to %s",
62150Sstevel@tonic-gate guestpw, remoteident, realfrom, realto);
62160Sstevel@tonic-gate else
62170Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to rename %s to %s",
62180Sstevel@tonic-gate pw->pw_name, remoteident, realfrom, realto);
62190Sstevel@tonic-gate perror_reply(550, "rename");
62200Sstevel@tonic-gate }
62210Sstevel@tonic-gate else {
62220Sstevel@tonic-gate char frompath[MAXPATHLEN];
62230Sstevel@tonic-gate char topath[MAXPATHLEN];
62240Sstevel@tonic-gate
62250Sstevel@tonic-gate wu_realpath(from, frompath, chroot_path);
62260Sstevel@tonic-gate wu_realpath(to, topath, chroot_path);
62270Sstevel@tonic-gate
62280Sstevel@tonic-gate if (log_security)
62290Sstevel@tonic-gate if (anonymous) {
62300Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s renamed %s to %s", guestpw, remoteident, frompath, topath);
62310Sstevel@tonic-gate }
62320Sstevel@tonic-gate else {
62330Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s renamed %s to %s", pw->pw_name,
62340Sstevel@tonic-gate remoteident, frompath, topath);
62350Sstevel@tonic-gate }
62360Sstevel@tonic-gate ack("RNTO");
62370Sstevel@tonic-gate }
62380Sstevel@tonic-gate }
62390Sstevel@tonic-gate
62400Sstevel@tonic-gate void dolog(struct SOCKSTORAGE *sin)
62410Sstevel@tonic-gate {
62420Sstevel@tonic-gate char *blah;
62430Sstevel@tonic-gate int rval;
62440Sstevel@tonic-gate
62450Sstevel@tonic-gate blah = inet_stop(sin);
62460Sstevel@tonic-gate (void) strncpy(remoteaddr, blah, sizeof(remoteaddr));
62470Sstevel@tonic-gate nameserved = 0;
62480Sstevel@tonic-gate (void) strncpy(remotehost, remoteaddr, sizeof(remotehost));
62490Sstevel@tonic-gate
62500Sstevel@tonic-gate rhlookup = rhostlookup(remoteaddr);
62510Sstevel@tonic-gate if (rhlookup) {
62520Sstevel@tonic-gate if (!strcasecmp(remoteaddr, "0.0.0.0")) {
62530Sstevel@tonic-gate nameserved = 1;
62540Sstevel@tonic-gate strncpy(remotehost, "localhost", sizeof(remotehost));
62550Sstevel@tonic-gate }
62560Sstevel@tonic-gate else {
62570Sstevel@tonic-gate #ifdef DNS_TRYAGAIN
62580Sstevel@tonic-gate int num_dns_tries = 0;
62590Sstevel@tonic-gate /*
62600Sstevel@tonic-gate * 27-Apr-93 EHK/BM
62610Sstevel@tonic-gate * far away connections might take some time to get their IP address
62620Sstevel@tonic-gate * resolved. That's why we try again -- maybe our DNS cache has the
62630Sstevel@tonic-gate * PTR-RR now. This code is sloppy. Far better is to check what the
62640Sstevel@tonic-gate * resolver returned so that in case of error, there's no need to
62650Sstevel@tonic-gate * try again.
62660Sstevel@tonic-gate */
62670Sstevel@tonic-gate dns_again:
62680Sstevel@tonic-gate #endif /* DNS_TRYAGAIN */
62690Sstevel@tonic-gate
62700Sstevel@tonic-gate rval = wu_gethostbyaddr(sin, remotehost, sizeof(remotehost));
62710Sstevel@tonic-gate
62720Sstevel@tonic-gate #ifdef DNS_TRYAGAIN
62730Sstevel@tonic-gate if (!rval && ++num_dns_tries <= 1) {
62740Sstevel@tonic-gate sleep(3);
62750Sstevel@tonic-gate goto dns_again; /* try DNS lookup once more */
62760Sstevel@tonic-gate }
62770Sstevel@tonic-gate #endif /* DNS_TRYAGAIN */
62780Sstevel@tonic-gate
62790Sstevel@tonic-gate if (rval)
62800Sstevel@tonic-gate nameserved = 1;
62810Sstevel@tonic-gate }
62820Sstevel@tonic-gate }
62830Sstevel@tonic-gate
62840Sstevel@tonic-gate remotehost[sizeof(remotehost) - 1] = '\0';
62850Sstevel@tonic-gate sprintf(proctitle, "%s: connected", remotehost);
62860Sstevel@tonic-gate setproctitle("%s", proctitle);
62870Sstevel@tonic-gate
62880Sstevel@tonic-gate wu_authenticate();
62890Sstevel@tonic-gate /* Create a composite source identification string, to improve the logging
62900Sstevel@tonic-gate * when RFC 931 is being used. */
62910Sstevel@tonic-gate {
62920Sstevel@tonic-gate int n = 20 + strlen(remotehost) + strlen(remoteaddr) +
62930Sstevel@tonic-gate (authenticated ? strlen(authuser + 5) : 0);
62940Sstevel@tonic-gate if ((remoteident = malloc(n)) == NULL) {
62950Sstevel@tonic-gate syslog(LOG_ERR, "malloc: %m");
62960Sstevel@tonic-gate #ifndef DEBUG
62970Sstevel@tonic-gate exit(1);
62980Sstevel@tonic-gate #endif
62990Sstevel@tonic-gate }
63000Sstevel@tonic-gate else if (authenticated)
63010Sstevel@tonic-gate sprintf(remoteident, "%s @ %s [%s]",
63020Sstevel@tonic-gate authuser, remotehost, remoteaddr);
63030Sstevel@tonic-gate else
63040Sstevel@tonic-gate sprintf(remoteident, "%s [%s]", remotehost, remoteaddr);
63050Sstevel@tonic-gate }
63060Sstevel@tonic-gate #ifdef DAEMON
63070Sstevel@tonic-gate if (be_daemon && logging)
63080Sstevel@tonic-gate syslog(LOG_INFO, "connection from %s", remoteident);
63090Sstevel@tonic-gate #else
63100Sstevel@tonic-gate #if 0 /* this is redundant unless the caller doesn't do *anything*, and
63110Sstevel@tonic-gate tcpd will pick it up and deal with it better anyways. _H */
63120Sstevel@tonic-gate if (logging)
63130Sstevel@tonic-gate syslog(LOG_INFO, "connection from %s", remoteident);
63140Sstevel@tonic-gate #endif
63150Sstevel@tonic-gate #endif
63160Sstevel@tonic-gate }
63170Sstevel@tonic-gate
63180Sstevel@tonic-gate /* Record logout in wtmp file and exit with supplied status. */
63190Sstevel@tonic-gate
63200Sstevel@tonic-gate void dologout(int status)
63210Sstevel@tonic-gate {
63220Sstevel@tonic-gate /*
63230Sstevel@tonic-gate * Prevent reception of SIGURG from resulting in a resumption
63240Sstevel@tonic-gate * back to the main program loop.
63250Sstevel@tonic-gate */
63260Sstevel@tonic-gate transflag = 0;
63270Sstevel@tonic-gate
63280Sstevel@tonic-gate /*
63290Sstevel@tonic-gate * Cancel any pending alarm request, reception of SIGALRM would cause
63300Sstevel@tonic-gate * dologout() to be called again from the SIGALRM handler toolong().
63310Sstevel@tonic-gate */
63320Sstevel@tonic-gate (void) alarm(0);
63330Sstevel@tonic-gate
63340Sstevel@tonic-gate if (logged_in) {
63350Sstevel@tonic-gate delay_signaling(); /* we can't allow any signals while euid==0: kinch */
63360Sstevel@tonic-gate #if defined(SOLARIS_BSM_AUDIT) && !defined(SOLARIS_NO_AUDIT_FTPD_LOGOUT)
63370Sstevel@tonic-gate audit_ftpd_logout();
63380Sstevel@tonic-gate #endif
63390Sstevel@tonic-gate (void) seteuid((uid_t) 0);
63400Sstevel@tonic-gate if (wtmp_logging)
63410Sstevel@tonic-gate wu_logwtmp(ttyline, pw->pw_name, remotehost, 0);
63420Sstevel@tonic-gate #ifdef USE_PAM
63430Sstevel@tonic-gate if (!anonymous && pamh) {
63440Sstevel@tonic-gate (void) pam_close_session(pamh, 0);
63450Sstevel@tonic-gate (void) pam_end(pamh, PAM_SUCCESS);
63460Sstevel@tonic-gate pamh = (pam_handle_t *)0;
63470Sstevel@tonic-gate }
63480Sstevel@tonic-gate #endif
63490Sstevel@tonic-gate }
63500Sstevel@tonic-gate if (logging)
63510Sstevel@tonic-gate syslog(LOG_INFO, "FTP session closed");
63520Sstevel@tonic-gate if (xferlog)
63530Sstevel@tonic-gate close(xferlog);
63540Sstevel@tonic-gate acl_remove();
63550Sstevel@tonic-gate if (data >= 0)
63560Sstevel@tonic-gate close(data);
63570Sstevel@tonic-gate if (pdata >= 0)
63580Sstevel@tonic-gate close(pdata);
63590Sstevel@tonic-gate #ifdef AFS_AUTH
63600Sstevel@tonic-gate ktc_ForgetAllTokens();
63610Sstevel@tonic-gate #endif
63620Sstevel@tonic-gate /* beware of flushing buffers after a SIGPIPE */
63630Sstevel@tonic-gate _exit(status);
63640Sstevel@tonic-gate }
63650Sstevel@tonic-gate
63660Sstevel@tonic-gate SIGNAL_TYPE myoob(int sig)
63670Sstevel@tonic-gate {
63680Sstevel@tonic-gate char *cp;
63690Sstevel@tonic-gate #ifdef SIGPIPE
63700Sstevel@tonic-gate void (*pipe_handler)();
63710Sstevel@tonic-gate #endif
63720Sstevel@tonic-gate
63730Sstevel@tonic-gate /* only process if transfer occurring */
63740Sstevel@tonic-gate if (!transflag) {
63750Sstevel@tonic-gate #ifdef SIGURG
63760Sstevel@tonic-gate (void) signal(SIGURG, myoob);
63770Sstevel@tonic-gate #endif
63780Sstevel@tonic-gate return;
63790Sstevel@tonic-gate }
63800Sstevel@tonic-gate #ifdef SIGPIPE
63810Sstevel@tonic-gate pipe_handler = signal(SIGPIPE, lostconn);
63820Sstevel@tonic-gate #endif
63830Sstevel@tonic-gate cp = tmpline;
63840Sstevel@tonic-gate if (wu_getline(cp, sizeof(tmpline) - 1, stdin) == NULL) {
63850Sstevel@tonic-gate reply(221, "You could at least say goodbye.");
63860Sstevel@tonic-gate dologout(0);
63870Sstevel@tonic-gate }
63880Sstevel@tonic-gate upper(cp);
63890Sstevel@tonic-gate if (strcasecmp(cp, "ABOR\r\n") == 0) {
63900Sstevel@tonic-gate tmpline[0] = '\0';
63910Sstevel@tonic-gate reply(426, "Transfer aborted. Data connection closed.");
63920Sstevel@tonic-gate reply(226, "Abort successful");
63930Sstevel@tonic-gate #ifdef SIGPIPE
63940Sstevel@tonic-gate (void) signal(SIGPIPE, pipe_handler);
63950Sstevel@tonic-gate #endif
63960Sstevel@tonic-gate #ifdef SIGURG
63970Sstevel@tonic-gate (void) signal(SIGURG, myoob);
63980Sstevel@tonic-gate #endif
63990Sstevel@tonic-gate if (ftwflag > 0) {
64000Sstevel@tonic-gate ftwflag++;
64010Sstevel@tonic-gate return;
64020Sstevel@tonic-gate }
64030Sstevel@tonic-gate wu_longjmp(urgcatch, 1);
64040Sstevel@tonic-gate }
64050Sstevel@tonic-gate if (strcasecmp(cp, "STAT\r\n") == 0) {
64060Sstevel@tonic-gate tmpline[0] = '\0';
64070Sstevel@tonic-gate if (file_size != (off_t) - 1)
64080Sstevel@tonic-gate reply(213, "Status: %" L_FORMAT " of %" L_FORMAT " bytes transferred",
64090Sstevel@tonic-gate byte_count, file_size);
64100Sstevel@tonic-gate else
64110Sstevel@tonic-gate reply(213, "Status: %" L_FORMAT " bytes transferred", byte_count);
64120Sstevel@tonic-gate }
64130Sstevel@tonic-gate #ifdef SIGPIPE
64140Sstevel@tonic-gate (void) signal(SIGPIPE, pipe_handler);
64150Sstevel@tonic-gate #endif
64160Sstevel@tonic-gate #ifdef SIGURG
64170Sstevel@tonic-gate (void) signal(SIGURG, myoob);
64180Sstevel@tonic-gate #endif
64190Sstevel@tonic-gate }
64200Sstevel@tonic-gate
64210Sstevel@tonic-gate /* Note: a response of 425 is not mentioned as a possible response to the
64220Sstevel@tonic-gate * PASV command in RFC959. However, it has been blessed as a legitimate
64230Sstevel@tonic-gate * response by Jon Postel in a telephone conversation with Rick Adams on 25
64240Sstevel@tonic-gate * Jan 89. */
64250Sstevel@tonic-gate
64260Sstevel@tonic-gate void passive(int passive_mode, int proto)
64270Sstevel@tonic-gate {
64280Sstevel@tonic-gate /* First prime number after 2^n where 4 <= n <= 16 */
64290Sstevel@tonic-gate static int primes[] = {17,37,67,131,257,521,1031,2053,4099,8209,16411,32771,65537,0};
64300Sstevel@tonic-gate static int prime = 0;
64310Sstevel@tonic-gate static int range;
64320Sstevel@tonic-gate #if defined(UNIXWARE) || defined(AIX)
64330Sstevel@tonic-gate size_t len;
64340Sstevel@tonic-gate #else
64350Sstevel@tonic-gate int len;
64360Sstevel@tonic-gate #endif
64370Sstevel@tonic-gate int bind_error, serrno;
64380Sstevel@tonic-gate int on = 1;
64390Sstevel@tonic-gate int i, j, inc, val;
64400Sstevel@tonic-gate unsigned short port;
64410Sstevel@tonic-gate register char *p, *a;
64420Sstevel@tonic-gate struct SOCKSTORAGE *reply_addr;
64430Sstevel@tonic-gate struct timeval tv;
64440Sstevel@tonic-gate #ifdef INET6
64450Sstevel@tonic-gate int isv4 = 0;
64460Sstevel@tonic-gate #endif
64470Sstevel@tonic-gate
64480Sstevel@tonic-gate /* H* fix: if we already *have* a passive socket, close it first. Prevents
64490Sstevel@tonic-gate a whole variety of entertaining clogging attacks. */
64500Sstevel@tonic-gate if (pdata >= 0) {
64510Sstevel@tonic-gate close(pdata);
64520Sstevel@tonic-gate pdata = -1;
64530Sstevel@tonic-gate }
64540Sstevel@tonic-gate if (!logged_in) {
64550Sstevel@tonic-gate reply(530, "Login with USER first.");
64560Sstevel@tonic-gate return;
64570Sstevel@tonic-gate }
64580Sstevel@tonic-gate #ifdef INET6
64590Sstevel@tonic-gate switch (proto) {
64600Sstevel@tonic-gate case 0:
64610Sstevel@tonic-gate if ((passive_mode == TYPE_PASV) && (SOCK_FAMILY(ctrl_addr) == AF_INET6)
64620Sstevel@tonic-gate && !ctrl_v4mapped) {
64630Sstevel@tonic-gate reply(501, "Network protocol mismatch");
64640Sstevel@tonic-gate return;
64650Sstevel@tonic-gate }
64660Sstevel@tonic-gate else
64670Sstevel@tonic-gate pasv_addr = ctrl_addr;
64680Sstevel@tonic-gate break;
64690Sstevel@tonic-gate case 1:
64700Sstevel@tonic-gate if (SOCK_FAMILY(ctrl_addr) == AF_INET)
64710Sstevel@tonic-gate pasv_addr = ctrl_addr;
64720Sstevel@tonic-gate else if (ctrl_v4mapped) {
64730Sstevel@tonic-gate struct sockaddr_in6 *ctrl_sin6 = (struct sockaddr_in6 *)&ctrl_addr;
64740Sstevel@tonic-gate struct sockaddr_in *pasv_sin = (struct sockaddr_in *)&pasv_addr;
64750Sstevel@tonic-gate
64760Sstevel@tonic-gate SET_SOCK_FAMILY(pasv_addr, AF_INET);
64770Sstevel@tonic-gate memcpy(&pasv_sin->sin_addr, &ctrl_sin6->sin6_addr.s6_addr[12],
64780Sstevel@tonic-gate sizeof(struct in_addr));
64790Sstevel@tonic-gate }
64800Sstevel@tonic-gate else {
64810Sstevel@tonic-gate reply(522, "Network protocol mismatch, use (2)");
64820Sstevel@tonic-gate return;
64830Sstevel@tonic-gate }
64840Sstevel@tonic-gate break;
64850Sstevel@tonic-gate case 2:
64860Sstevel@tonic-gate if ((SOCK_FAMILY(ctrl_addr) == AF_INET6) && !ctrl_v4mapped)
64870Sstevel@tonic-gate pasv_addr = ctrl_addr;
64880Sstevel@tonic-gate else {
64890Sstevel@tonic-gate reply(522, "Network protocol mismatch, use (1)");
64900Sstevel@tonic-gate return;
64910Sstevel@tonic-gate }
64920Sstevel@tonic-gate break;
64930Sstevel@tonic-gate default:
64940Sstevel@tonic-gate reply(522, "Network protocol not supported, use (1,2)");
64950Sstevel@tonic-gate return;
64960Sstevel@tonic-gate }
64970Sstevel@tonic-gate #else
64980Sstevel@tonic-gate pasv_addr = ctrl_addr;
64990Sstevel@tonic-gate #endif
65000Sstevel@tonic-gate
65010Sstevel@tonic-gate if (passive_port_min == 0 && passive_port_max == 0) {
65020Sstevel@tonic-gate /* let the kernel allocate the port */
65030Sstevel@tonic-gate SET_SOCK_PORT(pasv_addr, 0);
65040Sstevel@tonic-gate }
65050Sstevel@tonic-gate else if (prime == 0) {
65060Sstevel@tonic-gate range = passive_port_max - passive_port_min + 1;
65070Sstevel@tonic-gate
65080Sstevel@tonic-gate /* find the first prime greater than the range in the primes list */
65090Sstevel@tonic-gate for (i = 0; primes[i] != 0 && range >= primes[i]; i++)
65100Sstevel@tonic-gate ;
65110Sstevel@tonic-gate /* shouldn't happen, but check just in case */
65120Sstevel@tonic-gate if (primes[i] == 0) {
65130Sstevel@tonic-gate syslog(LOG_ERR, "passive ports range too large %d-%d", passive_port_min, passive_port_max);
65140Sstevel@tonic-gate /* let the kernel allocate the port */
65150Sstevel@tonic-gate SET_SOCK_PORT(pasv_addr, 0);
65160Sstevel@tonic-gate }
65170Sstevel@tonic-gate else
65180Sstevel@tonic-gate prime = primes[i];
65190Sstevel@tonic-gate }
65200Sstevel@tonic-gate len = SOCK_LEN(pasv_addr);
65210Sstevel@tonic-gate
65220Sstevel@tonic-gate port_priv_on(0); /* necessary as port can be < 1024 */
65230Sstevel@tonic-gate pdata = socket(SOCK_FAMILY(pasv_addr), SOCK_STREAM, 0);
65240Sstevel@tonic-gate if (pdata < 0) {
65250Sstevel@tonic-gate serrno = errno;
65260Sstevel@tonic-gate port_priv_off((uid_t) pw->pw_uid);
65270Sstevel@tonic-gate errno = serrno;
65280Sstevel@tonic-gate perror_reply(425, "Can't open passive connection");
65290Sstevel@tonic-gate return;
65300Sstevel@tonic-gate }
65310Sstevel@tonic-gate if (keepalive)
65320Sstevel@tonic-gate (void) setsockopt(pdata, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof(on));
65330Sstevel@tonic-gate if (TCPwindowsize) {
65340Sstevel@tonic-gate (void) setsockopt(pdata, SOL_SOCKET, SO_SNDBUF, (char *) &TCPwindowsize, sizeof(TCPwindowsize));
65350Sstevel@tonic-gate (void) setsockopt(pdata, SOL_SOCKET, SO_RCVBUF, (char *) &TCPwindowsize, sizeof(TCPwindowsize));
65360Sstevel@tonic-gate }
65370Sstevel@tonic-gate
65380Sstevel@tonic-gate bind_error = -1;
65390Sstevel@tonic-gate errno = EADDRINUSE;
65400Sstevel@tonic-gate
65410Sstevel@tonic-gate /* try each port in the specified range a maximum of 3 times */
65424283Ssatishk for (i = 0; i < 3 && bind_error != 0 && \
65434283Ssatishk ((errno == EADDRINUSE) || (errno == EACCES)); i++) {
65440Sstevel@tonic-gate if (i > 0)
65450Sstevel@tonic-gate sleep(i);
65460Sstevel@tonic-gate if (SOCK_PORT(pasv_addr) == 0)
65470Sstevel@tonic-gate bind_error = bind(pdata, (struct sockaddr *) &pasv_addr, len);
65480Sstevel@tonic-gate else {
65490Sstevel@tonic-gate gettimeofday(&tv, NULL);
65500Sstevel@tonic-gate srand(tv.tv_usec + tv.tv_sec);
65510Sstevel@tonic-gate inc = 1 + (int) ((1.0 * (prime - 1) * rand()) / (RAND_MAX + 1.0));
65520Sstevel@tonic-gate val = (int) ((1.0 * range * rand()) / (RAND_MAX + 1.0));
65530Sstevel@tonic-gate /*
65540Sstevel@tonic-gate * Using the modulus operator with a prime number allows us to
65550Sstevel@tonic-gate * try each port in the range once.
65560Sstevel@tonic-gate */
65574283Ssatishk for (j = 0; j < range && bind_error != 0 && \
65584283Ssatishk ((errno == EADDRINUSE) || (errno == EACCES)); j++) {
65590Sstevel@tonic-gate while ((val = ((val + inc) % prime)) >= range)
65600Sstevel@tonic-gate ;
65610Sstevel@tonic-gate SET_SOCK_PORT(pasv_addr, htons(val + passive_port_min));
65620Sstevel@tonic-gate bind_error = bind(pdata, (struct sockaddr *) &pasv_addr, len);
65630Sstevel@tonic-gate }
65640Sstevel@tonic-gate }
65650Sstevel@tonic-gate }
65660Sstevel@tonic-gate serrno = errno;
65670Sstevel@tonic-gate port_priv_off((uid_t) pw->pw_uid);
65680Sstevel@tonic-gate if (bind_error != 0) {
65690Sstevel@tonic-gate errno = serrno;
65700Sstevel@tonic-gate goto pasv_error;
65710Sstevel@tonic-gate }
65720Sstevel@tonic-gate
65730Sstevel@tonic-gate /* if the kernel allocated the port, find out which one */
65740Sstevel@tonic-gate if ((SOCK_PORT(pasv_addr) == 0) &&
65750Sstevel@tonic-gate (getsockname(pdata, (struct sockaddr *) &pasv_addr, &len) < 0))
65760Sstevel@tonic-gate goto pasv_error;
65770Sstevel@tonic-gate
65780Sstevel@tonic-gate if (listen(pdata, 1) < 0)
65790Sstevel@tonic-gate goto pasv_error;
65800Sstevel@tonic-gate usedefault = 1;
65810Sstevel@tonic-gate if (route_vectored)
65820Sstevel@tonic-gate reply_addr = &vect_addr;
65830Sstevel@tonic-gate else
65840Sstevel@tonic-gate reply_addr = &pasv_addr;
65850Sstevel@tonic-gate a = (char *) SOCK_ADDR(*reply_addr);
65860Sstevel@tonic-gate port = SOCK_PORT(pasv_addr);
65870Sstevel@tonic-gate p = (char *) &port;
65880Sstevel@tonic-gate
65890Sstevel@tonic-gate #define UC(b) (((int) b) & 0xff)
65900Sstevel@tonic-gate
65910Sstevel@tonic-gate if (debug) {
65920Sstevel@tonic-gate char *s = calloc(128 + strlen(remoteident), sizeof(char));
65930Sstevel@tonic-gate if (s) {
65940Sstevel@tonic-gate int i = ntohs(port);
65950Sstevel@tonic-gate sprintf(s, "PASV port %i assigned to %s", i, remoteident);
65960Sstevel@tonic-gate syslog(LOG_DEBUG, "%s", s);
65970Sstevel@tonic-gate free(s);
65980Sstevel@tonic-gate }
65990Sstevel@tonic-gate }
66000Sstevel@tonic-gate #ifdef INET6
66010Sstevel@tonic-gate if (SOCK_FAMILY(*reply_addr) == AF_INET)
66020Sstevel@tonic-gate isv4 = 1;
66030Sstevel@tonic-gate else if (IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)reply_addr)->sin6_addr)) {
66040Sstevel@tonic-gate isv4 = 1;
66050Sstevel@tonic-gate a += 12; /* move to the IPv4 part of an IPv4-mapped IPv6 address */
66060Sstevel@tonic-gate }
66070Sstevel@tonic-gate switch (passive_mode) {
66080Sstevel@tonic-gate case TYPE_PASV:
66090Sstevel@tonic-gate reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)",
66100Sstevel@tonic-gate UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
66110Sstevel@tonic-gate return;
66120Sstevel@tonic-gate case TYPE_EPSV:
66130Sstevel@tonic-gate reply(229, "Entering Extended Passive Mode (|||%d|)", ntohs(port));
66140Sstevel@tonic-gate return;
66150Sstevel@tonic-gate case TYPE_LPSV:
66160Sstevel@tonic-gate if (isv4) {
66170Sstevel@tonic-gate reply(228, "Entering Long Passive Mode "
66180Sstevel@tonic-gate "(%d,%d,%d,%d,%d,%d,%d,%d,%d)",
66190Sstevel@tonic-gate 4, 4, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
66200Sstevel@tonic-gate 2, UC(p[0]), UC(p[1]));
66210Sstevel@tonic-gate }
66220Sstevel@tonic-gate else {
66230Sstevel@tonic-gate reply(228, "Entering Long Passive Mode "
66240Sstevel@tonic-gate "(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,"
66250Sstevel@tonic-gate "%d,%d,%d,%d,%d)", 6, 16,
66260Sstevel@tonic-gate UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
66270Sstevel@tonic-gate UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
66280Sstevel@tonic-gate UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
66290Sstevel@tonic-gate UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
66300Sstevel@tonic-gate 2, UC(p[0]), UC(p[1]));
66310Sstevel@tonic-gate }
66320Sstevel@tonic-gate return;
66330Sstevel@tonic-gate }
66340Sstevel@tonic-gate #else
66350Sstevel@tonic-gate reply(227, "Entering Passive Mode (%d,%d,%d,%d,%d,%d)", UC(a[0]),
66360Sstevel@tonic-gate UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
66370Sstevel@tonic-gate return;
66380Sstevel@tonic-gate #endif /* INET6 */
66390Sstevel@tonic-gate
66400Sstevel@tonic-gate pasv_error:
66410Sstevel@tonic-gate perror_reply(425, "Can't open passive connection");
66420Sstevel@tonic-gate (void) close(pdata);
66430Sstevel@tonic-gate pdata = -1;
66440Sstevel@tonic-gate if (debug) {
66450Sstevel@tonic-gate char *s = calloc(128 + strlen(remoteident), sizeof(char));
66460Sstevel@tonic-gate if (s) {
66470Sstevel@tonic-gate sprintf(s, "PASV port assignment assigned for %s", remoteident);
66480Sstevel@tonic-gate syslog(LOG_DEBUG, "%s", s);
66490Sstevel@tonic-gate free(s);
66500Sstevel@tonic-gate }
66510Sstevel@tonic-gate }
66520Sstevel@tonic-gate return;
66530Sstevel@tonic-gate }
66540Sstevel@tonic-gate
66550Sstevel@tonic-gate /*
66560Sstevel@tonic-gate * Generate unique name for file with basename "local". The file named
66570Sstevel@tonic-gate * "local" is already known to exist. Generates failure reply on error.
66580Sstevel@tonic-gate */
66590Sstevel@tonic-gate char *gunique(char *local)
66600Sstevel@tonic-gate {
66610Sstevel@tonic-gate static char new[MAXPATHLEN];
66620Sstevel@tonic-gate struct stat st;
66630Sstevel@tonic-gate char *cp = strrchr(local, '/');
66640Sstevel@tonic-gate int count = 0;
66650Sstevel@tonic-gate
66660Sstevel@tonic-gate if (cp)
66670Sstevel@tonic-gate *cp = '\0';
66680Sstevel@tonic-gate if (stat(cp ? local : ".", &st) < 0) {
66690Sstevel@tonic-gate perror_reply(553, cp ? local : ".");
66700Sstevel@tonic-gate return ((char *) 0);
66710Sstevel@tonic-gate }
66720Sstevel@tonic-gate if (cp)
66730Sstevel@tonic-gate *cp = '/';
66740Sstevel@tonic-gate (void) strncpy(new, local, (sizeof new) - 3);
66750Sstevel@tonic-gate new[sizeof(new) - 3] = '\0';
66760Sstevel@tonic-gate cp = new + strlen(new);
66770Sstevel@tonic-gate *cp++ = '.';
66780Sstevel@tonic-gate for (count = 1; count < 100; count++) {
66790Sstevel@tonic-gate if (count == 10) {
66800Sstevel@tonic-gate cp -= 2;
66810Sstevel@tonic-gate *cp++ = '.';
66820Sstevel@tonic-gate }
66830Sstevel@tonic-gate (void) sprintf(cp, "%d", count);
66840Sstevel@tonic-gate if (stat(new, &st) < 0)
66850Sstevel@tonic-gate return (new);
66860Sstevel@tonic-gate }
66870Sstevel@tonic-gate reply(452, "Unique file name cannot be created.");
66880Sstevel@tonic-gate return ((char *) 0);
66890Sstevel@tonic-gate }
66900Sstevel@tonic-gate
66910Sstevel@tonic-gate /* Format and send reply containing system error number. */
66920Sstevel@tonic-gate
66930Sstevel@tonic-gate void perror_reply(int code, char *string)
66940Sstevel@tonic-gate {
66950Sstevel@tonic-gate /*
66960Sstevel@tonic-gate * If restricted user and string starts with home dir path, strip it off
66970Sstevel@tonic-gate * and return only the relative path.
66980Sstevel@tonic-gate */
66990Sstevel@tonic-gate if (restricted_user && (home != NULL) && (home[0] != '\0')) {
67000Sstevel@tonic-gate size_t len = strlen (home);
67010Sstevel@tonic-gate if (strncmp (home, string, len) == 0) {
67020Sstevel@tonic-gate if (string[len - 1] == '/')
67030Sstevel@tonic-gate string += len - 1;
67040Sstevel@tonic-gate else if (string[len] == '/')
67050Sstevel@tonic-gate string += len;
67060Sstevel@tonic-gate else if (string[len] == '\0')
67070Sstevel@tonic-gate string = "/";
67080Sstevel@tonic-gate }
67090Sstevel@tonic-gate }
67100Sstevel@tonic-gate reply(code, "%s: %s.", string, strerror(errno));
67110Sstevel@tonic-gate }
67120Sstevel@tonic-gate
67130Sstevel@tonic-gate static char *onefile[] =
67140Sstevel@tonic-gate {"", 0};
67150Sstevel@tonic-gate
6716*12333SMilan.Jurik@Sun.COM extern char **ftpglob(register char *v, boolean_t check_ncargs);
67170Sstevel@tonic-gate extern char *globerr;
67180Sstevel@tonic-gate
67190Sstevel@tonic-gate void send_file_list(char *whichfiles)
67200Sstevel@tonic-gate {
67210Sstevel@tonic-gate /* static so not clobbered by longjmp(), volatile would also work */
67220Sstevel@tonic-gate static FILE *dout;
67230Sstevel@tonic-gate static DIR *dirp;
67240Sstevel@tonic-gate static char **sdirlist;
67250Sstevel@tonic-gate static char *wildcard = NULL;
67260Sstevel@tonic-gate
67270Sstevel@tonic-gate struct stat st;
67280Sstevel@tonic-gate
67290Sstevel@tonic-gate register char **dirlist, *dirname;
67300Sstevel@tonic-gate int simple = 0;
67310Sstevel@tonic-gate int statret;
67320Sstevel@tonic-gate /* This is ANSI/ISO C .. strpbrk should be in <string.h> which we've
67330Sstevel@tonic-gate ** already included so we don't need the following line. 'sides, it
67340Sstevel@tonic-gate ** breaks the GNU EGCS C compiler
67350Sstevel@tonic-gate ** extern char *strpbrk(const char *, const char *);
67360Sstevel@tonic-gate */
67370Sstevel@tonic-gate
67380Sstevel@tonic-gate #ifdef TRANSFER_COUNT
67390Sstevel@tonic-gate #ifdef TRANSFER_LIMIT
67400Sstevel@tonic-gate if (((file_limit_raw_out > 0) && (xfer_count_out >= file_limit_raw_out))
67410Sstevel@tonic-gate || ((file_limit_raw_total > 0) && (xfer_count_total >= file_limit_raw_total))
67420Sstevel@tonic-gate || ((data_limit_raw_out > 0) && (byte_count_out >= data_limit_raw_out))
67430Sstevel@tonic-gate || ((data_limit_raw_total > 0) && (byte_count_total >= data_limit_raw_total))) {
67440Sstevel@tonic-gate if (log_security)
67450Sstevel@tonic-gate if (anonymous)
67460Sstevel@tonic-gate syslog(LOG_NOTICE, "anonymous(%s) of %s tried to list files (Transfer limits exceeded)",
67470Sstevel@tonic-gate guestpw, remoteident);
67480Sstevel@tonic-gate else
67490Sstevel@tonic-gate syslog(LOG_NOTICE, "%s of %s tried to list files (Transfer limits exceeded)",
67500Sstevel@tonic-gate pw->pw_name, remoteident);
67510Sstevel@tonic-gate reply(553, "Permission denied on server. (Transfer limits exceeded)");
67520Sstevel@tonic-gate return;
67530Sstevel@tonic-gate }
67540Sstevel@tonic-gate #endif
67550Sstevel@tonic-gate #endif
67560Sstevel@tonic-gate
67570Sstevel@tonic-gate draconian_FILE = NULL;
67580Sstevel@tonic-gate dout = NULL;
67590Sstevel@tonic-gate dirp = NULL;
67600Sstevel@tonic-gate sdirlist = NULL;
67610Sstevel@tonic-gate wildcard = NULL;
67620Sstevel@tonic-gate if (strpbrk(whichfiles, "~{[*?") == NULL) {
67630Sstevel@tonic-gate if (whichfiles[0] == '\0') {
67640Sstevel@tonic-gate wildcard = strdup("*");
67650Sstevel@tonic-gate if (wildcard == NULL) {
67660Sstevel@tonic-gate reply(550, "Memory allocation error");
67670Sstevel@tonic-gate goto globfree;
67680Sstevel@tonic-gate }
67690Sstevel@tonic-gate whichfiles = wildcard;
67700Sstevel@tonic-gate }
67710Sstevel@tonic-gate else {
67720Sstevel@tonic-gate if (statret=stat(whichfiles, &st) < 0)
67730Sstevel@tonic-gate statret=lstat(whichfiles, &st); /* Check if it's a dangling symlink */
67740Sstevel@tonic-gate if (statret >= 0) {
67750Sstevel@tonic-gate if ((st.st_mode & S_IFMT) == S_IFDIR) {
67760Sstevel@tonic-gate wildcard = malloc(strlen(whichfiles) + 3);
67770Sstevel@tonic-gate if (wildcard == NULL) {
67780Sstevel@tonic-gate reply(550, "Memory allocation error");
67790Sstevel@tonic-gate goto globfree;
67800Sstevel@tonic-gate }
67810Sstevel@tonic-gate strcpy(wildcard, whichfiles);
67820Sstevel@tonic-gate strcat(wildcard, "/*");
67830Sstevel@tonic-gate whichfiles = wildcard;
67840Sstevel@tonic-gate }
67850Sstevel@tonic-gate }
67860Sstevel@tonic-gate }
67870Sstevel@tonic-gate }
67880Sstevel@tonic-gate if (strpbrk(whichfiles, "~{[*?") != NULL) {
67890Sstevel@tonic-gate globerr = NULL;
6790*12333SMilan.Jurik@Sun.COM dirlist = ftpglob(whichfiles, B_FALSE);
67910Sstevel@tonic-gate sdirlist = dirlist; /* save to free later */
67920Sstevel@tonic-gate if (globerr != NULL) {
67930Sstevel@tonic-gate reply(550, "%s", globerr);
67940Sstevel@tonic-gate goto globfree;
67950Sstevel@tonic-gate }
67960Sstevel@tonic-gate else if (dirlist == NULL) {
67970Sstevel@tonic-gate errno = ENOENT;
67980Sstevel@tonic-gate perror_reply(550, whichfiles);
67990Sstevel@tonic-gate goto globfree;
68000Sstevel@tonic-gate }
68010Sstevel@tonic-gate }
68020Sstevel@tonic-gate else {
68030Sstevel@tonic-gate onefile[0] = whichfiles;
68040Sstevel@tonic-gate dirlist = onefile;
68050Sstevel@tonic-gate simple = 1;
68060Sstevel@tonic-gate }
68070Sstevel@tonic-gate
68080Sstevel@tonic-gate if (wu_setjmp(urgcatch)) {
68090Sstevel@tonic-gate transflag = 0;
68100Sstevel@tonic-gate if (dout != NULL)
68110Sstevel@tonic-gate (void) fclose(dout);
68120Sstevel@tonic-gate if (dirp != NULL)
68130Sstevel@tonic-gate (void) closedir(dirp);
68140Sstevel@tonic-gate data = -1;
68150Sstevel@tonic-gate pdata = -1;
68160Sstevel@tonic-gate goto globfree;
68170Sstevel@tonic-gate }
68180Sstevel@tonic-gate while ((dirname = *dirlist++) != NULL) {
68190Sstevel@tonic-gate statret=stat(dirname, &st);
68200Sstevel@tonic-gate if (statret < 0)
68210Sstevel@tonic-gate statret=lstat(dirname, &st); /* Could be a dangling symlink */
68220Sstevel@tonic-gate if (statret < 0) {
68230Sstevel@tonic-gate /* If user typed "ls -l", etc, and the client used NLST, do what
68240Sstevel@tonic-gate * the user meant. */
68250Sstevel@tonic-gate if (dirname[0] == '-' && *dirlist == NULL && transflag == 0) {
68260Sstevel@tonic-gate retrieve_is_data = 0;
68270Sstevel@tonic-gate #ifndef INTERNAL_LS
68280Sstevel@tonic-gate retrieve(ls_plain, dirname);
68290Sstevel@tonic-gate #else
68300Sstevel@tonic-gate ls(dirname, 1);
68310Sstevel@tonic-gate #endif
68320Sstevel@tonic-gate retrieve_is_data = 1;
68330Sstevel@tonic-gate goto globfree;
68340Sstevel@tonic-gate }
68350Sstevel@tonic-gate perror_reply(550, dirname);
68360Sstevel@tonic-gate if (dout != NULL) {
68370Sstevel@tonic-gate (void) fclose(dout);
68380Sstevel@tonic-gate transflag = 0;
68390Sstevel@tonic-gate data = -1;
68400Sstevel@tonic-gate pdata = -1;
68410Sstevel@tonic-gate }
68420Sstevel@tonic-gate goto globfree;
68430Sstevel@tonic-gate }
68440Sstevel@tonic-gate #ifndef NLST_SHOWS_DIRS
68450Sstevel@tonic-gate if ((st.st_mode & S_IFMT) != S_IFDIR)
68460Sstevel@tonic-gate #endif
68470Sstevel@tonic-gate {
68480Sstevel@tonic-gate if (dout == NULL) {
68490Sstevel@tonic-gate dout = dataconn("file list", (off_t) - 1, "w");
68500Sstevel@tonic-gate if (dout == NULL)
68510Sstevel@tonic-gate goto globfree;
68520Sstevel@tonic-gate transflag++;
68530Sstevel@tonic-gate draconian_FILE = dout;
68540Sstevel@tonic-gate }
68550Sstevel@tonic-gate if (draconian_FILE != NULL) {
68560Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
68570Sstevel@tonic-gate alarm(timeout_data);
68580Sstevel@tonic-gate #if defined(USE_GSS)
68590Sstevel@tonic-gate (void) sec_fprintf(dout, "%s%s\n", dirname,
68600Sstevel@tonic-gate type == TYPE_A ? "\r" : "");
68610Sstevel@tonic-gate #else
68620Sstevel@tonic-gate fprintf(dout, "%s%s\n", dirname,
68630Sstevel@tonic-gate type == TYPE_A ? "\r" : "");
68640Sstevel@tonic-gate #endif /* USE_GSS */
68650Sstevel@tonic-gate }
68660Sstevel@tonic-gate byte_count += strlen(dirname) + 1;
68670Sstevel@tonic-gate #ifdef TRANSFER_COUNT
68680Sstevel@tonic-gate byte_count_total += strlen(dirname) + 1;
68690Sstevel@tonic-gate byte_count_out += strlen(dirname) + 1;
68700Sstevel@tonic-gate if (type == TYPE_A) {
68710Sstevel@tonic-gate byte_count_total++;
68720Sstevel@tonic-gate byte_count_out++;
68730Sstevel@tonic-gate }
68740Sstevel@tonic-gate #endif
68750Sstevel@tonic-gate }
68760Sstevel@tonic-gate }
68770Sstevel@tonic-gate
68780Sstevel@tonic-gate if (dout != NULL) {
68790Sstevel@tonic-gate if (draconian_FILE != NULL) {
68800Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
68810Sstevel@tonic-gate alarm(timeout_data);
68820Sstevel@tonic-gate #if defined(USE_GSS)
68830Sstevel@tonic-gate if (sec_fflush(dout) < 0) {
68840Sstevel@tonic-gate alarm(0);
68850Sstevel@tonic-gate perror_reply(550, "Data connection");
68860Sstevel@tonic-gate goto sfl_cleanup; /* send file list cleanup */
68870Sstevel@tonic-gate }
68880Sstevel@tonic-gate #else
68890Sstevel@tonic-gate fflush(dout);
68900Sstevel@tonic-gate #endif /* USE_GSS */
68910Sstevel@tonic-gate }
68920Sstevel@tonic-gate if (draconian_FILE != NULL) {
68930Sstevel@tonic-gate (void) signal(SIGALRM, draconian_alarm_signal);
68940Sstevel@tonic-gate alarm(timeout_data);
68950Sstevel@tonic-gate socket_flush_wait(dout);
68960Sstevel@tonic-gate }
68970Sstevel@tonic-gate }
68980Sstevel@tonic-gate if (dout == NULL)
68990Sstevel@tonic-gate reply(550, "No files found.");
69000Sstevel@tonic-gate else if ((draconian_FILE == NULL) || ferror(dout) != 0) {
69010Sstevel@tonic-gate alarm(0);
69020Sstevel@tonic-gate perror_reply(550, "Data connection");
69030Sstevel@tonic-gate }
69040Sstevel@tonic-gate else {
69050Sstevel@tonic-gate #ifdef TRANSFER_COUNT
69060Sstevel@tonic-gate xfer_count_total++;
69070Sstevel@tonic-gate xfer_count_out++;
69080Sstevel@tonic-gate #endif
69090Sstevel@tonic-gate alarm(0);
69100Sstevel@tonic-gate reply(226, "Transfer complete.");
69110Sstevel@tonic-gate }
69120Sstevel@tonic-gate sfl_cleanup:
69130Sstevel@tonic-gate transflag = 0;
69140Sstevel@tonic-gate if ((dout != NULL) && (draconian_FILE != NULL))
69150Sstevel@tonic-gate (void) fclose(dout);
69160Sstevel@tonic-gate data = -1;
69170Sstevel@tonic-gate pdata = -1;
69180Sstevel@tonic-gate globfree:
69190Sstevel@tonic-gate if (wildcard != NULL) {
69200Sstevel@tonic-gate free(wildcard);
69210Sstevel@tonic-gate wildcard = NULL;
69220Sstevel@tonic-gate }
69230Sstevel@tonic-gate if (sdirlist) {
69240Sstevel@tonic-gate blkfree(sdirlist);
69250Sstevel@tonic-gate free((char *) sdirlist);
69260Sstevel@tonic-gate }
69270Sstevel@tonic-gate }
69280Sstevel@tonic-gate
69290Sstevel@tonic-gate /*
69300Sstevel@tonic-gate ** SETPROCTITLE -- set process title for ps
69310Sstevel@tonic-gate **
69320Sstevel@tonic-gate ** Parameters:
69330Sstevel@tonic-gate ** fmt -- a printf style format string.
69340Sstevel@tonic-gate ** a, b, c -- possible parameters to fmt.
69350Sstevel@tonic-gate **
69360Sstevel@tonic-gate ** Returns:
69370Sstevel@tonic-gate ** none.
69380Sstevel@tonic-gate **
69390Sstevel@tonic-gate ** Side Effects:
69400Sstevel@tonic-gate ** Clobbers argv of our main procedure so ps(1) will
69410Sstevel@tonic-gate ** display the title.
69420Sstevel@tonic-gate */
69430Sstevel@tonic-gate
69440Sstevel@tonic-gate #define SPT_NONE 0 /* don't use it at all */
69450Sstevel@tonic-gate #define SPT_REUSEARGV 1 /* cover argv with title information */
69460Sstevel@tonic-gate #define SPT_BUILTIN 2 /* use libc builtin */
69470Sstevel@tonic-gate #define SPT_PSTAT 3 /* use pstat(PSTAT_SETCMD, ...) */
69480Sstevel@tonic-gate #define SPT_PSSTRINGS 4 /* use PS_STRINGS->... */
69490Sstevel@tonic-gate #define SPT_SYSMIPS 5 /* use sysmips() supported by NEWS-OS 6 */
69500Sstevel@tonic-gate #define SPT_SCO 6 /* write kernel u. area */
69510Sstevel@tonic-gate #define SPT_CHANGEARGV 7 /* write our own strings into argv[] */
69520Sstevel@tonic-gate #define MAXLINE 2048 /* max line length for setproctitle */
69530Sstevel@tonic-gate #define SPACELEFT(buf, ptr) (sizeof buf - ((ptr) - buf))
69540Sstevel@tonic-gate
69550Sstevel@tonic-gate #ifndef SPT_TYPE
69560Sstevel@tonic-gate #define SPT_TYPE SPT_REUSEARGV
69570Sstevel@tonic-gate #endif
69580Sstevel@tonic-gate
69590Sstevel@tonic-gate #if SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN
69600Sstevel@tonic-gate
69610Sstevel@tonic-gate #if SPT_TYPE == SPT_PSTAT
69620Sstevel@tonic-gate #include <sys/pstat.h>
69630Sstevel@tonic-gate #endif
69640Sstevel@tonic-gate #if SPT_TYPE == SPT_PSSTRINGS
69650Sstevel@tonic-gate #include <machine/vmparam.h>
69660Sstevel@tonic-gate #include <sys/exec.h>
69670Sstevel@tonic-gate #ifndef PS_STRINGS /* hmmmm.... apparently not available after all */
69680Sstevel@tonic-gate #undef SPT_TYPE
69690Sstevel@tonic-gate #define SPT_TYPE SPT_REUSEARGV
69700Sstevel@tonic-gate #else
69710Sstevel@tonic-gate #ifndef NKPDE /* FreeBSD 2.0 */
69720Sstevel@tonic-gate #define NKPDE 63
69730Sstevel@tonic-gate typedef unsigned int *pt_entry_t;
69740Sstevel@tonic-gate #endif
69750Sstevel@tonic-gate #endif
69760Sstevel@tonic-gate #endif
69770Sstevel@tonic-gate
69780Sstevel@tonic-gate #if SPT_TYPE == SPT_PSSTRINGS || SPT_TYPE == SPT_CHANGEARGV
69790Sstevel@tonic-gate #define SETPROC_STATIC static
69800Sstevel@tonic-gate #else
69810Sstevel@tonic-gate #define SETPROC_STATIC
69820Sstevel@tonic-gate #endif
69830Sstevel@tonic-gate
69840Sstevel@tonic-gate #if SPT_TYPE == SPT_SYSMIPS
69850Sstevel@tonic-gate #include <sys/sysmips.h>
69860Sstevel@tonic-gate #include <sys/sysnews.h>
69870Sstevel@tonic-gate #endif
69880Sstevel@tonic-gate
69890Sstevel@tonic-gate #if SPT_TYPE == SPT_SCO
69900Sstevel@tonic-gate #ifdef UNIXWARE
69910Sstevel@tonic-gate #include <sys/exec.h>
69920Sstevel@tonic-gate #include <sys/ksym.h>
69930Sstevel@tonic-gate #include <sys/proc.h>
69940Sstevel@tonic-gate #include <sys/user.h>
69950Sstevel@tonic-gate #else /* UNIXWARE */
69960Sstevel@tonic-gate #include <sys/immu.h>
69970Sstevel@tonic-gate #include <sys/dir.h>
69980Sstevel@tonic-gate #include <sys/user.h>
69990Sstevel@tonic-gate #include <sys/fs/s5param.h>
70000Sstevel@tonic-gate #endif /* UNIXWARE */
70010Sstevel@tonic-gate #if PSARGSZ > MAXLINE
70020Sstevel@tonic-gate #define SPT_BUFSIZE PSARGSZ
70030Sstevel@tonic-gate #endif
70040Sstevel@tonic-gate #ifndef _PATH_KMEM
70050Sstevel@tonic-gate #define _PATH_KMEM "/dev/kmem"
70060Sstevel@tonic-gate #endif /* _PATH_KMEM */
70070Sstevel@tonic-gate #endif /* SPT_SCO */
70080Sstevel@tonic-gate
70090Sstevel@tonic-gate #ifndef SPT_PADCHAR
70100Sstevel@tonic-gate #define SPT_PADCHAR ' '
70110Sstevel@tonic-gate #endif
70120Sstevel@tonic-gate
70130Sstevel@tonic-gate #ifndef SPT_BUFSIZE
70140Sstevel@tonic-gate #define SPT_BUFSIZE MAXLINE
70150Sstevel@tonic-gate #endif
70160Sstevel@tonic-gate
70170Sstevel@tonic-gate #endif /* SPT_TYPE != SPT_NONE && SPT_TYPE != SPT_BUILTIN */
70180Sstevel@tonic-gate
70190Sstevel@tonic-gate #if SPT_TYPE == SPT_REUSEARGV || SPT_TYPE == SPT_CHANGEARGV
70200Sstevel@tonic-gate char **Argv = NULL; /* pointer to argument vector */
70210Sstevel@tonic-gate #endif
70220Sstevel@tonic-gate
70230Sstevel@tonic-gate #if SPT_TYPE == SPT_REUSEARGV
70240Sstevel@tonic-gate char *LastArgv = NULL; /* end of argv */
70250Sstevel@tonic-gate #endif
70260Sstevel@tonic-gate
70270Sstevel@tonic-gate /*
70280Sstevel@tonic-gate ** Pointers for setproctitle.
70290Sstevel@tonic-gate ** This allows "ps" listings to give more useful information.
70300Sstevel@tonic-gate */
70310Sstevel@tonic-gate void initsetproctitle(argc, argv, envp)
70320Sstevel@tonic-gate int argc;
70330Sstevel@tonic-gate char **argv;
70340Sstevel@tonic-gate char **envp;
70350Sstevel@tonic-gate {
70360Sstevel@tonic-gate #if SPT_TYPE == SPT_REUSEARGV
70370Sstevel@tonic-gate register int i, envpsize = 0;
70380Sstevel@tonic-gate char **newenviron;
70390Sstevel@tonic-gate extern char **environ;
70400Sstevel@tonic-gate
70410Sstevel@tonic-gate /*
70420Sstevel@tonic-gate ** Save start and extent of argv for setproctitle.
70430Sstevel@tonic-gate */
70440Sstevel@tonic-gate
70450Sstevel@tonic-gate LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
70460Sstevel@tonic-gate if (envp != NULL) {
70470Sstevel@tonic-gate /*
70480Sstevel@tonic-gate ** Move the environment so setproctitle can use the space at
70490Sstevel@tonic-gate ** the top of memory.
70500Sstevel@tonic-gate */
70510Sstevel@tonic-gate for (i = 0; envp[i] != NULL; i++)
70520Sstevel@tonic-gate envpsize += strlen(envp[i]) + 1;
70530Sstevel@tonic-gate newenviron = (char **) malloc(sizeof(char *) * (i + 1));
70540Sstevel@tonic-gate if (newenviron) {
70550Sstevel@tonic-gate int err = 0;
70560Sstevel@tonic-gate for (i = 0; envp[i] != NULL; i++) {
70570Sstevel@tonic-gate if ((newenviron[i] = strdup(envp[i])) == NULL) {
70580Sstevel@tonic-gate err = 1;
70590Sstevel@tonic-gate break;
70600Sstevel@tonic-gate }
70610Sstevel@tonic-gate }
70620Sstevel@tonic-gate if (err) {
70630Sstevel@tonic-gate for (i = 0; newenviron[i] != NULL; i++)
70640Sstevel@tonic-gate free(newenviron[i]);
70650Sstevel@tonic-gate free(newenviron);
70660Sstevel@tonic-gate i = 0;
70670Sstevel@tonic-gate }
70680Sstevel@tonic-gate else {
70690Sstevel@tonic-gate newenviron[i] = NULL;
70700Sstevel@tonic-gate environ = newenviron;
70710Sstevel@tonic-gate }
70720Sstevel@tonic-gate }
70730Sstevel@tonic-gate else {
70740Sstevel@tonic-gate i = 0;
70750Sstevel@tonic-gate }
70760Sstevel@tonic-gate
70770Sstevel@tonic-gate /*
70780Sstevel@tonic-gate ** Find the last environment variable within wu-ftpd's
70790Sstevel@tonic-gate ** process memory area.
70800Sstevel@tonic-gate */
70810Sstevel@tonic-gate while (i > 0 && (envp[i - 1] < argv[0] ||
70820Sstevel@tonic-gate envp[i - 1] > (argv[argc - 1] + strlen(argv[argc - 1]) +
70830Sstevel@tonic-gate 1 + envpsize)))
70840Sstevel@tonic-gate i--;
70850Sstevel@tonic-gate
70860Sstevel@tonic-gate if (i > 0)
70870Sstevel@tonic-gate LastArgv = envp[i - 1] + strlen(envp[i - 1]);
70880Sstevel@tonic-gate }
70890Sstevel@tonic-gate #endif /* SPT_TYPE == SPT_REUSEARGV */
70900Sstevel@tonic-gate
70910Sstevel@tonic-gate #if SPT_TYPE == SPT_REUSEARGV || SPT_TYPE == SPT_CHANGEARGV
70920Sstevel@tonic-gate Argv = argv;
70930Sstevel@tonic-gate #endif
70940Sstevel@tonic-gate }
70950Sstevel@tonic-gate
70960Sstevel@tonic-gate
70970Sstevel@tonic-gate #if SPT_TYPE != SPT_BUILTIN
70980Sstevel@tonic-gate
70990Sstevel@tonic-gate /*VARARGS1 */
71000Sstevel@tonic-gate void setproctitle(const char *fmt,...)
71010Sstevel@tonic-gate {
71020Sstevel@tonic-gate #if SPT_TYPE != SPT_NONE
71030Sstevel@tonic-gate register char *p;
71040Sstevel@tonic-gate register int i;
71050Sstevel@tonic-gate SETPROC_STATIC char buf[SPT_BUFSIZE];
71060Sstevel@tonic-gate VA_LOCAL_DECL
71070Sstevel@tonic-gate #if SPT_TYPE == SPT_PSTAT
71080Sstevel@tonic-gate union pstun pst;
71090Sstevel@tonic-gate #endif
71100Sstevel@tonic-gate #if SPT_TYPE == SPT_SCO
71110Sstevel@tonic-gate static off_t seek_off;
71120Sstevel@tonic-gate static int kmemfd = -1;
71130Sstevel@tonic-gate static int kmempid = -1;
71140Sstevel@tonic-gate #ifdef UNIXWARE
71150Sstevel@tonic-gate off_t offset;
71160Sstevel@tonic-gate void *ptr;
71170Sstevel@tonic-gate struct mioc_rksym rks;
71180Sstevel@tonic-gate #endif /* UNIXWARE */
71190Sstevel@tonic-gate #endif /* SPT_SCO */
71200Sstevel@tonic-gate
71210Sstevel@tonic-gate p = buf;
71220Sstevel@tonic-gate
71230Sstevel@tonic-gate /* print ftpd: heading for grep */
71240Sstevel@tonic-gate (void) strcpy(p, "ftpd: ");
71250Sstevel@tonic-gate p += strlen(p);
71260Sstevel@tonic-gate
71270Sstevel@tonic-gate /* print the argument string */
71280Sstevel@tonic-gate VA_START(fmt);
71290Sstevel@tonic-gate (void) vsnprintf(p, SPACELEFT(buf, p), fmt, ap);
71300Sstevel@tonic-gate VA_END;
71310Sstevel@tonic-gate
71320Sstevel@tonic-gate i = strlen(buf);
71330Sstevel@tonic-gate
71340Sstevel@tonic-gate #if SPT_TYPE == SPT_PSTAT
71350Sstevel@tonic-gate pst.pst_command = buf;
71360Sstevel@tonic-gate pstat(PSTAT_SETCMD, pst, i, 0, 0);
71370Sstevel@tonic-gate #endif
71380Sstevel@tonic-gate #if SPT_TYPE == SPT_PSSTRINGS
71390Sstevel@tonic-gate PS_STRINGS->ps_nargvstr = 1;
71400Sstevel@tonic-gate PS_STRINGS->ps_argvstr = buf;
71410Sstevel@tonic-gate #endif
71420Sstevel@tonic-gate #if SPT_TYPE == SPT_SYSMIPS
71430Sstevel@tonic-gate sysmips(SONY_SYSNEWS, NEWS_SETPSARGS, buf);
71440Sstevel@tonic-gate #endif
71450Sstevel@tonic-gate #if SPT_TYPE == SPT_SCO
71460Sstevel@tonic-gate if (kmemfd < 0 || kmempid != getpid()) {
71470Sstevel@tonic-gate if (kmemfd >= 0)
71480Sstevel@tonic-gate close(kmemfd);
71490Sstevel@tonic-gate if ((kmemfd = open(_PATH_KMEM, O_RDWR, 0)) < 0)
71500Sstevel@tonic-gate return;
71510Sstevel@tonic-gate (void) fcntl(kmemfd, F_SETFD, 1);
71520Sstevel@tonic-gate kmempid = getpid();
71530Sstevel@tonic-gate #ifdef UNIXWARE
71540Sstevel@tonic-gate seek_off = 0;
71550Sstevel@tonic-gate rks.mirk_symname = "upointer";
71560Sstevel@tonic-gate rks.mirk_buf = &ptr;
71570Sstevel@tonic-gate rks.mirk_buflen = sizeof(ptr);
71580Sstevel@tonic-gate if (ioctl(kmemfd, MIOC_READKSYM, &rks) < 0)
71590Sstevel@tonic-gate return;
71600Sstevel@tonic-gate offset = (off_t) ptr + (off_t) & ((struct user *) 0)->u_procp;
71610Sstevel@tonic-gate if (lseek(kmemfd, offset, SEEK_SET) != offset)
71620Sstevel@tonic-gate return;
71630Sstevel@tonic-gate if (read(kmemfd, &ptr, sizeof(ptr)) != sizeof(ptr))
71640Sstevel@tonic-gate return;
71650Sstevel@tonic-gate offset = (off_t) ptr + (off_t) & ((struct proc *) 0)->p_execinfo;
71660Sstevel@tonic-gate if (lseek(kmemfd, offset, SEEK_SET) != offset)
71670Sstevel@tonic-gate return;
71680Sstevel@tonic-gate if (read(kmemfd, &ptr, sizeof(ptr)) != sizeof(ptr))
71690Sstevel@tonic-gate return;
71700Sstevel@tonic-gate seek_off = (off_t) ptr + (off_t) ((struct execinfo *) 0)->ei_psargs;
71710Sstevel@tonic-gate #else /* UNIXWARE */
71720Sstevel@tonic-gate seek_off = UVUBLK + (off_t) & ((struct user *) 0)->u_psargs;
71730Sstevel@tonic-gate #endif /* UNIXWARE */
71740Sstevel@tonic-gate }
71750Sstevel@tonic-gate #ifdef UNIXWARE
71760Sstevel@tonic-gate if (seek_off == 0)
71770Sstevel@tonic-gate return;
71780Sstevel@tonic-gate #endif /* UNIXWARE */
71790Sstevel@tonic-gate buf[PSARGSZ - 1] = '\0';
71800Sstevel@tonic-gate if (lseek(kmemfd, (off_t) seek_off, SEEK_SET) == seek_off)
71810Sstevel@tonic-gate (void) write(kmemfd, buf, PSARGSZ);
71820Sstevel@tonic-gate #endif /* SPT_SCO */
71830Sstevel@tonic-gate #if SPT_TYPE == SPT_REUSEARGV
71840Sstevel@tonic-gate if (i > LastArgv - Argv[0] - 2) {
71850Sstevel@tonic-gate i = LastArgv - Argv[0] - 2;
71860Sstevel@tonic-gate buf[i] = '\0';
71870Sstevel@tonic-gate }
71880Sstevel@tonic-gate (void) strcpy(Argv[0], buf);
71890Sstevel@tonic-gate p = &Argv[0][i];
71900Sstevel@tonic-gate while (p < LastArgv)
71910Sstevel@tonic-gate *p++ = SPT_PADCHAR;
71920Sstevel@tonic-gate Argv[1] = NULL;
71930Sstevel@tonic-gate #endif
71940Sstevel@tonic-gate #if SPT_TYPE == SPT_CHANGEARGV
71950Sstevel@tonic-gate Argv[0] = buf;
71960Sstevel@tonic-gate Argv[1] = 0;
71970Sstevel@tonic-gate #endif
71980Sstevel@tonic-gate #endif /* SPT_TYPE != SPT_NONE */
71990Sstevel@tonic-gate }
72000Sstevel@tonic-gate
72010Sstevel@tonic-gate #endif /* SPT_TYPE != SPT_BUILTIN */
72020Sstevel@tonic-gate
72030Sstevel@tonic-gate #ifdef KERBEROS
72040Sstevel@tonic-gate /* thanks to gshapiro@wpi.wpi.edu for the following kerberosities */
72050Sstevel@tonic-gate
72060Sstevel@tonic-gate void init_krb()
72070Sstevel@tonic-gate {
72080Sstevel@tonic-gate char hostname[100];
72090Sstevel@tonic-gate
72100Sstevel@tonic-gate #ifdef HAVE_SYSINFO
72110Sstevel@tonic-gate if (sysinfo(SI_HOSTNAME, hostname, sizeof(hostname)) < 0) {
72120Sstevel@tonic-gate perror("sysinfo");
72130Sstevel@tonic-gate #else
72140Sstevel@tonic-gate if (gethostname(hostname, sizeof(hostname)) < 0) {
72150Sstevel@tonic-gate perror("gethostname");
72160Sstevel@tonic-gate #endif
72170Sstevel@tonic-gate exit(1);
72180Sstevel@tonic-gate }
72190Sstevel@tonic-gate if (strchr(hostname, '.'))
72200Sstevel@tonic-gate *(strchr(hostname, '.')) = 0;
72210Sstevel@tonic-gate
72220Sstevel@tonic-gate sprintf(krb_ticket_name, "/var/dss/kerberos/tkt/tkt.%d", getpid());
72230Sstevel@tonic-gate krb_set_tkt_string(krb_ticket_name);
72240Sstevel@tonic-gate
72250Sstevel@tonic-gate config_auth();
72260Sstevel@tonic-gate
72270Sstevel@tonic-gate if (krb_svc_init("hesiod", hostname, (char *) NULL, 0, (char *) NULL,
72280Sstevel@tonic-gate (char *) NULL) != KSUCCESS) {
72290Sstevel@tonic-gate fprintf(stderr, "Couldn't initialize Kerberos\n");
72300Sstevel@tonic-gate exit(1);
72310Sstevel@tonic-gate }
72320Sstevel@tonic-gate }
72330Sstevel@tonic-gate
72340Sstevel@tonic-gate void end_krb()
72350Sstevel@tonic-gate {
72360Sstevel@tonic-gate unlink(krb_ticket_name);
72370Sstevel@tonic-gate }
72380Sstevel@tonic-gate
72390Sstevel@tonic-gate #endif /* KERBEROS */
72400Sstevel@tonic-gate
72410Sstevel@tonic-gate #ifdef ULTRIX_AUTH
72420Sstevel@tonic-gate static int ultrix_check_pass(char *passwd, char *xpasswd)
72430Sstevel@tonic-gate {
72440Sstevel@tonic-gate struct svcinfo *svp;
72450Sstevel@tonic-gate int auth_status;
72460Sstevel@tonic-gate
72470Sstevel@tonic-gate if ((svp = getsvc()) == (struct svcinfo *) NULL) {
72480Sstevel@tonic-gate syslog(LOG_WARNING, "getsvc() failed in ultrix_check_pass");
72490Sstevel@tonic-gate return -1;
72500Sstevel@tonic-gate }
72510Sstevel@tonic-gate if (pw == (struct passwd *) NULL) {
72520Sstevel@tonic-gate return -1;
72530Sstevel@tonic-gate }
72540Sstevel@tonic-gate if (((svp->svcauth.seclevel == SEC_UPGRADE) &&
72550Sstevel@tonic-gate (!strcmp(pw->pw_passwd, "*")))
72560Sstevel@tonic-gate || (svp->svcauth.seclevel == SEC_ENHANCED)) {
72570Sstevel@tonic-gate if ((auth_status = authenticate_user(pw, passwd, "/dev/ttypXX")) >= 0) {
72580Sstevel@tonic-gate /* Indicate successful validation */
72590Sstevel@tonic-gate return auth_status;
72600Sstevel@tonic-gate }
72610Sstevel@tonic-gate if (auth_status < 0 && errno == EPERM) {
72620Sstevel@tonic-gate /* Log some information about the failed login attempt. */
72630Sstevel@tonic-gate switch (abs(auth_status)) {
72640Sstevel@tonic-gate case A_EBADPASS:
72650Sstevel@tonic-gate break;
72660Sstevel@tonic-gate case A_ESOFTEXP:
72670Sstevel@tonic-gate syslog(LOG_NOTICE, "password will expire soon for user %s",
72680Sstevel@tonic-gate pw->pw_name);
72690Sstevel@tonic-gate break;
72700Sstevel@tonic-gate case A_EHARDEXP:
72710Sstevel@tonic-gate syslog(LOG_NOTICE, "password has expired for user %s",
72720Sstevel@tonic-gate pw->pw_name);
72730Sstevel@tonic-gate break;
72740Sstevel@tonic-gate case A_ENOLOGIN:
72750Sstevel@tonic-gate syslog(LOG_NOTICE, "user %s attempted login to disabled acct",
72760Sstevel@tonic-gate pw->pw_name);
72770Sstevel@tonic-gate break;
72780Sstevel@tonic-gate }
72790Sstevel@tonic-gate }
72800Sstevel@tonic-gate }
72810Sstevel@tonic-gate else {
72820Sstevel@tonic-gate if ((*pw->pw_passwd != '\0') && (!strcmp(xpasswd, pw->pw_passwd))) {
72830Sstevel@tonic-gate /* passwd in /etc/passwd isn't empty && encrypted passwd matches */
72840Sstevel@tonic-gate return 0;
72850Sstevel@tonic-gate }
72860Sstevel@tonic-gate }
72870Sstevel@tonic-gate return -1;
72880Sstevel@tonic-gate }
72890Sstevel@tonic-gate #endif /* ULTRIX_AUTH */
72900Sstevel@tonic-gate
72910Sstevel@tonic-gate #ifdef USE_PAM
72920Sstevel@tonic-gate /* This is rather an abuse of PAM, but the FTP protocol doesn't allow much
72930Sstevel@tonic-gate * flexibility here. :-(
72940Sstevel@tonic-gate */
72950Sstevel@tonic-gate
72960Sstevel@tonic-gate /* Static variables used to communicate between the conversation function
72970Sstevel@tonic-gate * and the server_login function
72980Sstevel@tonic-gate */
72990Sstevel@tonic-gate static char *PAM_password;
73000Sstevel@tonic-gate
73010Sstevel@tonic-gate /* PAM conversation function
73020Sstevel@tonic-gate * Here we assume (for now, at least) that echo on means login name, and
73030Sstevel@tonic-gate * echo off means password.
73040Sstevel@tonic-gate */
73050Sstevel@tonic-gate #ifdef SOLARIS_2
73060Sstevel@tonic-gate /* Workaround bug 4430970/4413889 which causes a compiler warning, necessary
73070Sstevel@tonic-gate * as usr/src/Makefile.master now includes "-errwarn=%all".
73080Sstevel@tonic-gate */
73090Sstevel@tonic-gate static int PAM_conv(int num_msg, struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
73100Sstevel@tonic-gate #else
73110Sstevel@tonic-gate static int PAM_conv(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
73120Sstevel@tonic-gate #endif
73130Sstevel@tonic-gate {
73140Sstevel@tonic-gate int replies = 0;
73150Sstevel@tonic-gate struct pam_response *reply = NULL;
73160Sstevel@tonic-gate
73170Sstevel@tonic-gate #define COPY_STRING(s) (s) ? strdup(s) : NULL
73180Sstevel@tonic-gate
73190Sstevel@tonic-gate reply = malloc(sizeof(struct pam_response) * num_msg);
73200Sstevel@tonic-gate if (!reply)
73210Sstevel@tonic-gate return PAM_CONV_ERR;
73220Sstevel@tonic-gate
73230Sstevel@tonic-gate for (replies = 0; replies < num_msg; replies++) {
73240Sstevel@tonic-gate switch (msg[replies]->msg_style) {
73250Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON:
73260Sstevel@tonic-gate return PAM_CONV_ERR;
73270Sstevel@tonic-gate break;
73280Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF:
73290Sstevel@tonic-gate reply[replies].resp_retcode = PAM_SUCCESS;
73300Sstevel@tonic-gate reply[replies].resp = COPY_STRING(PAM_password);
73310Sstevel@tonic-gate /* PAM frees resp */
73320Sstevel@tonic-gate break;
73330Sstevel@tonic-gate case PAM_TEXT_INFO:
73340Sstevel@tonic-gate /* ignore it... */
73350Sstevel@tonic-gate reply[replies].resp_retcode = PAM_SUCCESS;
73360Sstevel@tonic-gate reply[replies].resp = NULL;
73370Sstevel@tonic-gate break;
73380Sstevel@tonic-gate case PAM_ERROR_MSG:
73390Sstevel@tonic-gate /* ignore it... */
73400Sstevel@tonic-gate reply[replies].resp_retcode = PAM_SUCCESS;
73410Sstevel@tonic-gate reply[replies].resp = NULL;
73420Sstevel@tonic-gate break;
73430Sstevel@tonic-gate default:
73440Sstevel@tonic-gate /* Must be an error of some sort... */
73450Sstevel@tonic-gate return PAM_CONV_ERR;
73460Sstevel@tonic-gate }
73470Sstevel@tonic-gate }
73480Sstevel@tonic-gate *resp = reply;
73490Sstevel@tonic-gate return PAM_SUCCESS;
73500Sstevel@tonic-gate }
73510Sstevel@tonic-gate static struct pam_conv PAM_conversation =
73520Sstevel@tonic-gate {
73530Sstevel@tonic-gate &PAM_conv,
73540Sstevel@tonic-gate NULL
73550Sstevel@tonic-gate };
73560Sstevel@tonic-gate
73570Sstevel@tonic-gate static int pam_check_pass(char *user, char *passwd)
73580Sstevel@tonic-gate {
73590Sstevel@tonic-gate char tty[20];
73600Sstevel@tonic-gate int pam_session = 0;
73610Sstevel@tonic-gate
73620Sstevel@tonic-gate /* Now use PAM to do authentication and session logging. Bail out if
73630Sstevel@tonic-gate * there are any errors. Since this is a limited protocol, and an even
73640Sstevel@tonic-gate * more limited function within a server speaking this protocol, we
73650Sstevel@tonic-gate * can't be as verbose as would otherwise make sense.
73660Sstevel@tonic-gate */
73670Sstevel@tonic-gate PAM_password = passwd;
73680Sstevel@tonic-gate pamh = (pam_handle_t *)0;
73690Sstevel@tonic-gate if (pam_start("ftp", user, &PAM_conversation, &pamh) != PAM_SUCCESS)
73700Sstevel@tonic-gate return 0;
73710Sstevel@tonic-gate
73720Sstevel@tonic-gate #if ((defined(BSD) && (BSD >= 199103)) || defined(sun))
73730Sstevel@tonic-gate (void) sprintf(tty, "/dev/ftp%ld", (long) getpid());
73740Sstevel@tonic-gate #else
73750Sstevel@tonic-gate (void) sprintf(tty, "/dev/ftpd%d", getpid());
73760Sstevel@tonic-gate #endif
73770Sstevel@tonic-gate
73780Sstevel@tonic-gate if (pam_set_item(pamh, PAM_TTY, tty) != PAM_SUCCESS)
73790Sstevel@tonic-gate goto pam_fail;
73800Sstevel@tonic-gate if (pam_set_item(pamh, PAM_RHOST, remotehost) != PAM_SUCCESS)
73810Sstevel@tonic-gate goto pam_fail;
73820Sstevel@tonic-gate if (pam_authenticate(pamh, PAM_DISALLOW_NULL_AUTHTOK) != PAM_SUCCESS) {
73830Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
73840Sstevel@tonic-gate audit_ftpd_bad_pw(user);
73850Sstevel@tonic-gate #endif
73860Sstevel@tonic-gate goto pam_fail;
73870Sstevel@tonic-gate }
73880Sstevel@tonic-gate if (pam_acct_mgmt(pamh, 0) != PAM_SUCCESS) {
73890Sstevel@tonic-gate #ifdef SOLARIS_BSM_AUDIT
73900Sstevel@tonic-gate audit_ftpd_bad_pw(user);
73910Sstevel@tonic-gate #endif
73920Sstevel@tonic-gate goto pam_fail;
73930Sstevel@tonic-gate }
73940Sstevel@tonic-gate if (pam_open_session(pamh, 0) != PAM_SUCCESS)
73950Sstevel@tonic-gate goto pam_fail;
73960Sstevel@tonic-gate pam_session = 1;
73970Sstevel@tonic-gate #ifdef PAM_ESTABLISH_CRED
73980Sstevel@tonic-gate if (pam_setcred(pamh, PAM_ESTABLISH_CRED) != PAM_SUCCESS)
73990Sstevel@tonic-gate goto pam_fail;
74000Sstevel@tonic-gate #else
74010Sstevel@tonic-gate if (pam_setcred(pamh, PAM_CRED_ESTABLISH) != PAM_SUCCESS)
74020Sstevel@tonic-gate goto pam_fail;
74030Sstevel@tonic-gate #endif
74040Sstevel@tonic-gate /* If this point is reached, the user has been authenticated. */
74050Sstevel@tonic-gate return 1;
74060Sstevel@tonic-gate
74070Sstevel@tonic-gate pam_fail:
74080Sstevel@tonic-gate if (pam_session)
74090Sstevel@tonic-gate (void) pam_close_session(pamh, 0);
74100Sstevel@tonic-gate (void) pam_end(pamh, 0);
74110Sstevel@tonic-gate pamh = (pam_handle_t *)0;
74120Sstevel@tonic-gate return 0;
74130Sstevel@tonic-gate }
74140Sstevel@tonic-gate #endif
74150Sstevel@tonic-gate
74160Sstevel@tonic-gate #ifdef DAEMON
74170Sstevel@tonic-gate
74180Sstevel@tonic-gate #ifdef INET6
74190Sstevel@tonic-gate static struct in6_addr acl_DaemonAddress6(void)
74200Sstevel@tonic-gate {
74210Sstevel@tonic-gate struct in6_addr rv = in6addr_any;
74220Sstevel@tonic-gate struct aclmember *entry = NULL;
74230Sstevel@tonic-gate
74240Sstevel@tonic-gate if (getaclentry("daemonaddress", &entry) && ARG0) {
74250Sstevel@tonic-gate if (inet_pton6(ARG0, &rv) != 1)
74260Sstevel@tonic-gate rv = in6addr_any;
74270Sstevel@tonic-gate }
74280Sstevel@tonic-gate return rv;
74290Sstevel@tonic-gate }
74300Sstevel@tonic-gate #endif /* INET6 */
74310Sstevel@tonic-gate static unsigned long int acl_DaemonAddress(void)
74320Sstevel@tonic-gate {
74330Sstevel@tonic-gate unsigned long int rv = INADDR_ANY;
74340Sstevel@tonic-gate struct aclmember *entry = NULL;
74350Sstevel@tonic-gate
74360Sstevel@tonic-gate if (getaclentry("daemonaddress", &entry) && ARG0) {
74370Sstevel@tonic-gate rv = inet_addr(ARG0);
74380Sstevel@tonic-gate if (rv == -1)
74390Sstevel@tonic-gate rv = INADDR_ANY;
74400Sstevel@tonic-gate }
74410Sstevel@tonic-gate return rv;
74420Sstevel@tonic-gate }
74430Sstevel@tonic-gate
74440Sstevel@tonic-gate /* I am running as a standalone daemon (not under inetd) */
74450Sstevel@tonic-gate static void do_daemon(void)
74460Sstevel@tonic-gate {
74470Sstevel@tonic-gate struct SOCKSTORAGE server;
74480Sstevel@tonic-gate struct servent *serv;
74490Sstevel@tonic-gate int pgrp;
74500Sstevel@tonic-gate int lsock;
74510Sstevel@tonic-gate int one = 1;
74520Sstevel@tonic-gate FILE *pidfile;
74530Sstevel@tonic-gate int i;
74540Sstevel@tonic-gate #if defined(UNIXWARE) || defined(AIX)
74550Sstevel@tonic-gate size_t addrlen;
74560Sstevel@tonic-gate #else
74570Sstevel@tonic-gate int addrlen;
74580Sstevel@tonic-gate #endif
74590Sstevel@tonic-gate
74600Sstevel@tonic-gate /* Some of this is "borrowed" from inn - lots of it isn't */
74610Sstevel@tonic-gate
74620Sstevel@tonic-gate if (be_daemon == 2) {
74630Sstevel@tonic-gate /* Fork - so I'm not the owner of the process group any more */
74640Sstevel@tonic-gate i = fork();
74650Sstevel@tonic-gate if (i < 0) {
74660Sstevel@tonic-gate syslog(LOG_ERR, "cant fork %m");
74670Sstevel@tonic-gate exit(1);
74680Sstevel@tonic-gate }
74690Sstevel@tonic-gate /* No need for the parent any more */
74700Sstevel@tonic-gate if (i > 0)
74710Sstevel@tonic-gate exit(0);
74720Sstevel@tonic-gate
74730Sstevel@tonic-gate #ifdef NO_SETSID
74740Sstevel@tonic-gate pgrp = setpgrp(0, getpid());
74750Sstevel@tonic-gate #else
74760Sstevel@tonic-gate pgrp = setsid();
74770Sstevel@tonic-gate #endif
74780Sstevel@tonic-gate if (pgrp < 0) {
74790Sstevel@tonic-gate syslog(LOG_ERR, "cannot daemonise: %m");
74800Sstevel@tonic-gate exit(1);
74810Sstevel@tonic-gate }
74820Sstevel@tonic-gate }
74830Sstevel@tonic-gate
74840Sstevel@tonic-gate if (!Bypass_PID_Files)
74850Sstevel@tonic-gate if ((pidfile = fopen(_PATH_FTPD_PID, "w"))) {
74860Sstevel@tonic-gate fprintf(pidfile, "%ld\n", (long) getpid());
74870Sstevel@tonic-gate fclose(pidfile);
74880Sstevel@tonic-gate }
74890Sstevel@tonic-gate else {
74900Sstevel@tonic-gate syslog(LOG_ERR, "Cannot write pidfile: %m");
74910Sstevel@tonic-gate }
74920Sstevel@tonic-gate
74930Sstevel@tonic-gate /* Close off all file descriptors and reopen syslog */
74940Sstevel@tonic-gate if (be_daemon == 2) {
74950Sstevel@tonic-gate closelog();
74960Sstevel@tonic-gate closefds(0);
74970Sstevel@tonic-gate (void) open(_PATH_DEVNULL, O_RDWR);
74980Sstevel@tonic-gate (void) dup2(0, 1);
74990Sstevel@tonic-gate /* junk stderr */
75000Sstevel@tonic-gate (void) freopen(_PATH_DEVNULL, "w", stderr);
75010Sstevel@tonic-gate
75020Sstevel@tonic-gate #ifdef FACILITY
75030Sstevel@tonic-gate openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
75040Sstevel@tonic-gate #else
75050Sstevel@tonic-gate openlog("ftpd", LOG_PID);
75060Sstevel@tonic-gate #endif
75070Sstevel@tonic-gate }
75080Sstevel@tonic-gate
75090Sstevel@tonic-gate if (RootDirectory != NULL) {
75100Sstevel@tonic-gate if ((chroot(RootDirectory) < 0)
75110Sstevel@tonic-gate || (chdir("/") < 0)) {
75120Sstevel@tonic-gate syslog(LOG_ERR, "Cannot chroot to initial directory, aborting.");
75130Sstevel@tonic-gate exit(1);
75140Sstevel@tonic-gate }
75150Sstevel@tonic-gate free(RootDirectory);
75160Sstevel@tonic-gate RootDirectory = NULL;
75170Sstevel@tonic-gate }
75180Sstevel@tonic-gate
75190Sstevel@tonic-gate if (!use_accessfile)
75200Sstevel@tonic-gate syslog(LOG_WARNING, "FTP server started without ftpaccess file");
75210Sstevel@tonic-gate
75220Sstevel@tonic-gate syslog(LOG_INFO, "FTP server (%s) ready.", version);
75230Sstevel@tonic-gate
75240Sstevel@tonic-gate /* Create a socket to listen on */
75250Sstevel@tonic-gate #ifdef INET6
75260Sstevel@tonic-gate if (listen_v4 == 0)
75270Sstevel@tonic-gate lsock = socket(AF_INET6, SOCK_STREAM, 0);
75280Sstevel@tonic-gate else
75290Sstevel@tonic-gate #endif
75300Sstevel@tonic-gate lsock = socket(AF_INET, SOCK_STREAM, 0);
75310Sstevel@tonic-gate if (lsock < 0) {
75320Sstevel@tonic-gate syslog(LOG_ERR, "Cannot create socket to listen on: %m");
75330Sstevel@tonic-gate exit(1);
75340Sstevel@tonic-gate }
75350Sstevel@tonic-gate if (setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one)) < 0) {
75360Sstevel@tonic-gate syslog(LOG_ERR, "Cannot set SO_REUSEADDR option: %m");
75370Sstevel@tonic-gate exit(1);
75380Sstevel@tonic-gate }
75390Sstevel@tonic-gate if (keepalive)
75400Sstevel@tonic-gate (void) setsockopt(lsock, SOL_SOCKET, SO_KEEPALIVE, (char *) &one, sizeof(one));
75410Sstevel@tonic-gate
75420Sstevel@tonic-gate #ifdef INET6
75430Sstevel@tonic-gate if (listen_v4 == 0) {
75440Sstevel@tonic-gate struct sockaddr_in6 *server_sin6 = (struct sockaddr_in6 *)&server;
75450Sstevel@tonic-gate
75460Sstevel@tonic-gate memset(&server, 0, sizeof(struct sockaddr_in6));
75470Sstevel@tonic-gate server_sin6->sin6_family = AF_INET6;
75480Sstevel@tonic-gate server_sin6->sin6_addr = acl_DaemonAddress6();
75490Sstevel@tonic-gate }
75500Sstevel@tonic-gate else {
75510Sstevel@tonic-gate struct sockaddr_in *server_sin = (struct sockaddr_in *)&server;
75520Sstevel@tonic-gate
75530Sstevel@tonic-gate server_sin->sin_family = AF_INET;
75540Sstevel@tonic-gate server_sin->sin_addr.s_addr = acl_DaemonAddress();
75550Sstevel@tonic-gate }
75560Sstevel@tonic-gate #else
75570Sstevel@tonic-gate server.sin_family = AF_INET;
75580Sstevel@tonic-gate server.sin_addr.s_addr = acl_DaemonAddress();
75590Sstevel@tonic-gate #endif
75600Sstevel@tonic-gate if (daemon_port == 0) {
75610Sstevel@tonic-gate if (!(serv = getservbyname("ftp", "tcp"))) {
75620Sstevel@tonic-gate syslog(LOG_ERR, "Cannot find service ftp: %m");
75630Sstevel@tonic-gate exit(1);
75640Sstevel@tonic-gate }
75650Sstevel@tonic-gate SET_SOCK_PORT(server, serv->s_port);
75660Sstevel@tonic-gate daemon_port = ntohs(serv->s_port);
75670Sstevel@tonic-gate }
75680Sstevel@tonic-gate else
75690Sstevel@tonic-gate SET_SOCK_PORT(server, htons(daemon_port));
75700Sstevel@tonic-gate
75710Sstevel@tonic-gate if (bind(lsock, (struct sockaddr *) &server, SOCK_LEN(server)) < 0) {
75720Sstevel@tonic-gate syslog(LOG_ERR, "Cannot bind socket: %m");
75730Sstevel@tonic-gate exit(1);
75740Sstevel@tonic-gate }
75750Sstevel@tonic-gate
75760Sstevel@tonic-gate listen(lsock, MAX_BACKLOG);
75770Sstevel@tonic-gate
75780Sstevel@tonic-gate sprintf(proctitle, "accepting connections on port %i", daemon_port);
75790Sstevel@tonic-gate setproctitle("%s", proctitle);
75800Sstevel@tonic-gate
75810Sstevel@tonic-gate while (1) {
75820Sstevel@tonic-gate int pid;
75830Sstevel@tonic-gate int msgsock;
75840Sstevel@tonic-gate
75850Sstevel@tonic-gate addrlen = sizeof(his_addr);
75860Sstevel@tonic-gate msgsock = accept(lsock, (struct sockaddr *) &his_addr, &addrlen);
75870Sstevel@tonic-gate if (msgsock < 0) {
75880Sstevel@tonic-gate int severity = LOG_ERR;
75890Sstevel@tonic-gate
75900Sstevel@tonic-gate if (errno == EINTR || errno == ECONNABORTED)
75910Sstevel@tonic-gate severity = LOG_INFO;
75920Sstevel@tonic-gate syslog(severity, "Accept failed: %m");
75930Sstevel@tonic-gate sleep(1);
75940Sstevel@tonic-gate continue;
75950Sstevel@tonic-gate }
75960Sstevel@tonic-gate
75970Sstevel@tonic-gate /* Fork off a handler */
75980Sstevel@tonic-gate pid = fork();
75990Sstevel@tonic-gate if (pid < 0) {
76000Sstevel@tonic-gate syslog(LOG_ERR, "failed to fork: %m");
76010Sstevel@tonic-gate close(msgsock);
76020Sstevel@tonic-gate sleep(1);
76030Sstevel@tonic-gate continue;
76040Sstevel@tonic-gate }
76050Sstevel@tonic-gate if (pid == 0) {
76060Sstevel@tonic-gate /* I am that forked off child */
76070Sstevel@tonic-gate /* Only parent needs lsock */
76080Sstevel@tonic-gate close(lsock);
76090Sstevel@tonic-gate closelog();
76100Sstevel@tonic-gate /* Make sure that stdin/stdout are the new socket */
76110Sstevel@tonic-gate dup2(msgsock, 0);
76120Sstevel@tonic-gate dup2(msgsock, 1);
76130Sstevel@tonic-gate if (msgsock != 0 && msgsock != 1)
76140Sstevel@tonic-gate close(msgsock);
76150Sstevel@tonic-gate #ifdef FACILITY
76160Sstevel@tonic-gate openlog("ftpd", LOG_PID | LOG_NDELAY, FACILITY);
76170Sstevel@tonic-gate #else
76180Sstevel@tonic-gate openlog("ftpd", LOG_PID);
76190Sstevel@tonic-gate #endif
76200Sstevel@tonic-gate setup_paths();
76210Sstevel@tonic-gate access_init();
76220Sstevel@tonic-gate return;
76230Sstevel@tonic-gate }
76240Sstevel@tonic-gate
76250Sstevel@tonic-gate /* I am the parent */
76260Sstevel@tonic-gate close(msgsock);
76270Sstevel@tonic-gate
76280Sstevel@tonic-gate /* Quick check to see if any of the forked off children have
76290Sstevel@tonic-gate * terminated. */
76300Sstevel@tonic-gate while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0) {
76310Sstevel@tonic-gate /* A child has finished */
76320Sstevel@tonic-gate }
76330Sstevel@tonic-gate
76340Sstevel@tonic-gate access_init();
76350Sstevel@tonic-gate }
76360Sstevel@tonic-gate }
76370Sstevel@tonic-gate
76380Sstevel@tonic-gate #endif /* DAEMON */
76390Sstevel@tonic-gate
76400Sstevel@tonic-gate #ifdef RATIO
76410Sstevel@tonic-gate int is_downloadfree(char *fname)
76420Sstevel@tonic-gate {
76430Sstevel@tonic-gate char rpath[MAXPATHLEN];
76440Sstevel@tonic-gate char class[BUFSIZ];
76450Sstevel@tonic-gate char *cp;
76460Sstevel@tonic-gate int which;
76470Sstevel@tonic-gate struct aclmember *entry = NULL;
76480Sstevel@tonic-gate
76490Sstevel@tonic-gate if( wu_realpath(fname,rpath,chroot_path) == NULL )
76500Sstevel@tonic-gate return 0;
76510Sstevel@tonic-gate
76520Sstevel@tonic-gate (void) acl_getclass(class);
76530Sstevel@tonic-gate
76540Sstevel@tonic-gate if (debug)
76550Sstevel@tonic-gate syslog(LOG_DEBUG, "class: %s, fname: %s, rpath: %s", class, fname, rpath);
76560Sstevel@tonic-gate
76570Sstevel@tonic-gate while( getaclentry("dl-free-dir",&entry) ) {
76580Sstevel@tonic-gate if( ARG0 == NULL )
76590Sstevel@tonic-gate continue;
76600Sstevel@tonic-gate if( strncmp(rpath,ARG0,strlen(ARG0)) == 0 ) {
76610Sstevel@tonic-gate if( ARG1 == NULL )
76620Sstevel@tonic-gate return 1;
76630Sstevel@tonic-gate else for(which = 1; (which < MAXARGS) && ARG[which]; which++) {
76640Sstevel@tonic-gate if( strcmp(class,ARG[which]) == 0 )
76650Sstevel@tonic-gate return 1;
76660Sstevel@tonic-gate }
76670Sstevel@tonic-gate }
76680Sstevel@tonic-gate }
76690Sstevel@tonic-gate while( getaclentry("dl-free",&entry) ) {
76700Sstevel@tonic-gate if( ARG0 == NULL )
76710Sstevel@tonic-gate continue;
76720Sstevel@tonic-gate if( *(ARG0) != '/' ) { /* compare basename */
76730Sstevel@tonic-gate if( (cp = strrchr(rpath,'/')) == NULL ) {
76740Sstevel@tonic-gate cp = rpath;
76750Sstevel@tonic-gate }
76760Sstevel@tonic-gate else {
76770Sstevel@tonic-gate ++cp;
76780Sstevel@tonic-gate }
76790Sstevel@tonic-gate if( strcmp(cp,ARG0) == 0 ) {
76800Sstevel@tonic-gate if( ARG1 == NULL )
76810Sstevel@tonic-gate return 1;
76820Sstevel@tonic-gate else for(which = 1; (which < MAXARGS) && ARG[which]; which++) {
76830Sstevel@tonic-gate if( strcmp(class,ARG[which]) == 0 )
76840Sstevel@tonic-gate return 1;
76850Sstevel@tonic-gate }
76860Sstevel@tonic-gate }
76870Sstevel@tonic-gate }
76880Sstevel@tonic-gate else { /* compare real path */
76890Sstevel@tonic-gate if( strcmp(rpath,ARG0) == 0 ) {
76900Sstevel@tonic-gate if( ARG1 == NULL )
76910Sstevel@tonic-gate return 1;
76920Sstevel@tonic-gate else for(which = 1; (which < MAXARGS) && ARG[which] ; which++) {
76930Sstevel@tonic-gate if( strcmp(class,ARG[which]) == 0 )
76940Sstevel@tonic-gate return 1;
76950Sstevel@tonic-gate }
76960Sstevel@tonic-gate }
76970Sstevel@tonic-gate }
76980Sstevel@tonic-gate }
76990Sstevel@tonic-gate return 0;
77000Sstevel@tonic-gate }
77010Sstevel@tonic-gate #endif /* RATIO */
77020Sstevel@tonic-gate
77030Sstevel@tonic-gate int pasv_allowed(char *remoteaddr)
77040Sstevel@tonic-gate {
77050Sstevel@tonic-gate char class[MAXPATHLEN];
77060Sstevel@tonic-gate int which;
77070Sstevel@tonic-gate struct aclmember *entry = NULL;
77080Sstevel@tonic-gate (void) acl_getclass(class);
77090Sstevel@tonic-gate while (getaclentry("pasv-allow", &entry)) {
77100Sstevel@tonic-gate if ((ARG0 != NULL) && (strcasecmp(class, ARG0) == 0))
77110Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && (ARG[which] != NULL); which++) {
77120Sstevel@tonic-gate if (hostmatch(ARG[which], remoteaddr, NULL))
77130Sstevel@tonic-gate return 1;
77140Sstevel@tonic-gate }
77150Sstevel@tonic-gate }
77160Sstevel@tonic-gate return 0;
77170Sstevel@tonic-gate }
77180Sstevel@tonic-gate
77190Sstevel@tonic-gate int port_allowed(char *remoteaddr)
77200Sstevel@tonic-gate {
77210Sstevel@tonic-gate char class[MAXPATHLEN];
77220Sstevel@tonic-gate int which;
77230Sstevel@tonic-gate struct aclmember *entry = NULL;
77240Sstevel@tonic-gate (void) acl_getclass(class);
77250Sstevel@tonic-gate while (getaclentry("port-allow", &entry)) {
77260Sstevel@tonic-gate if ((ARG0 != NULL) && (strcasecmp(class, ARG0) == 0))
77270Sstevel@tonic-gate for (which = 1; (which < MAXARGS) && (ARG[which] != NULL); which++) {
77280Sstevel@tonic-gate if (hostmatch(ARG[which], remoteaddr, NULL))
77290Sstevel@tonic-gate return 1;
77300Sstevel@tonic-gate }
77310Sstevel@tonic-gate }
77320Sstevel@tonic-gate return 0;
77330Sstevel@tonic-gate }
77340Sstevel@tonic-gate
77350Sstevel@tonic-gate #ifdef MAIL_ADMIN
77360Sstevel@tonic-gate char *email(char *full_address)
77370Sstevel@tonic-gate {
77380Sstevel@tonic-gate /* Get the plain address part from an e-mail address
77390Sstevel@tonic-gate (i.e. remove realname) */
77400Sstevel@tonic-gate
77410Sstevel@tonic-gate static char *email_buf = NULL;
77420Sstevel@tonic-gate char *addr, *ptr;
77430Sstevel@tonic-gate
77440Sstevel@tonic-gate if (email_buf != NULL)
77450Sstevel@tonic-gate free(email_buf);
77460Sstevel@tonic-gate
77470Sstevel@tonic-gate email_buf = (char *) malloc(strlen(full_address) + 1);
77480Sstevel@tonic-gate addr = email_buf;
77490Sstevel@tonic-gate memset(addr, 0, strlen(full_address) + 1);
77500Sstevel@tonic-gate strcpy(addr, full_address);
77510Sstevel@tonic-gate
77520Sstevel@tonic-gate /* Realname <user@host> type address */
77530Sstevel@tonic-gate if ((ptr = (char *) strchr(addr, '<')) != NULL) {
77540Sstevel@tonic-gate addr = ++ptr;
77550Sstevel@tonic-gate if ((ptr = (char *) strchr(addr, '>')) != NULL)
77560Sstevel@tonic-gate *ptr = '\0';
77570Sstevel@tonic-gate }
77580Sstevel@tonic-gate
77590Sstevel@tonic-gate /* user@host (Realname) type address */
77600Sstevel@tonic-gate if (((char *) strchr(addr, ' ')) != NULL)
77610Sstevel@tonic-gate addr[strchr(addr, ' ') - addr] = '\0';
77620Sstevel@tonic-gate
77630Sstevel@tonic-gate return addr;
77640Sstevel@tonic-gate }
77650Sstevel@tonic-gate
77660Sstevel@tonic-gate FILE *SockOpen(char *host, int clientPort)
77670Sstevel@tonic-gate {
77680Sstevel@tonic-gate int sock;
77690Sstevel@tonic-gate unsigned long inaddr;
77700Sstevel@tonic-gate struct sockaddr_in ad;
77710Sstevel@tonic-gate FILE *fp;
77720Sstevel@tonic-gate #ifdef INET6
77730Sstevel@tonic-gate struct sockaddr_in6 ad6;
77740Sstevel@tonic-gate struct addrinfo hints, *result, *res;
77750Sstevel@tonic-gate int af = AF_INET;
77760Sstevel@tonic-gate #else
77770Sstevel@tonic-gate struct hostent *hp;
77780Sstevel@tonic-gate #endif
77790Sstevel@tonic-gate
77800Sstevel@tonic-gate memset(&ad, 0, sizeof(ad));
77810Sstevel@tonic-gate ad.sin_family = AF_INET;
77820Sstevel@tonic-gate
77830Sstevel@tonic-gate #ifdef INET6
77840Sstevel@tonic-gate memset(&ad6, 0, sizeof(ad6));
77850Sstevel@tonic-gate ad6.sin6_family = AF_INET6;
77860Sstevel@tonic-gate
77870Sstevel@tonic-gate memset(&hints, 0, sizeof(hints));
77880Sstevel@tonic-gate hints.ai_flags = AI_CANONNAME;
77890Sstevel@tonic-gate hints.ai_family = PF_UNSPEC;
77900Sstevel@tonic-gate
77910Sstevel@tonic-gate if (getaddrinfo(host, NULL, &hints, &result) != 0)
77920Sstevel@tonic-gate return (FILE *) NULL;
77930Sstevel@tonic-gate
77940Sstevel@tonic-gate for (res = result; res; res = res->ai_next) {
77950Sstevel@tonic-gate af = res->ai_family;
77960Sstevel@tonic-gate if (af == AF_INET)
77970Sstevel@tonic-gate memcpy(&ad.sin_addr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, sizeof(struct in_addr));
77980Sstevel@tonic-gate else if (af == AF_INET6)
77990Sstevel@tonic-gate memcpy(&ad6.sin6_addr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
78000Sstevel@tonic-gate else
78010Sstevel@tonic-gate continue;
78020Sstevel@tonic-gate
78030Sstevel@tonic-gate if (af == AF_INET6) {
78040Sstevel@tonic-gate ad6.sin6_port = htons(clientPort);
78050Sstevel@tonic-gate sock = socket(AF_INET6, SOCK_STREAM, 0);
78060Sstevel@tonic-gate if (sock < 0)
78070Sstevel@tonic-gate continue;
78080Sstevel@tonic-gate if (connect(sock, (struct sockaddr *) &ad6, sizeof(ad6)) != -1)
78090Sstevel@tonic-gate break;
78100Sstevel@tonic-gate close(sock);
78110Sstevel@tonic-gate }
78120Sstevel@tonic-gate else {
78130Sstevel@tonic-gate ad.sin_port = htons(clientPort);
78140Sstevel@tonic-gate sock = socket(AF_INET, SOCK_STREAM, 0);
78150Sstevel@tonic-gate if (sock < 0)
78160Sstevel@tonic-gate continue;
78170Sstevel@tonic-gate if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) != -1)
78180Sstevel@tonic-gate break;
78190Sstevel@tonic-gate close(sock);
78200Sstevel@tonic-gate }
78210Sstevel@tonic-gate }
78220Sstevel@tonic-gate freeaddrinfo(result);
78230Sstevel@tonic-gate if (!res)
78240Sstevel@tonic-gate return (FILE *) NULL;
78250Sstevel@tonic-gate #else
78260Sstevel@tonic-gate inaddr = inet_addr(host);
78270Sstevel@tonic-gate if (inaddr != (unsigned long) -1)
78280Sstevel@tonic-gate memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));
78290Sstevel@tonic-gate else {
78300Sstevel@tonic-gate hp = gethostbyname(host);
78310Sstevel@tonic-gate if (hp == NULL)
78320Sstevel@tonic-gate return (FILE *) NULL;
78330Sstevel@tonic-gate memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);
78340Sstevel@tonic-gate }
78350Sstevel@tonic-gate ad.sin_port = htons(clientPort);
78360Sstevel@tonic-gate sock = socket(AF_INET, SOCK_STREAM, 0);
78370Sstevel@tonic-gate if (sock < 0)
78380Sstevel@tonic-gate return (FILE *) NULL;
78390Sstevel@tonic-gate if (connect(sock, (struct sockaddr *) &ad, sizeof(ad)) < 0) {
78400Sstevel@tonic-gate close(sock);
78410Sstevel@tonic-gate return (FILE *) NULL;
78420Sstevel@tonic-gate }
78430Sstevel@tonic-gate #endif /* INET6 */
78440Sstevel@tonic-gate
78450Sstevel@tonic-gate fp = fdopen(sock, "r+");
78460Sstevel@tonic-gate setvbuf(fp, NULL, _IOLBF, 2048);
78470Sstevel@tonic-gate return (fp);
78480Sstevel@tonic-gate }
78490Sstevel@tonic-gate
78500Sstevel@tonic-gate int SockPrintf(FILE *sockfp, char *format,...)
78510Sstevel@tonic-gate {
78520Sstevel@tonic-gate va_list ap;
78530Sstevel@tonic-gate char buf[16384];
78540Sstevel@tonic-gate
78550Sstevel@tonic-gate va_start(ap, format);
78560Sstevel@tonic-gate vsnprintf(buf, sizeof(buf), format, ap);
78570Sstevel@tonic-gate buf[sizeof(buf) - 1] = '\0';
78580Sstevel@tonic-gate va_end(ap);
78590Sstevel@tonic-gate return SockWrite(buf, 1, strlen(buf), sockfp);
78600Sstevel@tonic-gate }
78610Sstevel@tonic-gate
78620Sstevel@tonic-gate int SockWrite(char *buf, int size, int len, FILE *sockfp)
78630Sstevel@tonic-gate {
78640Sstevel@tonic-gate return (fwrite(buf, size, len, sockfp));
78650Sstevel@tonic-gate }
78660Sstevel@tonic-gate
78670Sstevel@tonic-gate char *SockGets(FILE *sockfp, char *buf, int len)
78680Sstevel@tonic-gate {
78690Sstevel@tonic-gate return (fgets(buf, len, sockfp));
78700Sstevel@tonic-gate }
78710Sstevel@tonic-gate
78720Sstevel@tonic-gate int SockPuts(FILE *sockfp, char *buf)
78730Sstevel@tonic-gate {
78740Sstevel@tonic-gate int rc;
78750Sstevel@tonic-gate
78760Sstevel@tonic-gate if ((rc = SockWrite(buf, 1, strlen(buf), sockfp)))
78770Sstevel@tonic-gate return rc;
78780Sstevel@tonic-gate return SockWrite("\r\n", 1, 2, sockfp);
78790Sstevel@tonic-gate }
78800Sstevel@tonic-gate
78810Sstevel@tonic-gate int Reply(FILE *sockfp)
78820Sstevel@tonic-gate {
78830Sstevel@tonic-gate char *reply, *rec, *separator;
78840Sstevel@tonic-gate int ret = 0;
78850Sstevel@tonic-gate
78860Sstevel@tonic-gate if ((reply = (char *) malloc(BUFSIZ)) == NULL)
78870Sstevel@tonic-gate return ret;
78880Sstevel@tonic-gate memset(reply, 0, 1024);
78890Sstevel@tonic-gate do {
78900Sstevel@tonic-gate rec = SockGets(sockfp, reply, BUFSIZ);
78910Sstevel@tonic-gate if (rec != NULL) {
78920Sstevel@tonic-gate ret = strtol(reply, &separator, 10);
78930Sstevel@tonic-gate }
78940Sstevel@tonic-gate else
78950Sstevel@tonic-gate ret = 250;
78960Sstevel@tonic-gate } while ((rec != NULL) && (separator[0] != ' '));
78970Sstevel@tonic-gate free(reply);
78980Sstevel@tonic-gate fflush(sockfp); /* Solaris bug: need to clear buf before fwrite() */
78990Sstevel@tonic-gate return ret;
79000Sstevel@tonic-gate }
79010Sstevel@tonic-gate
79020Sstevel@tonic-gate int Send(FILE *sockfp, char *format,...)
79030Sstevel@tonic-gate {
79040Sstevel@tonic-gate va_list ap;
79050Sstevel@tonic-gate char buf[16384];
79060Sstevel@tonic-gate
79070Sstevel@tonic-gate va_start(ap, format);
79080Sstevel@tonic-gate vsnprintf(buf, sizeof(buf), format, ap);
79090Sstevel@tonic-gate buf[sizeof(buf) - 1] = '\0';
79100Sstevel@tonic-gate va_end(ap);
79110Sstevel@tonic-gate SockWrite(buf, 1, strlen(buf), sockfp);
79120Sstevel@tonic-gate return Reply(sockfp);
79130Sstevel@tonic-gate }
79140Sstevel@tonic-gate #endif /* MAIL_ADMIN */
79150Sstevel@tonic-gate
79160Sstevel@tonic-gate
79170Sstevel@tonic-gate /*
79180Sstevel@tonic-gate * fixpath
79190Sstevel@tonic-gate *
79200Sstevel@tonic-gate * In principal, this is similar to realpath() or the mapping chdir function.
79210Sstevel@tonic-gate * It removes unnecessary path components. We do this to put a stop to
79220Sstevel@tonic-gate * attempts to cause a memory starvation DoS.
79230Sstevel@tonic-gate *
79240Sstevel@tonic-gate */
79250Sstevel@tonic-gate
79260Sstevel@tonic-gate void fixpath(char *path)
79270Sstevel@tonic-gate {
79280Sstevel@tonic-gate int abs = 0;
79290Sstevel@tonic-gate char *in;
79300Sstevel@tonic-gate char *out;
79310Sstevel@tonic-gate
79320Sstevel@tonic-gate if (*path == '/') {
79330Sstevel@tonic-gate abs = 1;
79340Sstevel@tonic-gate path++;
79350Sstevel@tonic-gate }
79360Sstevel@tonic-gate else if (*path == '~') {
79370Sstevel@tonic-gate do
79380Sstevel@tonic-gate path++;
79390Sstevel@tonic-gate while ((*path != '\0') && (*path != '/'));
79400Sstevel@tonic-gate if (*path == '/')
79410Sstevel@tonic-gate path++;
79420Sstevel@tonic-gate }
79430Sstevel@tonic-gate in = path;
79440Sstevel@tonic-gate out = path;
79450Sstevel@tonic-gate while (*in != '\0') {
79460Sstevel@tonic-gate if (*in == '/')
79470Sstevel@tonic-gate in++;
79480Sstevel@tonic-gate else if ((in[0] == '.') && ((in[1] == '/') || (in[1] == '\0'))) {
79490Sstevel@tonic-gate in++;
79500Sstevel@tonic-gate if (*in == '/')
79510Sstevel@tonic-gate in++;
79520Sstevel@tonic-gate }
79530Sstevel@tonic-gate else if ((in[0] == '.') && (in[1] == '.') && ((in[2] == '/') || (in[2] == '\0'))) {
79540Sstevel@tonic-gate if (out == path) {
79550Sstevel@tonic-gate if (abs) {
79560Sstevel@tonic-gate in++;
79570Sstevel@tonic-gate in++;
79580Sstevel@tonic-gate if (*in == '/')
79590Sstevel@tonic-gate in++;
79600Sstevel@tonic-gate }
79610Sstevel@tonic-gate else {
79620Sstevel@tonic-gate *out++ = *in++;
79630Sstevel@tonic-gate *out++ = *in++;
79640Sstevel@tonic-gate if (*in == '/')
79650Sstevel@tonic-gate *out++ = *in++;
79660Sstevel@tonic-gate path = out;
79670Sstevel@tonic-gate }
79680Sstevel@tonic-gate }
79690Sstevel@tonic-gate else {
79700Sstevel@tonic-gate out--;
79710Sstevel@tonic-gate while ((out != path) && (*--out != '/'));
79720Sstevel@tonic-gate in++;
79730Sstevel@tonic-gate in++;
79740Sstevel@tonic-gate if (*in == '/')
79750Sstevel@tonic-gate in++;
79760Sstevel@tonic-gate }
79770Sstevel@tonic-gate }
79780Sstevel@tonic-gate else {
79790Sstevel@tonic-gate do
79800Sstevel@tonic-gate *out++ = *in++;
79810Sstevel@tonic-gate while ((*in != '\0') && (*in != '/'));
79820Sstevel@tonic-gate if (*in == '/')
79830Sstevel@tonic-gate *out++ = *in++;
79840Sstevel@tonic-gate }
79850Sstevel@tonic-gate }
79860Sstevel@tonic-gate *out = '\0';
79870Sstevel@tonic-gate }
79883232Sas198278
79893232Sas198278 #if defined(SOLARIS_2)
79903232Sas198278
79913232Sas198278 /* Callback function to cleanup_nscd()'s fdwalk().
79923232Sas198278 * If "fd" has the same inode, device as nscd door
79933232Sas198278 * it returns 1 otherwise it returns 0.
79943232Sas198278 */
79953232Sas198278 int close_nsdoor(void *cb_data, int fd)
79963232Sas198278 {
79973232Sas198278 struct stat fd_buf;
79983232Sas198278 struct stat *nsdoor_buf = (struct stat *) cb_data;
79993232Sas198278
80003232Sas198278 if (fstat(fd, &fd_buf) != 0) {
80013232Sas198278 return (0);
80023232Sas198278 }
80033232Sas198278
80043232Sas198278 if ((nsdoor_buf->st_dev == fd_buf.st_dev) &&
80053232Sas198278 (nsdoor_buf->st_ino == fd_buf.st_ino)) {
80063232Sas198278 close(fd);
80073232Sas198278 return (1);
80083232Sas198278 }
80093232Sas198278
80103232Sas198278 return (0);
80113232Sas198278 }
80123232Sas198278
80133232Sas198278 /* Walk through the list of open file descriptors
80143232Sas198278 * of the ftp dameon and find the nscd door fd and
80153232Sas198278 * close it.
80163232Sas198278 */
80173232Sas198278 void cleanup_nscd()
80183232Sas198278 {
80193232Sas198278 struct stat nsdoor_buf;
80203232Sas198278
80213232Sas198278 if (stat(NAME_SERVICE_DOOR, &nsdoor_buf) == 0) {
80223232Sas198278 fdwalk(close_nsdoor, &nsdoor_buf);
80233232Sas198278 }
80243232Sas198278 }
80253232Sas198278
80263232Sas198278 #endif
8027