p7zip: assorted fixes
[oi-userland.git] / components / network / proftpd / mod_solaris_audit.c
blob43421be241895248ee2f44046441478fbb81f44a
1 /*
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.
25 #include "conf.h"
26 #include "cmd.h"
27 #include <bsm/adt.h>
28 #include <bsm/adt_event.h>
29 #include <security/pam_appl.h>
30 #include <sys/types.h>
31 #include <pwd.h>
32 #include <unistd.h>
33 #include <ucred.h>
35 #ifndef ADT_ftpd
36 #define ADT_ftpd 152
37 #endif
39 #ifndef ADT_ftpd_logout
40 #define ADT_ftpd_logout 153
41 #endif
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) {
52 case PR_AUTH_NOPWD:
53 auth_retval = PAM_USER_UNKNOWN;
54 break;
55 case PR_AUTH_AGEPWD:
56 auth_retval = PAM_CRED_EXPIRED;
57 break;
58 case PR_AUTH_DISABLEDPWD:
59 auth_retval = PAM_ACCT_EXPIRED;
60 break;
61 case PR_AUTH_CRED_INSUFFICIENT:
62 auth_retval = PAM_CRED_INSUFFICIENT;
63 break;
64 case PR_AUTH_CRED_UNAVAIL:
65 auth_retval = PAM_CRED_UNAVAIL;
66 break;
67 case PR_AUTH_CRED_ERROR:
68 auth_retval = PAM_CRED_ERR;
69 break;
70 case PR_AUTH_INFO_UNAVAIL:
71 auth_retval = PAM_AUTHINFO_UNAVAIL;
72 break;
73 case PR_AUTH_MAX_ATTEMPTS_EXCEEDED:
74 auth_retval = PAM_MAXTRIES;
75 break;
76 case PR_AUTH_INIT_ERROR:
77 auth_retval = PAM_SESSION_ERR;
78 break;
79 case PR_AUTH_NEW_TOKEN_REQUIRED:
80 auth_retval = PAM_NEW_AUTHTOK_REQD;
81 break;
82 default: /* PR_AUTH_BADPWD */
83 auth_retval = PAM_AUTH_ERR;
84 break;
89 static void audit_failure(pool *p, char *authuser) {
90 adt_event_data_t *event = NULL;
91 const char *how;
92 int saved_errno = 0;
93 struct passwd pwd;
94 struct passwd *result = NULL;
95 char *pwdbuf = NULL;
96 size_t pwdbuf_len;
97 long pwdbuf_len_max;
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) {
102 saved_errno = errno;
103 how = "couldn't determine maximum size of password buffer";
104 goto fail;
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) {
111 saved_errno = errno;
112 how = "couldn't start adt session";
113 goto fail;
116 if ((authuser != NULL) && (authuser[0] != NULL) &&
117 (getpwnam_r(authuser, &pwd, pwdbuf, pwdbuf_len, &result) == 0) &&
118 (result != NULL)) {
119 uid = pwd.pw_uid;
120 gid = pwd.pw_gid;
123 if (adt_set_user(asession, uid, gid, uid, gid, NULL, ADT_NEW) != 0) {
124 saved_errno = errno;
125 how = "couldn't set adt user";
126 goto fail;
129 if ((event = adt_alloc_event(asession, ADT_ftpd)) == NULL) {
130 saved_errno = errno;
131 how = "couldn't allocate adt event";
132 goto fail;
135 if (adt_put_event(event, ADT_FAILURE, ADT_FAIL_PAM + auth_retval) != 0) {
136 saved_errno = errno;
137 how = "couldn't put adt event";
138 goto fail;
141 adt_free_event(event);
142 (void) adt_end_session(asession);
143 asession = NULL;
144 return;
146 fail:
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);
152 asession = NULL;
155 static void audit_success(void) {
156 adt_event_data_t *event = NULL;
157 const char *how;
158 int saved_errno = 0;
160 if (adt_start_session(&asession, NULL, ADT_USE_PROC_DATA) != 0) {
161 saved_errno = errno;
162 how = "couldn't start adt session";
163 goto fail;
166 if ((event = adt_alloc_event(asession, ADT_ftpd)) == NULL) {
167 saved_errno = errno;
168 how = "couldn't allocate adt event";
169 goto fail;
172 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
173 saved_errno = errno;
174 how = "couldn't put adt event";
175 goto fail;
178 adt_free_event(event);
180 /* Don't end adt session - leave for when logging out. */
181 return;
183 fail:
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;
195 const char *how;
196 int saved_errno = 0;
198 /* If audit session was not created during login then leave */
199 if (asession == NULL)
200 return;
202 if ((event = adt_alloc_event(asession, ADT_ftpd_logout)) == NULL) {
203 saved_errno = errno;
204 how = "couldn't allocate adt event";
205 goto fail;
208 if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
209 saved_errno = errno;
210 how = "couldn't put adt event";
211 goto fail;
214 adt_free_event(event);
215 (void) adt_end_session(asession);
216 asession = NULL;
217 return;
219 fail:
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);
225 asession = NULL;
228 /* Logout */
229 static void audit_exit_ev(const void *event_data, void *user_data) {
230 audit_logout();
233 /* Login passed */
234 MODRET solaris_audit_post_pass(cmd_rec *cmd) {
236 audit_success();
238 /* Set handler for logout/timeout */
239 pr_event_register(&solaris_audit_module, "core.exit", audit_exit_ev, NULL);
241 return PR_DECLINED(cmd);
244 /* Login failed */
245 MODRET solaris_audit_post_fail(cmd_rec *cmd) {
246 char *login_user;
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;
257 priv_set_t *privset;
258 int rval = -1;
260 /* add privs for audit init */
261 if ((privset = priv_allocset()) == NULL) {
262 pr_log_pri(PR_LOG_DEBUG, "Auditing privilege initialization failed");
263 return rval;
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));
277 goto out;
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);
282 goto out;
285 if (adt_set_user(aht, ADT_NO_AUDIT, ADT_NO_AUDIT, 0, ADT_NO_AUDIT, termid,
286 ADT_SETTID) != 0) {
287 pr_log_pri(PR_LOG_DEBUG, "adt_set_user: %", strerror(errno));
288 free(termid);
289 (void) adt_end_session(aht);
290 goto out;
292 free(termid);
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);
296 goto out;
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);
304 rval = 0;
306 out:
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);
314 return rval;
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
354 * structure.
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 = "";
369 char *tmp = NULL;
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");
379 goto err;
382 if (arg2 != NULL) {
383 *arg2 = NULL;
385 if ((tmp = pstrdup(cmd->pool, cmd->arg)) == NULL) {
386 how = "no memory";
387 pr_log_pri(PR_LOG_DEBUG, "Auditing of %s(%s) failed: %s",
388 description, cmd->arg, how);
389 goto err;
391 *arg2 = tmp;
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");
397 goto err;
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));
404 goto err;
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);
412 goto err;
415 return event;
417 err:
418 return NULL;
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;
445 size_t size = 0;
446 int exit_error = pr_cmd_get_errno(cmd);
448 event = (adt_event_data_t*)pr_table_remove(cmd->notes, EVENT_KEY, &size);
449 if (event == NULL) {
450 how = "event is NULL";
451 pr_log_pri(PR_LOG_DEBUG, "Auditing of %s failed: %s", description, how);
452 goto out;
455 if (size != sizeof(*event)) {
456 how = "bad event size";
457 pr_log_pri(PR_LOG_DEBUG, "Auditing of %s failed: %s", description, how);
458 goto out;
461 if (fill_event != NULL) {
462 msg = fill_event(cmd, event);
463 if (msg != NULL) {
464 pr_log_pri(PR_LOG_DEBUG, "Auditing of %s failed: %s with %s", description,
465 msg, strerror(pr_cmd_get_errno(cmd)));
466 goto out;
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
472 * of the failure.
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);
487 out:
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)
512 struct stat64 *ptr;
513 int err;
515 if (ret == NULL)
516 return "NULL pointer";
518 *ret = NULL;
520 ptr = palloc(cmd->pool, sizeof(*ptr));
521 if (ptr == NULL)
522 return "no memory";
524 err = stat64(path, ptr);
525 if (err == -1)
526 return "stat64() failed";
528 *ret = ptr;
529 return NULL;
531 /* } */
534 /* Delete file. { */
535 static const char* dele_fill_attr(cmd_rec *cmd, adt_event_data_t *event) {
536 return __fill_attr(
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;
543 char* ptr = NULL;
544 char* rp = NULL;
546 event = __solaris_audit_pre_arg2(cmd, "remove", ADT_ft_remove, &ptr);
547 if (event == NULL) {
548 error_451();
549 return PR_ERROR(cmd);
552 rp = realpath(ptr, src_realpath);
553 if (rp == NULL) {
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);
558 error_451();
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.
567 if (rp != NULL)
568 ptr = rp;
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);
584 /* } */
587 /* Make directory. { */
588 MODRET solaris_audit_pre_mkd(cmd_rec *cmd) {
589 adt_event_data_t *event = NULL;
590 char* ptr = NULL;
592 event = __solaris_audit_pre_arg2(cmd, "mkdir", ADT_ft_mkdir, &ptr);
593 if (event == NULL) {
594 error_451();
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) {
610 char *rp = NULL;
612 rp = realpath(event->adt_ft_mkdir.d_path, src_realpath);
613 if (rp == NULL) {
614 pr_cmd_set_errno(cmd, errno);
615 return "realpath() failed";
618 event->adt_ft_mkdir.d_path = rp;
619 return __fill_attr(
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) {
625 char *rp = NULL;
627 rp = realpath(event->adt_ft_mkdir.d_path, src_realpath);
628 if (rp != NULL) {
629 event->adt_ft_mkdir.d_path = rp;
630 (void) __fill_attr(
631 cmd, event->adt_ft_mkdir.d_path, &(event->adt_ft_mkdir.d_attr));
634 return NULL;
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);
646 /* } */
648 /* Remove directory. { */
649 static const char* rmd_fill_attr(cmd_rec *cmd, adt_event_data_t *event) {
650 return __fill_attr(
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;
657 char* ptr = NULL;
658 char* rp = NULL;
660 event = __solaris_audit_pre_arg2(cmd, "rmdir", ADT_ft_rmdir, &ptr);
661 if (event == NULL) {
662 error_451();
663 return PR_ERROR(cmd);
666 rp = realpath(ptr, src_realpath);
667 if (rp == NULL) {
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");
672 error_451();
673 return PR_ERROR(cmd);
677 if (rp != NULL)
678 ptr = rp;
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);
693 /* } */
695 /* Get modification time and date. { */
696 MODRET solaris_audit_pre_mdtm(cmd_rec *cmd) {
697 adt_event_data_t *event = NULL;
698 char* ptr = NULL;
699 char* rp = NULL;
701 event = __solaris_audit_pre_arg2(cmd, "utimes", ADT_ft_utimes, &ptr);
702 if (event == NULL) {
703 error_451();
704 return PR_ERROR(cmd);
707 rp = realpath(ptr, src_realpath);
708 if (rp == NULL) {
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");
713 error_451();
714 return PR_ERROR(cmd);
718 if (rp != NULL)
719 ptr = rp;
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) {
728 return __fill_attr(
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);
741 /* } */
743 /* Upload file. { */
744 MODRET solaris_audit_pre_put(cmd_rec *cmd) {
745 adt_event_data_t *event = NULL;
746 char* ptr = NULL;
748 event = __solaris_audit_pre_arg2(cmd, "put", ADT_ft_put, &ptr);
749 if (event == NULL) {
750 error_451();
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) {
761 char *rp = NULL;
763 rp = realpath(event->adt_ft_put.f_path, src_realpath);
764 if (rp == NULL) {
765 pr_cmd_set_errno(cmd, errno);
766 return "realpath() failed";
769 event->adt_ft_put.f_path = rp;
770 return __fill_attr(
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);
783 /* } */
785 /* Download file. { */
786 MODRET solaris_audit_pre_get(cmd_rec *cmd) {
787 adt_event_data_t *event = NULL;
788 char* ptr = NULL;
789 char* rp = NULL;
791 event = __solaris_audit_pre_arg2(cmd, "get", ADT_ft_get, &ptr);
792 if (event == NULL) {
793 error_451();
794 return PR_ERROR(cmd);
797 rp = realpath(ptr, src_realpath);
798 if (rp == NULL) {
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");
803 error_451();
804 return PR_ERROR(cmd);
808 if (rp != NULL)
809 ptr = rp;
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) {
818 return __fill_attr(
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);
831 /* } */
833 /* Rename file. { */
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;
854 /* RNFR. { */
855 static void __solaris_audit_rnfr_err(cmd_rec *cmd)
857 adt_event_data_t *event = NULL;
859 if (src_path == NULL)
860 return;
862 event = __solaris_audit_pre_arg2(cmd, "RNFR", ADT_ft_rename, NULL);
863 if (event == NULL) {
864 error_451();
865 goto out;
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);
874 out:
875 free(src_path);
876 src_path = NULL;
879 MODRET solaris_audit_pre_rnfr(cmd_rec *cmd) {
880 adt_event_data_t *event = NULL;
881 char* ptr = 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);
902 if (event == NULL)
903 goto err;
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");
913 goto err;
916 return PR_DECLINED(cmd);
917 err:
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) {
928 char *ptr;
930 ptr = realpath(src_path, src_realpath);
931 if (ptr == NULL) {
932 pr_log_pri(PR_LOG_DEBUG, "Auditing of %s(%s) failed: %s",
933 "RNFR", src_path, "realpath() failed");
934 error_451();
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
947 * the audit log.
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
951 * command handlers.
953 static const char* rnfr_err_fill_event(cmd_rec *cmd, adt_event_data_t *event) {
954 char *ptr = NULL;
956 if (src_path != NULL) {
957 ptr = realpath(src_path, src_realpath);
958 if (ptr != NULL)
959 event->adt_ft_rename.src_path = ptr;
962 return NULL;
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) {
972 MODRET ret;
974 ret = __solaris_audit_post(cmd, "RNFR", ADT_FAILURE, ADT_FAILURE,
975 rnfr_err_fill_event);
977 free(src_path);
978 src_path = NULL;
980 return ret;
982 /* } RNFR. */
984 /* RNTO. { */
985 static const char* rnto_fill_attr(cmd_rec *cmd, adt_event_data_t *event) {
986 return __fill_attr(
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;
994 char* ptr = NULL;
996 event = __solaris_audit_pre_arg2(cmd, "get", ADT_ft_rename, &ptr);
997 if (event == NULL)
998 goto err;
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);
1014 if (msg != NULL) {
1015 pr_log_pri(PR_LOG_DEBUG, "Auditing of %s(%s,%s) failed: %s",
1016 "RNTO", event->adt_ft_rename.src_path, ptr, msg);
1017 goto err;
1020 return PR_DECLINED(cmd);
1022 err:
1023 error_451();
1024 return PR_ERROR(cmd);
1027 static const char* rnto_fill_event(cmd_rec *cmd, adt_event_data_t *event) {
1028 char *ptr;
1030 ptr = realpath(event->adt_ft_rename.dst_path, dst_realpath);
1031 if (ptr == NULL) {
1032 return "realpath() failed";
1035 event->adt_ft_rename.src_path = src_realpath;
1036 event->adt_ft_rename.dst_path = dst_realpath;
1038 return NULL;
1041 MODRET solaris_audit_post_rnto(cmd_rec *cmd) {
1042 MODRET retval;
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,
1049 rnto_fill_event);
1051 free(src_path);
1052 src_path = NULL;
1054 return retval;
1057 /* It can happen, that RNTO command fails, but the destination path exists.
1058 * Therefore make an attempt to resolve its realpath before doing
1059 * the audit log.
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;
1067 return NULL;
1070 MODRET solaris_audit_post_rnto_err(cmd_rec *cmd) {
1071 MODRET retval;
1072 retval = __solaris_audit_post(cmd, "RNTO", ADT_FAILURE, ADT_FAILURE,
1073 rnto_err_fill_event);
1074 if (src_path != NULL) {
1075 free(src_path);
1076 src_path = NULL;
1078 return retval;
1080 /* } RNTO. */
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 },
1087 /* Delete file. */
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,
1091 FALSE, FALSE },
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,
1097 FALSE, FALSE },
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,
1103 FALSE, FALSE },
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,
1108 FALSE, FALSE },
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,
1114 FALSE, FALSE },
1116 /* Upload file. */
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,
1120 FALSE, FALSE },
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,
1125 FALSE, FALSE },
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,
1130 FALSE, FALSE },
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,
1136 FALSE, FALSE },
1138 /* Rename file. */
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,
1142 FALSE, FALSE },
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,
1147 FALSE, FALSE },
1149 { 0, NULL }
1152 module solaris_audit_module = {
1153 NULL, NULL, /* Always NULL */
1154 0x20, /* API Version 2.0 */
1155 "solaris_audit",
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 */