/*
* ProFTPD - FTP server daemon
*
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
*
* As a special exemption, copyright holders give permission to link
* this program with OpenSSL, and distribute the resulting executable,
* without including the source code for OpenSSL in the source distribution.
*
*/
#include "conf.h"
#include <bsm/adt_event.h>
#include <security/pam_appl.h>
#include <pwd.h>
#include <unistd.h>
#include <ucred.h>
#ifndef ADT_ftpd
#endif
#ifndef ADT_ftpd_logout
#endif
switch (*(int *)event_data) {
case PR_AUTH_NOPWD:
break;
case PR_AUTH_AGEPWD:
break;
case PR_AUTH_DISABLEDPWD:
break;
case PR_AUTH_CRED_INSUFF:
break;
case PR_AUTH_CRED_UNAVAIL:
break;
case PR_AUTH_CRED_ERR:
break;
case PR_AUTH_UNAVAIL:
break;
case PR_AUTH_MAXTRIES:
break;
case PR_AUTH_INIT_FAIL:
break;
case PR_AUTH_NEWTOK:
break;
case PR_AUTH_OPEN_ERR:
break;
case PR_AUTH_SYMBOL_ERR:
break;
case PR_AUTH_SERVICE_ERR:
break;
case PR_AUTH_SYSTEM_ERR:
break;
case PR_AUTH_BUF_ERR:
break;
case PR_AUTH_CONV_ERR:
break;
case PR_AUTH_PERM_DENIED:
break;
default: /* PR_AUTH_BADPWD */
break;
}
}
const char *how;
int saved_errno = 0;
long pwdbuf_len_max;
saved_errno = errno;
how = "couldn't determine maximum size of password buffer";
goto fail;
}
saved_errno = errno;
how = "couldn't start adt session";
goto fail;
}
}
saved_errno = errno;
how = "couldn't set adt user";
goto fail;
}
saved_errno = errno;
how = "couldn't allocate adt event";
goto fail;
}
saved_errno = errno;
how = "couldn't put adt event";
goto fail;
}
(void) adt_end_session(asession);
return;
fail:
(void) adt_end_session(asession);
}
static void audit_success(void) {
const char *how;
int saved_errno = 0;
saved_errno = errno;
how = "couldn't start adt session";
goto fail;
}
saved_errno = errno;
how = "couldn't allocate adt event";
goto fail;
}
saved_errno = errno;
how = "couldn't put adt event";
goto fail;
}
/* Don't end adt session - leave for when logging out. */
return;
fail:
/* Don't end adt session - leave for when logging out. */
}
static void audit_logout(void) {
const char *how;
int saved_errno = 0;
/* If audit session was not created during login then leave */
return;
saved_errno = errno;
how = "couldn't allocate adt event";
goto fail;
}
saved_errno = errno;
how = "couldn't put adt event";
goto fail;
}
(void) adt_end_session(asession);
return;
fail:
(void) adt_end_session(asession);
}
/* Logout */
audit_logout();
}
/* Login passed */
return PR_DECLINED(cmd);
}
/* Login failed */
char *login_user;
return PR_DECLINED(cmd);
}
static int audit_sess_init(void) {
/* add privs for audit init */
return rval;
}
/* basic terminal id setup */
goto out;
}
(void) adt_end_session(aht);
goto out;
}
ADT_SETTID) != 0) {
(void) adt_end_session(aht);
goto out;
}
if (adt_set_proc(aht) != 0) {
(void) adt_end_session(aht);
goto out;
}
(void) adt_end_session(aht);
/* Set handler for authentication error */
rval = 0;
out:
/* remove unneeded privileges */
return rval;
}
/* Helper functions and global variables
* for the file transfer command handlers.
* {
*/
/*
* If an error occurs in any of the file transfer handlers,
* and the handler wants to return PR_ERROR(cmd), then it is necessary
* to send some FTP error message to user. This is in order to prevent
* a hang-up of the user's ftp client.
*
* This function sends the 451 error message to the user.
* It is only called in the "pre-" handlers. When a "pre-" handler
* returns PR_ERROR(cmd), then the corresponding "post_err-"
* handler is also called. Therefore it can happen that an error condition
* (such as no memory) can be logged (with the pr_log_pri() routine) twice.
* Once in the "pre-" handler, and once in the "post_err-" handler.
*/
static void error_451(void)
{
"Requested action aborted: local error in processing.\n");
}
/*
* Allocate resources to process a command outcome.
*
* All file transfer command handlers need to allocate adt_event_data_t
* structure and also make a copy of the command argument.
* This function does both. If it can't, it logs an error and returns NULL.
* On success, it returns the pointer (event) to the allocated adt_event_data_t
* structure.
*
* If arg2 is not NULL, it makes a copy of the first (and only) command
* argument (using the memory pool "pool" from "cmd") and stores it to *arg2.
* There must be always exactly one command argument, otherwise it is an error.
*
* On success, the pointer to the created event structure is stored
* into cmd under "notes" variable, so that it is accessible
* by the subsequent corresponding "post-" or "post_err-" command handler.
*/
/* The ftp server code will save errno into this variable
* in case an error happens, and there is a valid errno for it.
*/
description, "bad argument");
goto err;
}
how = "no memory";
goto err;
}
}
goto err;
}
how = "couldn't allocate adt event";
goto err;
}
how = "pr_table_add() failed";
goto err;
}
return event;
err:
return NULL;
}
/*
* This function implements logic that is common to most "post-"
* and "post_err-" file transfer command handlers.
*
* It retrieves the pointer (event) to the adt_event_data_t structure
* from "cmd->notes" and logs it. This structure has been created by the
* __solaris_audit_pre_arg2() function.
*
* Some audit event structures contain an optional *_stat member.
* If "fill_attr" is not NULL, it is called to fill in this member,
* before the audit event is logged.
*
* This function always returns PR_DECLINED, even if it failed
* to log the audit event. The reason is that it is called in the
* "post-" file transfer command handlers, which means that the command
* has been already successfully executed by the ftp server.
*/
{
how = "event is NULL";
goto out;
}
how = "bad event size";
goto out;
}
if (fill_event != NULL) {
goto out;
}
}
/* It can happen, that the ftp command succeeds but only to some degree.
* In such case, the exit_error might contain the errno number
* of the failure.
*/
if (exit_status == ADT_SUCCESS) {
if (exit_error == ADT_FAILURE)
}
how = "couldn't put adt event";
}
out:
return PR_DECLINED(cmd);
}
/*
* This is a generic function to fill in the given "stat" member
* of some audit event structure. The path and the member are specified
* by the caller. The pointer to cmd is supplied, because the stat64
* structure has to be allocated (the "stat" member is a pointer).
*
* The function returns NULL on success.
* In case of an error, it returns a descriptive message.
* This message is used by the caller to log an error.
*
* For some file transfer commands, the "stat" member is filled in
* the "pre-" handler (because the file is expected to exist prior
* to the execution of the command). For other file transfer commands,
* the "stat" member is filled in the "post-" handler (because
* the file is expected _not_ to exist prior to the execution of the command,
* but to exist after the command execution).
*/
static const char* __fill_attr
(
{
int err;
return "NULL pointer";
return "no memory";
if (err == -1)
return "stat64() failed";
return NULL;
}
/* } */
/* Delete file. { */
return __fill_attr(
);
}
error_451();
}
error_451();
}
/* If rp is NULL and errno is ENOENT, it means that
* the file to be deleted does not exist. In this case,
* the post_dele_err callback will be called to log this.
*/
}
return PR_DECLINED(cmd);
}
return __solaris_audit_post(
}
}
/* } */
/* Make directory. { */
error_451();
}
/* Value 0777 is hardcoded in the ftp server. */
return PR_DECLINED(cmd);
}
return "realpath() failed";
}
return __fill_attr(
);
}
(void) __fill_attr(
}
return NULL;
}
return __solaris_audit_post(
}
return __solaris_audit_post(
}
/* } */
/* Remove directory. { */
return __fill_attr(
);
}
error_451();
}
error_451();
}
}
return PR_DECLINED(cmd);
}
}
}
/* } */
/* Get modification time and date. { */
error_451();
}
error_451();
}
}
return PR_DECLINED(cmd);
}
return __fill_attr(
);
}
return __solaris_audit_post(
}
}
/* } */
/* Upload file. { */
error_451();
}
return PR_DECLINED(cmd);
}
return "realpath() failed";
}
return __fill_attr(
);
}
return __solaris_audit_post(
}
}
/* } */
/* Download file. { */
error_451();
}
error_451();
}
}
return PR_DECLINED(cmd);
}
return __fill_attr(
);
}
return __solaris_audit_post(
}
}
/* } */
/* Rename file. { */
/*
* The rename file implementation uses malloc()/free(),
* which the ProFTP module interface prohibits. I do not see another way.
*
* Any memory allocation method provided by the ProFTP API uses a memory pool.
* To avoid malloc()/free() a persistent memory pool is needed.
*/
/*
* To successfully log the rename audit event, a cooperation
* of RNFR and RNTO command handlers is necessary.
* The RNFR command specifies the source file name,
* and the RNTO command specifies the destination file name.
*
* The RNFR command handlers save the source file in the "src_path"
* variable, so that it is available to the RNTO command handler,
* which logs the audit event.
*/
/* RNFR. { */
{
return;
error_451();
goto out;
}
out:
}
/*
* If src_path is not NULL, it means that this RNFR command immediatelly
* follows a successfull RNFR command not terminated with a RNTO command.
* In such case, log an audit error for this unterminated RNFR command,
* and then continue normally.
*
* A correctly working ftp client can not cause this situation to happen.
* But this situation can be created, for instance, by manually sending
* commands to the ftp server with a telnet client.
*/
/*
* Prepare the audit event structure and remember the new src_path.
* This audit event structure will be used, if the RNFR command fails.
* It will be unused, if it succeeds.
*/
goto err;
goto err;
}
return PR_DECLINED(cmd);
err:
}
/*
* On success, the RNFR command handlers do not log any audit event.
* A success means that a rename command is in progress and that
* the immediatelly following command is to be RNTO.
*/
char *ptr;
error_451();
}
/*
* The argument to RNFR command is saved in src_path.
* It will be used in the subsequent RNTO command, or RNFR command.
*/
return PR_DECLINED(cmd);
}
/* It can happen, that RNFR command fails, but the source path exists.
* Therefore make an attempt to resolve its realpath before doing
* the audit log.
*
* Even if the realpath() call fails, the src_path contents are still
* copied to src_realpath buffer. This makes them available to the RNTO
* command handlers.
*/
}
return NULL;
}
/*
* On error, an audit event is logged, specifying that a rename
* command failed. The destination path in the audit event structure
* is empty, simply because the corresponding RNTO command did not yet
* happen, and it is not suppossed to happen.
*/
return ret;
}
/* } RNFR. */
/* RNTO. { */
return __fill_attr(
);
}
goto err;
/*
* If src_path is NULL, this means that there is no previous
* successfull RNFR command. The ftp server should know about this
* and terminate this RNTO command with an error (call the error callback).
*/
/*
* If the code executes here, it means that there is a successfully finished
* RNFR command immediatelly before us, which means that the src_path exists,
* and it should be therefore possible to get its status.
*/
goto err;
}
return PR_DECLINED(cmd);
err:
error_451();
}
char *ptr;
return "realpath() failed";
}
return NULL;
}
/* NULL means that there is no preceeding successfull RNFR command. */
return retval;
}
/* It can happen, that RNTO command fails, but the destination path exists.
* Therefore make an attempt to resolve its realpath before doing
* the audit log.
*/
return NULL;
}
}
return retval;
}
/* } RNTO. */
/* Login, logout. */
/* Delete file. */
/* Make directory. */
/* Remove directory. */
/* Get modification time. */
/* Upload file. */
/* Download file. */
/* Rename file. */
{ 0, NULL }
};
0x20, /* API Version 2.0 */
"solaris_audit",
NULL, /* configuration table */
solaris_audit_commands, /* command table is for local use only */
NULL, /* No authentication handlers */
NULL, /* No initialization function */
audit_sess_init /* Post-fork "child mode" init */
};