access.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/****************************************************************************
Copyright (c) 1999,2000 WU-FTPD Development Group.
All rights reserved.
Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994
The Regents of the University of California.
Portions Copyright (c) 1993, 1994 Washington University in Saint Louis.
Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc.
Portions Copyright (c) 1989 Massachusetts Institute of Technology.
Portions Copyright (c) 1998 Sendmail, Inc.
Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman.
Portions Copyright (c) 1997 by Stan Barber.
Portions Copyright (c) 1997 by Kent Landfield.
Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
Free Software Foundation, Inc.
Use and distribution of this software and its source code are governed
by the terms and conditions of the WU-FTPD Software License ("LICENSE").
If you did not receive a copy of the license, it may be obtained online
$Id: access.c,v 1.30 2000/07/01 18:17:38 wuftpd Exp $
****************************************************************************/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#ifdef HAVE_SYS_SYSLOG_H
#endif
#include <syslog.h>
#endif
#ifdef TIME_WITH_SYS_TIME
#include <time.h>
#elif defined(HAVE_SYS_TIME_H)
#else
#include <time.h>
#endif
#include <ctype.h>
#include <pwd.h>
#include <grp.h>
#include <limits.h>
#ifdef HAVE_PATHS_H
#include <paths.h>
#endif
#include "pathnames.h"
#include "extensions.h"
#include "proto.h"
#if defined(HAVE_FCNTL_H)
#include <fcntl.h>
#endif
#ifdef OTHER_PASSWD
#include "getpwnam.h"
extern char _path_passwd[];
#ifdef SHADOW_PASSWORD
extern char _path_shadow[];
#endif
#endif
#if defined(USE_PAM) && defined(OTHER_PASSWD)
extern int use_pam;
#endif
char Shutdown[MAXPATHLEN];
int keepalive = 0;
#define MAXLINE 80
static int pidfd = -1;
extern int Bypass_PID_Files;
#ifndef HELP_CRACKERS
extern char DelayedMessageFile[];
#endif
#include "wu_fnmatch.h"
#define ACL_COUNT 0
#define ACL_JOIN 1
#define ACL_REMOVE 2
/*************************************************************************/
/* FUNCTION : parse_time */
/* PURPOSE : Check a single valid-time-string against the current time */
/* and return whether or not a match occurs. */
/* ARGUMENTS : a pointer to the time-string */
/*************************************************************************/
{
static char *days[] =
{"Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Wk"};
validday = 0;
match = 1;
match = 0;
whattime += 2;
match = 1;
validday = 1;
}
}
}
}
if (!validday) {
validday = 1;
whattime += 3;
}
else
return (0);
}
return (1);
return (1);
}
else
return (1);
return (0);
}
/*************************************************************************/
/* FUNCTION : validtime */
/* PURPOSE : Break apart a set of valid time-strings and pass them to */
/* parse_time, returning whether or not ANY matches occurred */
/* ARGUMENTS : a pointer to the time-string */
/*************************************************************************/
{
char *nextptr;
int good;
while (1) {
*nextptr = '\0';
/* gotta restore the | or things get skipped! */
*nextptr++ = '|';
if (good)
return (1);
}
}
#ifdef INET6
/*************************************************************************/
/* FUNCTION : ipv6str */
/* PURPOSE : Convert an IPv6 address string with optional /CIDR suffix */
/* into an IPv6 address and a CIDR, which are returned in */
/* the arguments pointed to by in6p and cidrp. */
/* ARGUMENTS : The IPv6 address string and pointers to in6_addr and CIDR */
/* RETURNS : 1 if addr is an IPv6 address string, 0 if not */
/*************************************************************************/
{
char *ptr;
*ptr = '\0';
if (ptr)
*ptr = '/';
return 0;
}
if (ptr) {
*ptr++ = '/';
if (cidr < 0)
cidr = 0;
else if (cidr > 128)
cidr = 128;
}
return 1;
}
#endif
/*************************************************************************/
/* FUNCTION : hostmatch */
/* PURPOSE : Match remote hostname or address against a glob string */
/* ARGUMENTS : The string to match, remote address, remote hostname */
/* RETURNS : 0 if no match, 1 if a match occurs */
/*************************************************************************/
{
int found = 1;
int not_found = 0;
int match = 0;
#ifdef INET6
#endif
return (0);
if (*addr == '!') {
found = 0;
not_found = 1;
addr++;
}
m[0] = 0;
m[1] = 0;
m[2] = 0;
m[3] = 0;
if (cidr < 0)
cidr = 0;
else if (cidr > 32)
cidr = 32;
for (i = 0; cidr > 8; i++) {
m[i] = 255;
cidr -= 8;
}
switch (cidr) {
case 8:
m[i] += 1;
case 7:
m[i] += 2;
case 6:
m[i] += 4;
case 5:
m[i] += 8;
case 4:
m[i] += 16;
case 3:
m[i] += 32;
case 2:
m[i] += 64;
case 1:
m[i] += 128;
}
/* make sure remoteaddr is an IPv4 address */
return not_found;
for (i = 0; i < 4; i++)
if ((a[i] & m[i]) != (r[i] & m[i]))
return not_found;
return found;
}
else if (sscanf(addr, "%d.%d.%d.%d:%d.%d.%d.%d", a, a + 1, a + 2, a + 3, m, m + 1, m + 2, m + 3) == 8) {
/* make sure remoteaddr is an IPv4 address */
return not_found;
for (i = 0; i < 4; i++)
if ((a[i] & m[i]) != (r[i] & m[i]))
return not_found;
return found;
}
/* make sure remoteaddr is an IPv4 address */
return not_found;
for (i = 0; i < 4; i++)
return not_found;
return found;
}
#ifdef INET6
int bitstozero;
return not_found;
/* IPv6 addresses are 128-bits long */
/* zero bits starting with the least significant */
if (bitstozero >= 32)
else {
}
}
return not_found;
return found;
}
#endif
else if (*addr == '/') {
/*
* read addrglobs from named path using similar format as addrglobs
* in access file
*/
"cannot open addrglob file %s: %m", addr);
return (0);
}
match = 1;
match = 1;
}
}
}
else { /* match a hostname or hostname glob */
}
}
/*************************************************************************/
/* FUNCTION : acl_guestgroup */
/* PURPOSE : If the real user is a member of any of the listed groups, */
/* return 1. Otherwise return 0. */
/* ARGUMENTS : pw, a pointer to the passwd struct for the user */
/*************************************************************************/
{
/*
* guestuser <name> [<name> ...]
*
* If name begins with '%' treat as numeric.
* Numeric names may be ranges.
* %<uid> A single numeric UID
* %<uid>+ All UIDs greater or equal to UID
* %<uid>- All UIDs greater or equal to UID
* %-<uid> All UIDs less or equal to UID
* %<uid>-<uid> All UIDs between the two (inclusive)
* * All UIDs
*/
return (1);
/*
* guestgroup <group> [<group> ...]
*
* If group begins with '%' treat as numeric.
* Numeric groups may be ranges.
* %<gid> A single GID
* %<gid>+ All GIDs greater or equal to GID
* %<gid>- All GIDs greater or equal to GID
* %-<gid> All GIDs less or equal to GID
* %<gid>-<gid> All GIDs between the two (inclusive)
* * All GIDs
*/
return (1);
return (0);
}
{
/*
* realuser <name> [<name> ...]
*
* If name begins with '%' treat as numeric.
* Numeric names may be ranges.
* %<uid> A single numeric UID
* %<uid>+ All UIDs greater or equal to UID
* %<uid>- All UIDs greater or equal to UID
* %-<uid> All UIDs less or equal to UID
* %<uid>-<uid> All UIDs between the two (inclusive)
* * All UIDs
*/
return (1);
/*
* realgroup <group> [<group> ...]
*
* If group begins with '%' treat as numeric.
* Numeric groups may be ranges.
* %<gid> A single GID
* %<gid>+ All GIDs greater or equal to GID
* %<gid>- All GIDs greater or equal to GID
* %-<gid> All GIDs less or equal to GID
* %<gid>-<gid> All GIDs between the two (inclusive)
* * All GIDs
*/
return (1);
return (0);
}
/*************************************************************************/
/* FUNCTION : acl_autogroup */
/* PURPOSE : If the guest user is a member of any of the classes in */
/* the autogroup comment, cause a setegid() to the specified */
/* group. */
/* ARGUMENTS : pw, a pointer to the passwd struct for the user */
/*************************************************************************/
{
int which;
(void) acl_getclass(class);
/* autogroup <group> <class> [<class> ...] */
continue;
if (ARG0[0] == '%')
else {
else
endgrent();
}
return;
}
}
}
}
/*************************************************************************/
/* FUNCTION : acl_setfunctions */
/* PURPOSE : Scan the ACL buffer and determine what logging to perform */
/* for this user, and whether or not user is allowed to use */
/* the automatic TAR and COMPRESS functions. Also, set the */
/* current process priority of this copy of the ftpd server */
/* to a `nice' value value if this user is a member of a */
/* group which the ftpaccess file says should be nice'd. */
/* ARGUMENTS : none */
/*************************************************************************/
void acl_setfunctions(void)
{
log_security = 0;
/* Initialize to the logging value specified on the command line, can't
just use the current value as it may have been set by a previous call. */
(void) acl_getclass(class);
}
#ifndef NO_PRIVATE
#endif /* !NO_PRIVATE */
set = 0;
if (!ARG0)
continue;
l_compress = 0;
l_compress = 1;
set = 1;
}
}
}
set = 0;
if (!ARG0)
continue;
l_tar = 0;
l_tar = 1;
set = 1;
}
}
}
/* plan on expanding command syntax to include classes for each of these */
if (!ARG0)
continue;
if (!ARG1)
continue;
log_commands |= 1;
log_commands |= 1;
log_commands |= 1;
}
continue;
set = 0;
set = 1;
set = 1;
set = 1;
inbound = 1;
outbound = 1;
if (set)
if (set)
}
if (!ARG1)
continue;
log_security = 1;
log_security = 1;
log_security = 1;
}
syslogmsg = 1;
syslogmsg = 0;
syslogmsg = 2;
}
}
/*************************************************************************/
/* FUNCTION : acl_getclass */
/* PURPOSE : Scan the ACL buffer and determine what class user is in */
/* ARGUMENTS : pointer to buffer to class name, pointer to ACL buffer */
/*************************************************************************/
int acl_getclass(char *classbuf)
{
int which;
if (ARG0)
return (1);
return (1);
return (1);
}
}
return (0);
}
/*************************************************************************/
/* FUNCTION : acl_getlimit */
/* PURPOSE : Scan the ACL buffer and determine what limit applies to */
/* the user */
/* ARGUMENTS : pointer class name, pointer to ACL buffer */
/*************************************************************************/
{
int limit;
if (msgpathbuf)
*msgpathbuf = '\0';
/* limit <class> <n> <times> [<message_file>] */
continue;
if (ARG3 && msgpathbuf)
return (limit);
}
}
}
return (-1);
}
/*************************************************************************/
/* FUNCTION : acl_getnice */
/* PURPOSE : Scan the ACL buffer and determine what nice value applies */
/* to the user */
/* ARGUMENTS : pointer class name */
/*************************************************************************/
int acl_getnice(char *class)
{
int nice_delta_for_class_found = 0;
int nice_delta = 0;
int default_nice_delta = 0;
/* nice <nice_delta> [<class>] */
if (!ARG0)
continue;
if (!ARG1)
}
}
return nice_delta;
}
/*************************************************************************/
/* FUNCTION : acl_getdefumask */
/* PURPOSE : Scan the ACL buffer to determine what umask value applies */
/* to the user */
/* ARGUMENTS : pointer to class name */
/*************************************************************************/
void acl_getdefumask(char *class)
{
char *ptr;
unsigned int val;
/* defumask <umask> [<class>] */
if (!ARG0)
continue;
val = 0;
if (ARG1)
break;
}
else
}
}
}
/*************************************************************************/
/* FUNCTION : acl_tcpwindow */
/* PURPOSE : Scan the ACL buffer and determine what TCP window size to */
/* use based upon the class */
/* ARGUMENTS : pointer to class name */
/*************************************************************************/
void acl_tcpwindow(char *class)
{
/* tcpwindow <size> [<class>] */
if (!ARG0)
continue;
if (!ARG1)
break;
}
}
}
/*************************************************************************/
/* FUNCTION : acl_bufsize */
/* PURPOSE : Scan the ACL buffer and determine the send and receive */
/* buffer sizes to use */
/* ARGUMENTS : None */
/*************************************************************************/
static void acl_bufsize()
{
/* sendbuf <size> [<typelist>] */
sendbufsz = 0;
if (!ARG0)
continue;
if (!ARG1)
else if (type_match(ARG1)) {
break;
}
}
/* recvbuf <size> [<typelist>] */
recvbufsz = 0;
if (!ARG0)
continue;
if (!ARG1)
else if (type_match(ARG1)) {
break;
}
}
}
#ifdef TRANSFER_COUNT
#ifdef TRANSFER_LIMIT
/*************************************************************************/
/* FUNCTION : acl_filelimit */
/* PURPOSE : Scan the ACL buffer and determine what file limit to use */
/* based upon the class */
/* ARGUMENTS : pointer to class name */
/*************************************************************************/
void acl_filelimit(char *class)
{
int raw_in = 0;
int raw_out = 0;
int raw_total = 0;
int data_in = 0;
int data_out = 0;
int data_total = 0;
extern int file_limit_raw_in;
extern int file_limit_raw_out;
extern int file_limit_raw_total;
extern int file_limit_data_in;
extern int file_limit_data_out;
extern int file_limit_data_total;
/* file-limit [<raw>] <in|out|total> <count> [<class>] */
continue;
if (!ARG2)
continue;
if (!ARG3) {
if (!raw_in)
}
raw_in = 1;
}
}
if (!ARG3) {
if (!raw_out)
}
raw_out = 1;
}
}
if (!ARG3) {
if (!raw_total)
}
raw_total = 1;
}
}
}
if (!ARG2) {
if (!data_in)
}
data_in = 1;
}
}
if (!ARG2) {
if (!data_out)
}
data_out = 1;
}
}
if (!ARG2) {
if (!data_total)
}
data_total = 1;
}
}
}
}
/*************************************************************************/
/* FUNCTION : acl_datalimit */
/* PURPOSE : Scan the ACL buffer and determine what data limit to use */
/* based upon the class */
/* ARGUMENTS : pointer to class name */
/*************************************************************************/
void acl_datalimit(char *class)
{
int raw_in = 0;
int raw_out = 0;
int raw_total = 0;
int data_in = 0;
int data_out = 0;
int data_total = 0;
extern off_t data_limit_raw_in;
extern off_t data_limit_raw_out;
extern off_t data_limit_raw_total;
extern off_t data_limit_data_in;
extern off_t data_limit_data_out;
extern off_t data_limit_data_total;
/* data-limit [<raw>] <in|out|total> <count> [<class>] */
continue;
if (!ARG2)
continue;
if (!ARG3) {
if (!raw_in)
}
raw_in = 1;
}
}
if (!ARG3) {
if (!raw_out)
}
raw_out = 1;
}
}
if (!ARG3) {
if (!raw_total)
}
raw_total = 1;
}
}
}
if (!ARG2) {
if (!data_in)
}
data_in = 1;
}
}
if (!ARG2) {
if (!data_out)
}
data_out = 1;
}
}
if (!ARG2) {
if (!data_total)
}
data_total = 1;
}
}
}
}
#ifdef RATIO
/*************************************************************************/
/* FUNCTION : acl_downloadrate */
/* PURPOSE : Scan the ACL buffer and determine what data limit to use */
/* based upon the class */
/* ARGUMENTS : pointer to class name */
/*************************************************************************/
void acl_downloadrate(char *class)
{
extern int upload_download_rate;
int which;
/* ul-dl-rate <rate> [<class> ...] */
if (!ARG0 )
continue;
if (!ARG1) {
}
else {
}
}
}
}
#endif /* RATIO */
#endif
#endif
/*************************************************************************/
/* FUNCTION : acl_deny */
/* PURPOSE : Scan the ACL buffer and determine if access is denied. */
/* ARGUMENTS : Pointer to buffer into which the path of the message file */
/* is copied. */
/*************************************************************************/
int acl_deny(char *msgpathbuf)
{
if (msgpathbuf)
*msgpathbuf = (char) NULL;
/* deny <addrglob> [<message_file>] */
if (!ARG0)
continue;
if (!nameserved) {
if (ARG1)
return (1);
}
}
if (ARG1)
return (1);
}
}
return (0);
}
/*************************************************************************/
/* FUNCTION : lock_fd */
/* PURPOSE : Lock a file. */
/* ARGUMENTS : File descriptor of file to lock. */
/*************************************************************************/
{
#if !defined(HAVE_FLOCK)
#endif /* !defined(HAVE_FLOCK) */
#if defined(HAVE_FLOCK)
# if !defined(NO_PID_SLEEP_MSGS)
# endif /* !defined(NO_PID_SLEEP_MSGS) */
#else /* !(defined(HAVE_FLOCK)) */
# if !defined(NO_PID_SLEEP_MSGS)
# endif /* !defined(NO_PID_SLEEP_MSGS) */
#endif /* !(defined(HAVE_FLOCK)) */
sleep(1);
}
}
/*************************************************************************/
/* FUNCTION : unlock_fd */
/* PURPOSE : Unlock a file locked by lock_fd. */
/* ARGUMENTS : File descriptor of file to unlock. */
/*************************************************************************/
{
#if !defined(HAVE_FLOCK)
#endif /* !defined(HAVE_FLOCK) */
#if defined(HAVE_FLOCK)
#else /* !(defined(HAVE_FLOCK)) */
#endif /* !(defined(HAVE_FLOCK)) */
}
/*************************************************************************/
/* FUNCTION : limit_op */
/* PURPOSE : Carry out the specified limit operation, returning the */
/* number of users in the class or -1 on failure. */
/* ARGUMENTS : Operation (ACL_COUNT/ACL_JOIN/ACL_REMOVE), user limit */
/*************************************************************************/
{
int i, j, n, count;
struct pidfile_header hdr;
if (pidfd < 0)
return (-1);
return (-1);
return (-1);
}
toomany = 0;
write_all_header = 0;
hdr.last_checked = 0;
}
/* check bitmap accuracy and re-calculate the count every 15 minutes */
count = 0;
procid = 0;
bit_changed = 0;
for (i = 0; i < n; i++) {
if (buf[i] == 0) {
}
else {
bits = 1;
for (j = 0; j < CHAR_BIT; j++) {
count++;
}
else {
bit_changed = 1;
}
}
bits <<= 1;
procid++;
}
}
}
if (bit_changed) {
bit_changed = 0;
}
}
}
write_all_header = 1;
}
/* limit set to -1 when no limit defined */
/* return if no need to update the header */
if (write_all_header == 0) {
return (-1);
}
toomany = 1;
}
else {
/* update the count */
}
/* update the header */
if (write_all_header)
else
/* return if no need to update the bitmap */
if (toomany) {
return (-1);
}
/* update the bitmap entry for the process */
bits = 0;
else /* ACL_REMOVE */
}
/*************************************************************************/
/* FUNCTION : open_pidfile */
/* PURPOSE : Return a file descriptor of an opened PID file. */
/* ARGUMENTS : Users class. */
/*************************************************************************/
static int open_pidfile(char *class)
{
int fd;
char pidfile[MAXPATHLEN];
if (fd < 0)
return (fd);
}
/*************************************************************************/
/* FUNCTION : acl_countusers */
/* PURPOSE : Return the number of users in the specified class. */
/* ARGUMENTS : The name of the class to count. */
/*************************************************************************/
int acl_countusers(char *class)
{
if (Bypass_PID_Files)
return (0);
if (pidfd < 0) {
return (-1);
}
/*
* acl_countusers may be called from msg_massage before the correct class
* is known, so close the pid file if we opened it.
*/
if (opidfd < 0) {
pidfd = -1;
}
return (count);
}
/*************************************************************************/
/* FUNCTION : acl_join */
/* PURPOSE : Add the current process to the list of processes in the */
/* specified class. */
/* ARGUMENTS : The name of the class to join, user limit for the class. */
/* RETURNS : 0 on success, -1 on failure */
/*************************************************************************/
{
if (Bypass_PID_Files)
return (0);
if (pidfd < 0) {
return (-1);
}
/* no need to leave the pid file open as we were not added to it */
pidfd = -1;
return (-1);
}
/* pidfd left open so can be updated after a chroot */
return (0);
}
/*************************************************************************/
/* FUNCTION : acl_remove */
/* PURPOSE : Remove the current process from the list of processes in */
/* our class. */
/* ARGUMENTS : None. */
/*************************************************************************/
void acl_remove(void)
{
if (pidfd < 0)
return;
(void) limit_op(ACL_REMOVE, 0);
pidfd = -1;
}
/*************************************************************************/
/* FUNCTION : pr_mesg */
/* PURPOSE : Display a message to the user */
/* ARGUMENTS : message code, name of file to display */
/*************************************************************************/
{
if (infile) {
*cr = '\0';
}
}
}
}
/*************************************************************************/
/* FUNCTION : access_init */
/* PURPOSE : Read and parse the access lists to set things up */
/* ARGUMENTS : none */
/*************************************************************************/
void access_init(void)
{
if (!use_accessfile)
return;
return;
}
/* only reload the ftpaccess file if its changed */
return;
#ifdef OTHER_PASSWD
#ifdef SHADOW_PASSWORD
#endif
#endif
#if defined(USE_PAM) && defined(OTHER_PASSWD)
use_pam = 1;
#endif
Shutdown[0] = '\0';
keepalive = 0;
if (!readacl(_path_ftpaccess))
return;
(void) parseacl();
#ifdef OTHER_PASSWD
#ifdef USE_PAM
use_pam = 0;
#endif
}
#ifdef SHADOW_PASSWORD
#ifdef USE_PAM
use_pam = 0;
#endif
}
#endif
#endif
keepalive = 1;
}
/*************************************************************************/
/* FUNCTION : access_ok */
/* PURPOSE : Check the anonymous FTP access lists to see if this */
/* access is permitted. */
/* ARGUMENTS : reply code to use */
/*************************************************************************/
{
int limit;
int nice_delta;
if (!use_accessfile)
return (1);
"ACCESS DENIED (error reading access file) TO %s",
return (0);
}
#ifndef HELP_CRACKERS
#else
#endif
return (0);
}
/* if user is not in any class, deny access */
if (!acl_getclass(class)) {
return (0);
}
#ifdef LOG_TOOMANY
#endif
#ifndef HELP_CRACKERS
#else
#endif
return (-1);
}
if (nice_delta < 0)
}
#ifdef TRANSFER_COUNT
#ifdef TRANSFER_LIMIT
#ifdef RATIO
#endif
#endif
#endif
acl_bufsize();
return (1);
}