2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
19 * As a special exemption, copyright holders give permission to link
20 * this program with OpenSSL, and distribute the resulting executable,
21 * without including the source code for OpenSSL in the source distribution.
28 #include <bsm/adt_event.h>
29 #include <security/pam_appl.h>
30 #include <sys/types.h>
39 #ifndef ADT_ftpd_logout
40 #define ADT_ftpd_logout 153
43 module solaris_audit_module
;
45 static adt_session_data_t
*asession
= NULL
;
47 static int auth_retval
= PAM_AUTH_ERR
;
49 static void audit_autherr_ev(const void *event_data
, void *user_data
) {
51 switch (*(int *)event_data
) {
53 auth_retval
= PAM_USER_UNKNOWN
;
56 auth_retval
= PAM_CRED_EXPIRED
;
58 case PR_AUTH_DISABLEDPWD
:
59 auth_retval
= PAM_ACCT_EXPIRED
;
61 case PR_AUTH_CRED_INSUFFICIENT
:
62 auth_retval
= PAM_CRED_INSUFFICIENT
;
64 case PR_AUTH_CRED_UNAVAIL
:
65 auth_retval
= PAM_CRED_UNAVAIL
;
67 case PR_AUTH_CRED_ERROR
:
68 auth_retval
= PAM_CRED_ERR
;
70 case PR_AUTH_INFO_UNAVAIL
:
71 auth_retval
= PAM_AUTHINFO_UNAVAIL
;
73 case PR_AUTH_MAX_ATTEMPTS_EXCEEDED
:
74 auth_retval
= PAM_MAXTRIES
;
76 case PR_AUTH_INIT_ERROR
:
77 auth_retval
= PAM_SESSION_ERR
;
79 case PR_AUTH_NEW_TOKEN_REQUIRED
:
80 auth_retval
= PAM_NEW_AUTHTOK_REQD
;
82 default: /* PR_AUTH_BADPWD */
83 auth_retval
= PAM_AUTH_ERR
;
89 static void audit_failure(pool
*p
, char *authuser
) {
90 adt_event_data_t
*event
= NULL
;
94 struct passwd
*result
= NULL
;
98 uid_t uid
= ADT_NO_ATTRIB
;
99 gid_t gid
= ADT_NO_ATTRIB
;
101 if ((pwdbuf_len_max
= sysconf(_SC_GETPW_R_SIZE_MAX
)) == -1) {
103 how
= "couldn't determine maximum size of password buffer";
107 pwdbuf_len
= (size_t)pwdbuf_len_max
;
108 pwdbuf
= pcalloc(p
, pwdbuf_len
);
110 if (adt_start_session(&asession
, NULL
, ADT_USE_PROC_DATA
) != 0) {
112 how
= "couldn't start adt session";
116 if ((authuser
!= NULL
) && (authuser
[0] != NULL
) &&
117 (getpwnam_r(authuser
, &pwd
, pwdbuf
, pwdbuf_len
, &result
) == 0) &&
123 if (adt_set_user(asession
, uid
, gid
, uid
, gid
, NULL
, ADT_NEW
) != 0) {
125 how
= "couldn't set adt user";
129 if ((event
= adt_alloc_event(asession
, ADT_ftpd
)) == NULL
) {
131 how
= "couldn't allocate adt event";
135 if (adt_put_event(event
, ADT_FAILURE
, ADT_FAIL_PAM
+ auth_retval
) != 0) {
137 how
= "couldn't put adt event";
141 adt_free_event(event
);
142 (void) adt_end_session(asession
);
147 pr_log_pri(PR_LOG_DEBUG
, "Auditing of login failed: %s (%s)", how
,
148 strerror(saved_errno
));
150 adt_free_event(event
);
151 (void) adt_end_session(asession
);
155 static void audit_success(void) {
156 adt_event_data_t
*event
= NULL
;
160 if (adt_start_session(&asession
, NULL
, ADT_USE_PROC_DATA
) != 0) {
162 how
= "couldn't start adt session";
166 if ((event
= adt_alloc_event(asession
, ADT_ftpd
)) == NULL
) {
168 how
= "couldn't allocate adt event";
172 if (adt_put_event(event
, ADT_SUCCESS
, ADT_SUCCESS
) != 0) {
174 how
= "couldn't put adt event";
178 adt_free_event(event
);
180 /* Don't end adt session - leave for when logging out. */
184 pr_log_pri(PR_LOG_DEBUG
, "Auditing of login failed: %s (%s)", how
,
185 strerror(saved_errno
));
187 adt_free_event(event
);
189 /* Don't end adt session - leave for when logging out. */
193 static void audit_logout(void) {
194 adt_event_data_t
*event
= NULL
;
198 /* If audit session was not created during login then leave */
199 if (asession
== NULL
)
202 if ((event
= adt_alloc_event(asession
, ADT_ftpd_logout
)) == NULL
) {
204 how
= "couldn't allocate adt event";
208 if (adt_put_event(event
, ADT_SUCCESS
, ADT_SUCCESS
) != 0) {
210 how
= "couldn't put adt event";
214 adt_free_event(event
);
215 (void) adt_end_session(asession
);
220 pr_log_pri(PR_LOG_DEBUG
, "Auditing of logout failed: %s (%s)", how
,
221 strerror(saved_errno
));
223 adt_free_event(event
);
224 (void) adt_end_session(asession
);
229 static void audit_exit_ev(const void *event_data
, void *user_data
) {
234 MODRET
solaris_audit_post_pass(cmd_rec
*cmd
) {
238 /* Set handler for logout/timeout */
239 pr_event_register(&solaris_audit_module
, "core.exit", audit_exit_ev
, NULL
);
241 return PR_DECLINED(cmd
);
245 MODRET
solaris_audit_post_fail(cmd_rec
*cmd
) {
248 login_user
= pr_table_get(session
.notes
, "mod_auth.orig-user", NULL
);
250 audit_failure(cmd
->tmp_pool
, login_user
);
251 return PR_DECLINED(cmd
);
254 static int audit_sess_init(void) {
255 adt_session_data_t
*aht
;
256 adt_termid_t
*termid
;
260 /* add privs for audit init */
261 if ((privset
= priv_allocset()) == NULL
) {
262 pr_log_pri(PR_LOG_DEBUG
, "Auditing privilege initialization failed");
266 (void) getppriv(PRIV_INHERITABLE
, privset
);
267 priv_addset(privset
, PRIV_PROC_AUDIT
);
268 (void) setppriv(PRIV_SET
, PRIV_INHERITABLE
, privset
);
270 (void) getppriv(PRIV_EFFECTIVE
, privset
);
271 priv_addset(privset
, PRIV_SYS_AUDIT
);
272 (void) setppriv(PRIV_SET
, PRIV_EFFECTIVE
, privset
);
274 /* basic terminal id setup */
275 if (adt_start_session(&aht
, NULL
, 0) != 0) {
276 pr_log_pri(PR_LOG_DEBUG
, "pam adt_start_session: %s", strerror(errno
));
279 if (adt_load_termid(session
.c
->rfd
, &termid
) != 0) {
280 pr_log_pri(PR_LOG_DEBUG
, "adt_load_termid: %s", strerror(errno
));
281 (void) adt_end_session(aht
);
285 if (adt_set_user(aht
, ADT_NO_AUDIT
, ADT_NO_AUDIT
, 0, ADT_NO_AUDIT
, termid
,
287 pr_log_pri(PR_LOG_DEBUG
, "adt_set_user: %", strerror(errno
));
289 (void) adt_end_session(aht
);
293 if (adt_set_proc(aht
) != 0) {
294 pr_log_pri(PR_LOG_DEBUG
, "adt_set_proc: %", strerror(errno
));
295 (void) adt_end_session(aht
);
298 (void) adt_end_session(aht
);
300 /* Set handler for authentication error */
301 pr_event_register(&solaris_audit_module
, "mod_auth.authentication-code",
302 audit_autherr_ev
, NULL
);
308 /* remove unneeded privileges */
309 priv_delset(privset
, PRIV_SYS_AUDIT
);
310 (void) setppriv(PRIV_SET
, PRIV_EFFECTIVE
, privset
);
311 (void) setpflags(PRIV_AWARE_RESET
, 1);
312 priv_freeset(privset
);
317 #define EVENT_KEY "event"
319 /* Helper functions and global variables
320 * for the file transfer command handlers.
324 static char src_realpath
[PATH_MAX
];
325 static char dst_realpath
[PATH_MAX
];
329 * If an error occurs in any of the file transfer handlers,
330 * and the handler wants to return PR_ERROR(cmd), then it is necessary
331 * to send some FTP error message to user. This is in order to prevent
332 * a hang-up of the user's ftp client.
334 * This function sends the 451 error message to the user.
335 * It is only called in the "pre-" handlers. When a "pre-" handler
336 * returns PR_ERROR(cmd), then the corresponding "post_err-"
337 * handler is also called. Therefore it can happen that an error condition
338 * (such as no memory) can be logged (with the pr_log_pri() routine) twice.
339 * Once in the "pre-" handler, and once in the "post_err-" handler.
341 static void error_451(void)
343 pr_response_add_err(R_451
,
344 "Requested action aborted: local error in processing.\n");
348 * Allocate resources to process a command outcome.
350 * All file transfer command handlers need to allocate adt_event_data_t
351 * structure and also make a copy of the command argument.
352 * This function does both. If it can't, it logs an error and returns NULL.
353 * On success, it returns the pointer (event) to the allocated adt_event_data_t
356 * If arg2 is not NULL, it makes a copy of the first (and only) command
357 * argument (using the memory pool "pool" from "cmd") and stores it to *arg2.
358 * There must be always exactly one command argument, otherwise it is an error.
360 * On success, the pointer to the created event structure is stored
361 * into cmd under "notes" variable, so that it is accessible
362 * by the subsequent corresponding "post-" or "post_err-" command handler.
364 adt_event_data_t
* __solaris_audit_pre_arg2(
365 cmd_rec
*cmd
, const char* description
, int event_type
, char **arg2
) {
367 adt_event_data_t
*event
= NULL
;
368 const char *how
= "";
371 /* The ftp server code will save errno into this variable
372 * in case an error happens, and there is a valid errno for it.
374 pr_cmd_set_errno(cmd
, ADT_FAILURE
);
376 if (cmd
->arg
== NULL
) {
377 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s failed: %s",
378 description
, "bad argument");
385 if ((tmp
= pstrdup(cmd
->pool
, cmd
->arg
)) == NULL
) {
387 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
388 description
, cmd
->arg
, how
);
394 if (cmd
->notes
== NULL
) {
395 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
396 description
, cmd
->arg
, "API error, notes is NULL");
400 if ((event
= adt_alloc_event(asession
, event_type
)) == NULL
) {
401 how
= "couldn't allocate adt event";
402 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s(%s)",
403 description
, cmd
->arg
, how
, strerror(errno
));
407 if (pr_table_add(cmd
->notes
, EVENT_KEY
, event
, sizeof(*event
)) == -1) {
408 how
= "pr_table_add() failed";
409 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
410 description
, cmd
->arg
, how
);
411 adt_free_event(event
);
422 * This function implements logic that is common to most "post-"
423 * and "post_err-" file transfer command handlers.
425 * It retrieves the pointer (event) to the adt_event_data_t structure
426 * from "cmd->notes" and logs it. This structure has been created by the
427 * __solaris_audit_pre_arg2() function.
429 * Some audit event structures contain an optional *_stat member.
430 * If "fill_attr" is not NULL, it is called to fill in this member,
431 * before the audit event is logged.
433 * This function always returns PR_DECLINED, even if it failed
434 * to log the audit event. The reason is that it is called in the
435 * "post-" file transfer command handlers, which means that the command
436 * has been already successfully executed by the ftp server.
438 MODRET
__solaris_audit_post(cmd_rec
*cmd
,
439 const char* description
, int exit_status
, int __unused
,
440 const char* (*fill_event
)(cmd_rec
*cmd
, adt_event_data_t
*event
))
442 adt_event_data_t
*event
= NULL
;
443 const char* how
= "";
444 const char* msg
= NULL
;
446 int exit_error
= pr_cmd_get_errno(cmd
);
448 event
= (adt_event_data_t
*)pr_table_remove(cmd
->notes
, EVENT_KEY
, &size
);
450 how
= "event is NULL";
451 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s failed: %s", description
, how
);
455 if (size
!= sizeof(*event
)) {
456 how
= "bad event size";
457 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s failed: %s", description
, how
);
461 if (fill_event
!= NULL
) {
462 msg
= fill_event(cmd
, event
);
464 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s failed: %s with %s", description
,
465 msg
, strerror(pr_cmd_get_errno(cmd
)));
470 /* It can happen, that the ftp command succeeds but only to some degree.
471 * In such case, the exit_error might contain the errno number
474 if (exit_status
== ADT_SUCCESS
) {
475 if (exit_error
== ADT_FAILURE
)
476 exit_error
= ADT_SUCCESS
;
479 if (adt_put_event(event
, exit_status
, exit_error
) != 0) {
480 how
= "couldn't put adt event";
481 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s failed: %s (%s)",
482 description
, how
, strerror(errno
));
485 adt_free_event(event
);
488 return PR_DECLINED(cmd
);
492 * This is a generic function to fill in the given "stat" member
493 * of some audit event structure. The path and the member are specified
494 * by the caller. The pointer to cmd is supplied, because the stat64
495 * structure has to be allocated (the "stat" member is a pointer).
497 * The function returns NULL on success.
498 * In case of an error, it returns a descriptive message.
499 * This message is used by the caller to log an error.
501 * For some file transfer commands, the "stat" member is filled in
502 * the "pre-" handler (because the file is expected to exist prior
503 * to the execution of the command). For other file transfer commands,
504 * the "stat" member is filled in the "post-" handler (because
505 * the file is expected _not_ to exist prior to the execution of the command,
506 * but to exist after the command execution).
508 static const char* __fill_attr
510 cmd_rec
*cmd
, const char* path
, adt_stat_t
**ret
)
516 return "NULL pointer";
520 ptr
= palloc(cmd
->pool
, sizeof(*ptr
));
524 err
= stat64(path
, ptr
);
526 return "stat64() failed";
535 static const char* dele_fill_attr(cmd_rec
*cmd
, adt_event_data_t
*event
) {
537 cmd
, event
->adt_ft_remove
.f_path
, &(event
->adt_ft_remove
.f_attr
)
541 MODRET
solaris_audit_pre_dele(cmd_rec
*cmd
) {
542 adt_event_data_t
*event
= NULL
;
546 event
= __solaris_audit_pre_arg2(cmd
, "remove", ADT_ft_remove
, &ptr
);
549 return PR_ERROR(cmd
);
552 rp
= realpath(ptr
, src_realpath
);
554 if (errno
!= ENOENT
) {
555 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
556 "remove", ptr
, "realpath() failed");
557 pr_cmd_set_errno(cmd
, errno
);
559 return PR_ERROR(cmd
);
561 /* If rp is NULL and errno is ENOENT, it means that
562 * the file to be deleted does not exist. In this case,
563 * the post_dele_err callback will be called to log this.
570 event
->adt_ft_remove
.f_path
= ptr
;
571 (void) dele_fill_attr(cmd
, event
);
573 return PR_DECLINED(cmd
);
576 MODRET
solaris_audit_post_dele(cmd_rec
*cmd
) {
577 return __solaris_audit_post(
578 cmd
, "remove", ADT_SUCCESS
, ADT_SUCCESS
, NULL
);
581 MODRET
solaris_audit_post_dele_err(cmd_rec
*cmd
) {
582 return __solaris_audit_post(cmd
, "remove", ADT_FAILURE
, ADT_FAILURE
, NULL
);
587 /* Make directory. { */
588 MODRET
solaris_audit_pre_mkd(cmd_rec
*cmd
) {
589 adt_event_data_t
*event
= NULL
;
592 event
= __solaris_audit_pre_arg2(cmd
, "mkdir", ADT_ft_mkdir
, &ptr
);
595 return PR_ERROR(cmd
);
598 event
->adt_ft_mkdir
.d_path
= ptr
;
599 event
->adt_ft_mkdir
.d_attr
= NULL
;
601 /* Value 0777 is hardcoded in the ftp server. */
602 event
->adt_ft_mkdir
.arg
= 0777;
603 event
->adt_ft_mkdir
.arg_id
= 2;
604 event
->adt_ft_mkdir
.arg_desc
= "mode";
606 return PR_DECLINED(cmd
);
609 static const char* mkd_fill_event(cmd_rec
*cmd
, adt_event_data_t
*event
) {
612 rp
= realpath(event
->adt_ft_mkdir
.d_path
, src_realpath
);
614 pr_cmd_set_errno(cmd
, errno
);
615 return "realpath() failed";
618 event
->adt_ft_mkdir
.d_path
= rp
;
620 cmd
, event
->adt_ft_mkdir
.d_path
, &(event
->adt_ft_mkdir
.d_attr
)
624 static const char* mkd_fill_event_err(cmd_rec
*cmd
, adt_event_data_t
*event
) {
627 rp
= realpath(event
->adt_ft_mkdir
.d_path
, src_realpath
);
629 event
->adt_ft_mkdir
.d_path
= rp
;
631 cmd
, event
->adt_ft_mkdir
.d_path
, &(event
->adt_ft_mkdir
.d_attr
));
637 MODRET
solaris_audit_post_mkd(cmd_rec
*cmd
) {
638 return __solaris_audit_post(
639 cmd
, "mkdir", ADT_SUCCESS
, ADT_SUCCESS
, mkd_fill_event
);
642 MODRET
solaris_audit_post_mkd_err(cmd_rec
*cmd
) {
643 return __solaris_audit_post(
644 cmd
, "mkdir", ADT_FAILURE
, ADT_FAILURE
, mkd_fill_event_err
);
648 /* Remove directory. { */
649 static const char* rmd_fill_attr(cmd_rec
*cmd
, adt_event_data_t
*event
) {
651 cmd
, event
->adt_ft_rmdir
.f_path
, &(event
->adt_ft_rmdir
.f_attr
)
655 MODRET
solaris_audit_pre_rmd(cmd_rec
*cmd
) {
656 adt_event_data_t
*event
= NULL
;
660 event
= __solaris_audit_pre_arg2(cmd
, "rmdir", ADT_ft_rmdir
, &ptr
);
663 return PR_ERROR(cmd
);
666 rp
= realpath(ptr
, src_realpath
);
668 if (errno
!= ENOENT
) {
669 pr_cmd_set_errno(cmd
, errno
);
670 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
671 "rmdir", ptr
, "realpath() failed");
673 return PR_ERROR(cmd
);
680 event
->adt_ft_rmdir
.f_path
= ptr
;
681 (void) rmd_fill_attr(cmd
, event
);
683 return PR_DECLINED(cmd
);
686 MODRET
solaris_audit_post_rmd(cmd_rec
*cmd
) {
687 return __solaris_audit_post(cmd
, "rmdir", ADT_SUCCESS
, ADT_SUCCESS
, NULL
);
690 MODRET
solaris_audit_post_rmd_err(cmd_rec
*cmd
) {
691 return __solaris_audit_post(cmd
, "rmdir", ADT_FAILURE
, ADT_FAILURE
, NULL
);
695 /* Get modification time and date. { */
696 MODRET
solaris_audit_pre_mdtm(cmd_rec
*cmd
) {
697 adt_event_data_t
*event
= NULL
;
701 event
= __solaris_audit_pre_arg2(cmd
, "utimes", ADT_ft_utimes
, &ptr
);
704 return PR_ERROR(cmd
);
707 rp
= realpath(ptr
, src_realpath
);
709 if (errno
!= ENOENT
) {
710 pr_cmd_set_errno(cmd
, errno
);
711 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
712 "utimes", ptr
, "realpath() failed");
714 return PR_ERROR(cmd
);
721 event
->adt_ft_utimes
.f_path
= ptr
;
722 event
->adt_ft_utimes
.f_attr
= NULL
;
724 return PR_DECLINED(cmd
);
727 static const char* mdtm_fill_attr(cmd_rec
*cmd
, adt_event_data_t
*event
) {
729 cmd
, event
->adt_ft_utimes
.f_path
, &(event
->adt_ft_utimes
.f_attr
)
733 MODRET
solaris_audit_post_mdtm(cmd_rec
*cmd
) {
734 return __solaris_audit_post(
735 cmd
, "utimes", ADT_SUCCESS
, ADT_SUCCESS
, mdtm_fill_attr
);
738 MODRET
solaris_audit_post_mdtm_err(cmd_rec
*cmd
) {
739 return __solaris_audit_post(cmd
, "utimes", ADT_FAILURE
, ADT_FAILURE
, NULL
);
744 MODRET
solaris_audit_pre_put(cmd_rec
*cmd
) {
745 adt_event_data_t
*event
= NULL
;
748 event
= __solaris_audit_pre_arg2(cmd
, "put", ADT_ft_put
, &ptr
);
751 return PR_ERROR(cmd
);
754 event
->adt_ft_put
.f_path
= ptr
;
755 event
->adt_ft_put
.f_attr
= NULL
;
757 return PR_DECLINED(cmd
);
760 static const char* put_fill_event(cmd_rec
*cmd
, adt_event_data_t
*event
) {
763 rp
= realpath(event
->adt_ft_put
.f_path
, src_realpath
);
765 pr_cmd_set_errno(cmd
, errno
);
766 return "realpath() failed";
769 event
->adt_ft_put
.f_path
= rp
;
771 cmd
, event
->adt_ft_put
.f_path
, &(event
->adt_ft_put
.f_attr
)
775 MODRET
solaris_audit_post_put(cmd_rec
*cmd
) {
776 return __solaris_audit_post(
777 cmd
, "put", ADT_SUCCESS
, ADT_SUCCESS
, put_fill_event
);
780 MODRET
solaris_audit_post_put_err(cmd_rec
*cmd
) {
781 return __solaris_audit_post(cmd
, "put", ADT_FAILURE
, ADT_FAILURE
, NULL
);
785 /* Download file. { */
786 MODRET
solaris_audit_pre_get(cmd_rec
*cmd
) {
787 adt_event_data_t
*event
= NULL
;
791 event
= __solaris_audit_pre_arg2(cmd
, "get", ADT_ft_get
, &ptr
);
794 return PR_ERROR(cmd
);
797 rp
= realpath(ptr
, src_realpath
);
799 if (errno
!= ENOENT
) {
800 pr_cmd_set_errno(cmd
, errno
);
801 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
802 "get", ptr
, "realpath() failed");
804 return PR_ERROR(cmd
);
811 event
->adt_ft_get
.f_path
= ptr
;
812 event
->adt_ft_get
.f_attr
= NULL
;
814 return PR_DECLINED(cmd
);
817 static const char* get_fill_attr(cmd_rec
*cmd
, adt_event_data_t
*event
) {
819 cmd
, event
->adt_ft_get
.f_path
, &(event
->adt_ft_get
.f_attr
)
823 MODRET
solaris_audit_post_get(cmd_rec
*cmd
) {
824 return __solaris_audit_post(
825 cmd
, "get", ADT_SUCCESS
, ADT_SUCCESS
, get_fill_attr
);
828 MODRET
solaris_audit_post_get_err(cmd_rec
*cmd
) {
829 return __solaris_audit_post(cmd
, "get", ADT_FAILURE
, ADT_FAILURE
, NULL
);
835 * The rename file implementation uses malloc()/free(),
836 * which the ProFTP module interface prohibits. I do not see another way.
838 * Any memory allocation method provided by the ProFTP API uses a memory pool.
839 * To avoid malloc()/free() a persistent memory pool is needed.
843 * To successfully log the rename audit event, a cooperation
844 * of RNFR and RNTO command handlers is necessary.
845 * The RNFR command specifies the source file name,
846 * and the RNTO command specifies the destination file name.
848 * The RNFR command handlers save the source file in the "src_path"
849 * variable, so that it is available to the RNTO command handler,
850 * which logs the audit event.
852 static char* src_path
= NULL
;
855 static void __solaris_audit_rnfr_err(cmd_rec
*cmd
)
857 adt_event_data_t
*event
= NULL
;
859 if (src_path
== NULL
)
862 event
= __solaris_audit_pre_arg2(cmd
, "RNFR", ADT_ft_rename
, NULL
);
868 event
->adt_ft_rename
.src_path
= src_path
;
869 event
->adt_ft_rename
.src_attr
= NULL
;
870 event
->adt_ft_rename
.dst_path
= NULL
;
872 (void) __solaris_audit_post(cmd
, "RNFR", ADT_FAILURE
, ADT_FAILURE
, NULL
);
879 MODRET
solaris_audit_pre_rnfr(cmd_rec
*cmd
) {
880 adt_event_data_t
*event
= NULL
;
884 * If src_path is not NULL, it means that this RNFR command immediatelly
885 * follows a successful RNFR command not terminated with a RNTO command.
886 * In such case, log an audit error for this unterminated RNFR command,
887 * and then continue normally.
889 * A correctly working ftp client can not cause this situation to happen.
890 * But this situation can be created, for instance, by manually sending
891 * commands to the ftp server with a telnet client.
893 if (src_path
!= NULL
)
894 __solaris_audit_rnfr_err(cmd
);
897 * Prepare the audit event structure and remember the new src_path.
898 * This audit event structure will be used, if the RNFR command fails.
899 * It will be unused, if it succeeds.
901 event
= __solaris_audit_pre_arg2(cmd
, "get", ADT_ft_rename
, &ptr
);
905 event
->adt_ft_rename
.src_path
= ptr
;
906 event
->adt_ft_rename
.src_attr
= NULL
;
907 event
->adt_ft_rename
.dst_path
= "";
909 src_path
= strdup(cmd
->arg
);
910 if (src_path
== NULL
) {
911 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
912 "RNFR", ptr
, "no memory");
916 return PR_DECLINED(cmd
);
918 return PR_ERROR(cmd
);
922 * On success, the RNFR command handlers do not log any audit event.
923 * A success means that a rename command is in progress and that
924 * the immediatelly following command is to be RNTO.
926 MODRET
solaris_audit_post_rnfr(cmd_rec
*cmd
) {
930 ptr
= realpath(src_path
, src_realpath
);
932 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s) failed: %s",
933 "RNFR", src_path
, "realpath() failed");
935 return PR_ERROR(cmd
);
939 * The argument to RNFR command is saved in src_path.
940 * It will be used in the subsequent RNTO command, or RNFR command.
942 return PR_DECLINED(cmd
);
945 /* It can happen, that RNFR command fails, but the source path exists.
946 * Therefore make an attempt to resolve its realpath before doing
949 * Even if the realpath() call fails, the src_path contents are still
950 * copied to src_realpath buffer. This makes them available to the RNTO
953 static const char* rnfr_err_fill_event(cmd_rec
*cmd
, adt_event_data_t
*event
) {
956 if (src_path
!= NULL
) {
957 ptr
= realpath(src_path
, src_realpath
);
959 event
->adt_ft_rename
.src_path
= ptr
;
966 * On error, an audit event is logged, specifying that a rename
967 * command failed. The destination path in the audit event structure
968 * is empty, simply because the corresponding RNTO command did not yet
969 * happen, and it is not suppossed to happen.
971 MODRET
solaris_audit_post_rnfr_err(cmd_rec
*cmd
) {
974 ret
= __solaris_audit_post(cmd
, "RNFR", ADT_FAILURE
, ADT_FAILURE
,
975 rnfr_err_fill_event
);
985 static const char* rnto_fill_attr(cmd_rec
*cmd
, adt_event_data_t
*event
) {
987 cmd
, event
->adt_ft_rename
.src_path
, &(event
->adt_ft_rename
.src_attr
)
991 MODRET
solaris_audit_pre_rnto(cmd_rec
*cmd
) {
992 adt_event_data_t
*event
= NULL
;
993 const char* msg
= NULL
;
996 event
= __solaris_audit_pre_arg2(cmd
, "get", ADT_ft_rename
, &ptr
);
1001 * If src_path is NULL, this means that there is no previous
1002 * successful RNFR command. The ftp server should know about this
1003 * and terminate this RNTO command with an error (call the error callback).
1005 event
->adt_ft_rename
.src_path
= (src_path
)?src_path
:"";
1006 event
->adt_ft_rename
.dst_path
= ptr
;
1009 * If the code executes here, it means that there is a successfully finished
1010 * RNFR command immediatelly before us, which means that the src_path exists,
1011 * and it should be therefore possible to get its status.
1013 msg
= rnto_fill_attr(cmd
, event
);
1015 pr_log_pri(PR_LOG_DEBUG
, "Auditing of %s(%s,%s) failed: %s",
1016 "RNTO", event
->adt_ft_rename
.src_path
, ptr
, msg
);
1020 return PR_DECLINED(cmd
);
1024 return PR_ERROR(cmd
);
1027 static const char* rnto_fill_event(cmd_rec
*cmd
, adt_event_data_t
*event
) {
1030 ptr
= realpath(event
->adt_ft_rename
.dst_path
, dst_realpath
);
1032 return "realpath() failed";
1035 event
->adt_ft_rename
.src_path
= src_realpath
;
1036 event
->adt_ft_rename
.dst_path
= dst_realpath
;
1041 MODRET
solaris_audit_post_rnto(cmd_rec
*cmd
) {
1044 /* NULL means that there is no preceeding successful RNFR command. */
1045 if (src_path
== NULL
)
1046 return PR_ERROR(cmd
);
1048 retval
= __solaris_audit_post(cmd
, "RNTO", ADT_SUCCESS
, ADT_SUCCESS
,
1057 /* It can happen, that RNTO command fails, but the destination path exists.
1058 * Therefore make an attempt to resolve its realpath before doing
1061 static const char* rnto_err_fill_event(cmd_rec
*cmd
, adt_event_data_t
*event
) {
1063 (void) realpath(event
->adt_ft_rename
.dst_path
, dst_realpath
);
1064 event
->adt_ft_rename
.src_path
= src_realpath
;
1065 event
->adt_ft_rename
.dst_path
= dst_realpath
;
1070 MODRET
solaris_audit_post_rnto_err(cmd_rec
*cmd
) {
1072 retval
= __solaris_audit_post(cmd
, "RNTO", ADT_FAILURE
, ADT_FAILURE
,
1073 rnto_err_fill_event
);
1074 if (src_path
!= NULL
) {
1082 static cmdtable solaris_audit_commands
[] = {
1083 /* Login, logout. */
1084 { POST_CMD
, C_PASS
, G_NONE
, solaris_audit_post_pass
, FALSE
, FALSE
},
1085 { POST_CMD_ERR
, C_PASS
, G_NONE
, solaris_audit_post_fail
, FALSE
, FALSE
},
1088 { PRE_CMD
, C_DELE
, G_NONE
, solaris_audit_pre_dele
, FALSE
, FALSE
},
1089 { POST_CMD
, C_DELE
, G_NONE
, solaris_audit_post_dele
, FALSE
, FALSE
},
1090 { POST_CMD_ERR
, C_DELE
, G_NONE
, solaris_audit_post_dele_err
,
1093 /* Make directory. */
1094 { PRE_CMD
, C_MKD
, G_NONE
, solaris_audit_pre_mkd
, FALSE
, FALSE
},
1095 { POST_CMD
, C_MKD
, G_NONE
, solaris_audit_post_mkd
, FALSE
, FALSE
},
1096 { POST_CMD_ERR
, C_MKD
, G_NONE
, solaris_audit_post_mkd_err
,
1099 /* Remove directory. */
1100 { PRE_CMD
, C_RMD
, G_NONE
, solaris_audit_pre_rmd
, FALSE
, FALSE
},
1101 { POST_CMD
, C_RMD
, G_NONE
, solaris_audit_post_rmd
, FALSE
, FALSE
},
1102 { POST_CMD_ERR
, C_RMD
, G_NONE
, solaris_audit_post_rmd_err
,
1105 { PRE_CMD
, C_XRMD
, G_NONE
, solaris_audit_pre_rmd
, FALSE
, FALSE
},
1106 { POST_CMD
, C_XRMD
, G_NONE
, solaris_audit_post_rmd
, FALSE
, FALSE
},
1107 { POST_CMD_ERR
, C_XRMD
, G_NONE
, solaris_audit_post_rmd_err
,
1110 /* Get modification time. */
1111 { PRE_CMD
, C_MDTM
, G_NONE
, solaris_audit_pre_mdtm
, FALSE
, FALSE
},
1112 { POST_CMD
, C_MDTM
, G_NONE
, solaris_audit_post_mdtm
, FALSE
, FALSE
},
1113 { POST_CMD_ERR
, C_MDTM
, G_NONE
, solaris_audit_post_mdtm_err
,
1117 { PRE_CMD
, C_STOR
, G_WRITE
, solaris_audit_pre_put
, FALSE
, FALSE
},
1118 { POST_CMD
, C_STOR
, G_WRITE
, solaris_audit_post_put
, FALSE
, FALSE
},
1119 { POST_CMD_ERR
, C_STOR
, G_WRITE
, solaris_audit_post_put_err
,
1122 { PRE_CMD
, C_STOU
, G_WRITE
, solaris_audit_pre_put
, FALSE
, FALSE
},
1123 { POST_CMD
, C_STOU
, G_WRITE
, solaris_audit_post_put
, FALSE
, FALSE
},
1124 { POST_CMD_ERR
, C_STOU
, G_WRITE
, solaris_audit_post_put_err
,
1127 { PRE_CMD
, C_APPE
, G_WRITE
, solaris_audit_pre_put
, FALSE
, FALSE
},
1128 { POST_CMD
, C_APPE
, G_WRITE
, solaris_audit_post_put
, FALSE
, FALSE
},
1129 { POST_CMD_ERR
, C_APPE
, G_WRITE
, solaris_audit_post_put_err
,
1132 /* Download file. */
1133 { PRE_CMD
, C_RETR
, G_READ
, solaris_audit_pre_get
, FALSE
, FALSE
},
1134 { POST_CMD
, C_RETR
, G_READ
, solaris_audit_post_get
, FALSE
, FALSE
},
1135 { POST_CMD_ERR
, C_RETR
, G_READ
, solaris_audit_post_get_err
,
1139 { PRE_CMD
, C_RNFR
, G_NONE
, solaris_audit_pre_rnfr
, FALSE
, FALSE
},
1140 { POST_CMD
, C_RNFR
, G_NONE
, solaris_audit_post_rnfr
, FALSE
, FALSE
},
1141 { POST_CMD_ERR
, C_RNFR
, G_NONE
, solaris_audit_post_rnfr_err
,
1144 { PRE_CMD
, C_RNTO
, G_NONE
, solaris_audit_pre_rnto
, FALSE
, FALSE
},
1145 { POST_CMD
, C_RNTO
, G_NONE
, solaris_audit_post_rnto
, FALSE
, FALSE
},
1146 { POST_CMD_ERR
, C_RNTO
, G_NONE
, solaris_audit_post_rnto_err
,
1152 module solaris_audit_module
= {
1153 NULL
, NULL
, /* Always NULL */
1154 0x20, /* API Version 2.0 */
1156 NULL
, /* configuration table */
1157 solaris_audit_commands
, /* command table is for local use only */
1158 NULL
, /* No authentication handlers */
1159 NULL
, /* No initialization function */
1160 audit_sess_init
/* Post-fork "child mode" init */