aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/audit/tools/aulast/aulast.c
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/audit/tools/aulast/aulast.c')
-rw-r--r--framework/src/audit/tools/aulast/aulast.c610
1 files changed, 610 insertions, 0 deletions
diff --git a/framework/src/audit/tools/aulast/aulast.c b/framework/src/audit/tools/aulast/aulast.c
new file mode 100644
index 00000000..00ae055a
--- /dev/null
+++ b/framework/src/audit/tools/aulast/aulast.c
@@ -0,0 +1,610 @@
+/*
+ * aulast.c - A last program based on audit logs
+ * Copyright (c) 2008-2009,2011 Red Hat Inc., Durham, North Carolina.
+ * All Rights Reserved.
+ *
+ * This software may be freely redistributed and/or modified under the
+ * terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2, 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; see the file COPYING. If not, write to the
+ * Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors:
+ * Steve Grubb <sgrubb@redhat.com>
+ */
+
+#include "config.h"
+#include <stdio.h>
+#include <locale.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include "libaudit.h"
+#include "auparse.h"
+#include "aulast-llist.h"
+
+
+static llist l;
+static FILE *f = NULL;
+static char *kernel = NULL;
+
+/* command line params */
+static int cuid = -1, bad = 0, proof = 0, debug = 0;
+static char *cterm = NULL;
+
+void usage(void)
+{
+ fprintf(stderr,
+ "usage: aulast [--stdin] [--proof] [--extract] [-f file] [user name] [tty]\n");
+}
+
+/* This outputs a line of text reporting the login/out times */
+static void report_session(lnode* cur)
+{
+ int notime = 0;
+
+ // Don't list failed logins
+ if (cur == NULL)
+ return;
+
+ if (cur->result != bad)
+ return;
+
+ if (cur->name) {
+ // This is a reboot record
+ printf("%-8.8s ", cur->name);
+ if (cur->end == 0) {
+ cur->end = time(NULL);
+ notime = 1;
+ }
+ } else {
+ struct passwd *p = getpwuid(cur->auid);
+ if (p)
+ printf("%-8.8s ", p->pw_name);
+ else
+ printf("%-8.u ", cur->auid);
+ }
+ if (strncmp("/dev/", cur->term, 5) == 0)
+ printf("%-12.12s ", cur->term+5);
+ else
+ printf("%-12.12s ", cur->term);
+ printf("%-16.16s ", cur->host ? cur->host : "?");
+ printf("%-16.16s ", ctime(&cur->start));
+ switch(cur->status)
+ {
+ case SESSION_START:
+ printf(" still logged in\n");
+ break;
+ case DOWN:
+ printf("- down\n");
+ break;
+ case CRASH:
+ printf("- crash\n");
+ break;
+ case GONE:
+ printf(" gone - no logout\n");
+ break;
+ case LOG_OUT: {
+ time_t secs;
+ int mins, hours, days;
+ if (notime)
+ printf("- %-7.5s", " ");
+ else
+ printf("- %-7.5s", ctime(&cur->end) + 11);
+ secs = cur->end - cur->start;
+ mins = (secs / 60) % 60;
+ hours = (secs / 3600) % 24;
+ days = secs / 86400;
+ if (days)
+ printf("(%d+%02d:%02d)\n", days, hours, mins);
+ else
+ printf("(%02d:%02d)\n", hours, mins);
+ }
+ break;
+ default:
+ printf("\n");
+ break;
+ }
+ if (proof) {
+ char start[32], end[32];
+ struct tm *btm;
+
+ if (cur->loginuid_proof == 0 && cur->result == 1) // Bad login
+ printf(" audit event proof serial number:"
+ " %lu\n", cur->user_login_proof);
+ else
+ printf(" audit event proof serial numbers:"
+ " %lu, %lu, %lu\n", cur->loginuid_proof,
+ cur->user_login_proof, cur->user_end_proof);
+ printf(" Session data can be found with this search:\n");
+ btm = localtime(&cur->start);
+ strftime(start, sizeof(start), "%x %T", btm);
+ if (cur->end != 0) {
+ btm = localtime(&cur->end);
+ strftime(end, sizeof(end), "%x %T", btm);
+ printf(" ausearch --start %s --end %s",
+ start, end);
+ } else {
+ printf(" ausearch --start %s", start);
+ }
+ if (cur->name == NULL)
+ printf(" --session %d", cur->session);
+ if (cur->loginuid_proof == 0 && cur->result == 1) // Bad login
+ printf(" -a %lu", cur->user_login_proof);
+ printf("\n\n");
+ }
+}
+
+static void extract_record(auparse_state_t *au)
+{
+ if (f == NULL)
+ return;
+
+ fprintf(f, "%s\n", auparse_get_record_text(au));
+}
+
+static void create_new_session(auparse_state_t *au)
+{
+ const char *tpid, *tses, *tauid;
+ int pid = -1, auid = -1, ses = -1;
+ lnode *cur;
+
+ // Get pid
+ tpid = auparse_find_field(au, "pid");
+ if (tpid)
+ pid = auparse_get_field_int(au);
+
+ // Get second auid field
+ tauid = auparse_find_field(au, "old-auid");
+ if (tauid)
+ tauid = auparse_find_field(au, "auid");
+ else { // kernel 3.13 or older
+ auparse_first_record(au);
+ auparse_find_field(au, "auid");
+ auparse_next_field(au);
+ tauid = auparse_find_field(au, "auid");
+ }
+ if (tauid)
+ auid = auparse_get_field_int(au);
+
+ // Get second ses field
+ tses = auparse_find_field(au, "old-ses");
+ if (tses)
+ tses = auparse_find_field(au, "ses");
+ else { // kernel 3.13 or older
+ auparse_first_record(au);
+ auparse_find_field(au, "ses");
+ auparse_next_field(au);
+ tses = auparse_find_field(au, "ses");
+ }
+ if (tses)
+ ses = auparse_get_field_int(au);
+
+ // Check that they are valid
+ if (pid == -1 || auid ==-1 || ses == -1) {
+ if (debug)
+ fprintf(stderr, "Bad login for event: %lu\n",
+ auparse_get_serial(au));
+ return;
+ }
+
+ // See if this session is already open
+ //cur = list_find_auid(&l, auid, pid, ses);
+ cur = list_find_session(&l, ses);
+ if (cur) {
+ // This means we have an open session close it out
+ cur->status = GONE;
+ cur->end = auparse_get_time(au);
+ report_session(cur);
+ list_delete_cur(&l);
+ }
+
+ // If this is supposed to be limited to a specific
+ // uid and we don't have that record, skip creating it
+ if (cuid != -1 && cuid != auid) {
+ if (debug)
+ fprintf(stderr,
+ "login reporting limited to %d for event: %lu\n",
+ cuid, auparse_get_serial(au));
+ return;
+ }
+
+ list_create_session(&l, auid, pid, ses, auparse_get_serial(au));
+}
+
+static void update_session_login(auparse_state_t *au)
+{
+ const char *tpid, *tses, *tuid, *tacct=NULL, *host, *term, *tres;
+ int pid = -1, uid = -1, ses = -1, result = -1;
+ time_t start;
+ lnode *cur;
+
+ // Get pid
+ tpid = auparse_find_field(au, "pid");
+ if (tpid)
+ pid = auparse_get_field_int(au);
+
+ // Get ses field - skipping first uid
+ tses = auparse_find_field(au, "ses");
+ if (tses)
+ ses = auparse_get_field_int(au);
+
+ // Get second uid field - we should be positioned past the first one
+ // gdm sends uid, everything else sends id, we try acct as last resort
+ tuid = auparse_find_field(au, "uid");
+ if (tuid)
+ uid = auparse_get_field_int(au);
+ else {
+ auparse_first_record(au);
+ tuid = auparse_find_field(au, "id");
+ if (tuid)
+ uid = auparse_get_field_int(au);
+ else {
+ auparse_first_record(au);
+ tuid = auparse_find_field(au, "acct");
+ if (tuid) {
+ const char *tacct = auparse_interpret_field(au);
+ struct passwd *pw = getpwnam (tacct);
+ if (pw != NULL)
+ uid = pw->pw_uid;
+ } else
+ auparse_first_record(au);
+ }
+ }
+
+ start = auparse_get_time(au);
+
+ host = auparse_find_field(au, "hostname");
+ if (host && strcmp(host, "?") == 0)
+ host = auparse_find_field(au, "addr");
+
+ term = auparse_find_field(au, "terminal");
+ if (term == NULL)
+ term = "?";
+ tres = auparse_find_field(au, "res");
+ if (tres)
+ tres = auparse_interpret_field(au);
+ if (tres) {
+ if (strcmp(tres, "success") == 0)
+ result = 0;
+ else
+ result = 1;
+ }
+ // We only get tacct when its a bad login
+ if (result == 1) {
+ auparse_first_record(au);
+ tacct = auparse_find_field(au, "acct");
+ if (tacct)
+ tacct = auparse_interpret_field(au);
+ } else {
+ // Check that they are valid
+ if (pid == -1 || uid ==-1 || ses == -1) {
+ if (debug)
+ fprintf(stderr,
+ "Bad user login for event: %lu\n",
+ auparse_get_serial(au));
+ return;
+ }
+ }
+
+ // See if this session is already open
+ if (result == 0)
+ cur = list_find_auid(&l, uid, pid, ses);
+ else
+ cur = NULL;
+ if (cur) {
+ // If we are limited to a specific terminal and
+ // we find out the session is not associated with
+ // the terminal of interest, delete the current node
+ if (cterm && strstr(term, cterm) == NULL) {
+ list_delete_cur(&l);
+ if (debug)
+ fprintf(stderr,
+ "User login limited to %s for event: %lu\n",
+ cterm, auparse_get_serial(au));
+ return;
+ }
+
+ // This means we have an open session - update it
+ list_update_start(&l, start, host, term, result,
+ auparse_get_serial(au));
+
+ // If the results were failed, we can close it out
+ /* FIXME: result cannot be true. This is dead code.
+ if (result) {
+ report_session(cur);
+ list_delete_cur(&l);
+ } */
+ } else if (bad == 1 && result == 1) {
+ // If it were a bad login and we are wanting bad logins
+ // create the record and report it.
+ lnode n;
+
+ n.start = start;
+ n.end = start;
+ n.auid = uid;
+ n.name = tacct;
+ n.term = term;
+ n.host = host;
+ n.result = result;
+ n.status = LOG_OUT;
+ n.loginuid_proof = 0;
+ n.user_login_proof = auparse_get_serial(au);
+ n.user_end_proof = 0;
+ report_session(&n);
+ } else if (debug)
+ printf("Session not found or updated\n");
+}
+
+static void update_session_logout(auparse_state_t *au)
+{
+ const char *tses, *tauid, *tpid;
+ int pid = -1, auid = -1, ses = -1;
+ lnode *cur;
+
+ // Get pid field
+ tpid = auparse_find_field(au, "pid");
+ if (tpid)
+ pid = auparse_get_field_int(au);
+
+ // Get auid field
+ tauid = auparse_find_field(au, "auid");
+ if (tauid)
+ auid = auparse_get_field_int(au);
+
+ // Get ses field
+ tses = auparse_find_field(au, "ses");
+ if (tses)
+ ses = auparse_get_field_int(au);
+
+ // Check that they are valid
+ if (pid == -1 || auid ==-1 || ses == -1) {
+ if (debug)
+ fprintf(stderr, "Bad user logout for event: %lu\n",
+ auparse_get_serial(au));
+ return;
+ }
+
+ // See if this session is already open
+ cur = list_find_auid(&l, auid, pid, ses);
+ if (cur) {
+ // if time never got updated, this must be a cron or su
+ // session...so we will just delete it.
+ if (cur->start) {
+ // This means we have an open session close it out
+ time_t end = auparse_get_time(au);
+ list_update_logout(&l, end, auparse_get_serial(au));
+ report_session(cur);
+ } else if (debug)
+ fprintf(stderr, "start time error for event: %lu\n",
+ auparse_get_serial(au));
+ list_delete_cur(&l);
+ }
+}
+
+static void process_bootup(auparse_state_t *au)
+{
+ lnode *cur;
+ int start;
+
+ // See if we have unclosed boot up and make into CRASH record
+ list_first(&l);
+ cur = list_get_cur(&l);
+ while (cur) {
+ if (cur->name) {
+ cur->user_end_proof = auparse_get_serial(au);
+ cur->status = CRASH;
+ cur->end = auparse_get_time(au);
+ report_session(cur);
+ }
+ cur = list_next(&l);
+ }
+
+ // Logout and process anyone still left in the machine
+ list_first(&l);
+ cur = list_get_cur(&l);
+ while (cur) {
+ if (cur->status != CRASH) {
+ cur->user_end_proof = auparse_get_serial(au);
+ cur->status = DOWN;
+ cur->end = auparse_get_time(au);
+ report_session(cur);
+ }
+ cur = list_next(&l);
+ }
+
+ // Since this is a boot message, all old entries should be gone
+ list_clear(&l);
+ list_create(&l);
+
+ // make reboot record - user:reboot, tty:system boot, host: kernel
+ start = auparse_get_time(au);
+ list_create_session(&l, 0, 0, 0, auparse_get_serial(au));
+ cur = list_get_cur(&l);
+ cur->start = start;
+ cur->name = strdup("reboot");
+ cur->term = strdup("system boot");
+ if (kernel)
+ cur->host = strdup(kernel);
+ cur->result = 0;
+}
+
+static void process_kernel(auparse_state_t *au)
+{
+ const char *kernel_str = auparse_find_field(au, "kernel");
+ if (kernel_str == NULL)
+ return;
+
+ free(kernel);
+ kernel = strdup(kernel_str);
+}
+
+static void process_shutdown(auparse_state_t *au)
+{
+ lnode *cur;
+
+ // Find reboot record
+ list_first(&l);
+ cur = list_get_cur(&l);
+ while (cur) {
+ if (cur->name) {
+ // Found it - close it out and display it
+ time_t end = auparse_get_time(au);
+ list_update_logout(&l, end, auparse_get_serial(au));
+ report_session(cur);
+ list_delete_cur(&l);
+ return;
+ }
+ cur = list_next(&l);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ int i, use_stdin = 0;
+ char *user = NULL, *file = NULL;
+ struct passwd *p;
+ auparse_state_t *au;
+
+ setlocale (LC_ALL, "");
+ for (i=1; i<argc; i++) {
+ if (argv[i][0] != '-') {
+ //take input and lookup as if it were a user name
+ //if that fails assume its a tty
+ if (user == NULL) {
+ p = getpwnam(argv[i]);
+ if (p) {
+ cuid = p->pw_uid;
+ user = argv[i];
+ continue;
+ }
+ }
+ if (cterm == NULL) {
+ cterm = argv[i];
+ } else {
+ usage();
+ return 1;
+ }
+ } else {
+ if (strcmp(argv[i], "-f") == 0) {
+ if (use_stdin == 0) {
+ i++;
+ file = argv[i];
+ } else {
+ fprintf(stderr,"stdin already given\n");
+ return 1;
+ }
+ } else if (strcmp(argv[i], "--bad") == 0) {
+ bad = 1;
+ } else if (strcmp(argv[i], "--proof") == 0) {
+ proof = 1;
+ } else if (strcmp(argv[i], "--extract") == 0) {
+ f = fopen("aulast.log", "wt");
+ } else if (strcmp(argv[i], "--stdin") == 0) {
+ if (file == NULL)
+ use_stdin = 1;
+ else {
+ fprintf(stderr, "file already given\n");
+ return 1;
+ }
+ } else if (strcmp(argv[i], "--debug") == 0) {
+ debug = 1;
+ } else {
+ usage();
+ return 1;
+ }
+ }
+ }
+ list_create(&l);
+
+ // Search for successful user logins
+ if (file)
+ au = auparse_init(AUSOURCE_FILE, file);
+ else if (use_stdin)
+ au = auparse_init(AUSOURCE_FILE_POINTER, stdin);
+ else {
+ if (getuid()) {
+ fprintf(stderr, "You probably need to be root for this to work\n");
+ }
+ au = auparse_init(AUSOURCE_LOGS, NULL);
+ }
+ if (au == NULL) {
+ fprintf(stderr, "Error - %s\n", strerror(errno));
+ goto error_exit_1;
+ }
+
+ // The theory: iterate though events
+ // 1) when LOGIN is found, create a new session node
+ // 2) if that session number exists, close out the old one
+ // 3) when USER_LOGIN is found, update session node
+ // 4) When USER_END is found update session node and close it out
+ // 5) When BOOT record found make new record and check for previous
+ // 6) If previous boot found, set status to crash and logout everyone
+ // 7) When SHUTDOWN found, close out reboot record
+
+ while (auparse_next_event(au) > 0) {
+ // We will take advantage of the fact that all events
+ // of interest are one record long
+ int type = auparse_get_type(au);
+ if (type < 0)
+ continue;
+ switch (type)
+ {
+ case AUDIT_LOGIN:
+ create_new_session(au);
+ extract_record(au);
+ break;
+ case AUDIT_USER_LOGIN:
+ update_session_login(au);
+ extract_record(au);
+ break;
+ case AUDIT_USER_END:
+ update_session_logout(au);
+ extract_record(au);
+ break;
+ case AUDIT_SYSTEM_BOOT:
+ process_bootup(au);
+ extract_record(au);
+ break;
+ case AUDIT_SYSTEM_SHUTDOWN:
+ process_shutdown(au);
+ extract_record(au);
+ break;
+ case AUDIT_DAEMON_START:
+ process_kernel(au);
+ extract_record(au);
+ break;
+ }
+ }
+ auparse_destroy(au);
+
+ // Now output the leftovers
+ list_first(&l);
+ do {
+ lnode *cur = list_get_cur(&l);
+ report_session(cur);
+ } while (list_next(&l));
+
+ free(kernel);
+ list_clear(&l);
+ if (f)
+ fclose(f);
+ return 0;
+
+error_exit_1:
+ list_clear(&l);
+ if (f)
+ fclose(f);
+ return 1;
+}
+