LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 05-21-2012, 06:28 PM   #1
iffarrukh
LQ Newbie
 
Registered: May 2008
Posts: 18

Rep: Reputation: 0
script to detect root login


Hi All

I am looking for a script for Red Hat which can detect if root is logged in at console for more than 15min and if yes send an email and snmp alert like may be using "who" or "finger" .. and I can run this as cron to check after every 15 min.


Any ideas as I am not really expert in scripts.

Any help would be highly appreciated.

Regards
Sam
 
Old 05-21-2012, 10:21 PM   #2
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
You can use the w command (LC_ALL=C LANG=C w -lh) to see which users are logged in, but personally I hate trying to parse its fields in a script. (The login time field, in particular, can be either a date or a time..)

Here is a helper C program that makes this much easier.
Note: this is the updated version, with DURATION calculation fixed. The previous version would consider the input as days in all cases.
Code:
#include <stdio.h>
#include <utmp.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>

const char hexhi[256] =
    "0000000000000000111111111111111122222222222222223333333333333333"
    "4444444444444444555555555555555566666666666666667777777777777777"
    "88888888888888889999999999999999aaaaaaaaaaaaaaaabbbbbbbbbbbbbbbb"
    "ccccccccccccccccddddddddddddddddeeeeeeeeeeeeeeeeffffffffffffffff";

const char hexlo[256] =
    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
    "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";

const char *ip(const void *const address)
{
    static char buffer[40]; /* Only 40 bytes used */
    char       *p = buffer;

    if (((const uint64_t *)address)[1] || ((const uint32_t *)address)[1]) {
        const uint8_t *const ipv6 = (const uint8_t *)address;
        /* IPv6 address. */
        *(p++) = hexhi[ipv6[0]];
        *(p++) = hexlo[ipv6[0]];
        *(p++) = hexlo[ipv6[1]];
        *(p++) = hexlo[ipv6[1]];
        *(p++) = ':';
        *(p++) = hexhi[ipv6[2]];
        *(p++) = hexlo[ipv6[2]];
        *(p++) = hexlo[ipv6[3]];
        *(p++) = hexlo[ipv6[3]];
        *(p++) = ':';
        *(p++) = hexhi[ipv6[4]];
        *(p++) = hexlo[ipv6[4]];
        *(p++) = hexlo[ipv6[5]];
        *(p++) = hexlo[ipv6[5]];
        *(p++) = ':';
        *(p++) = hexhi[ipv6[6]];
        *(p++) = hexlo[ipv6[6]];
        *(p++) = hexlo[ipv6[7]];
        *(p++) = hexlo[ipv6[7]];
        *(p++) = ':';
        *(p++) = hexhi[ipv6[8]];
        *(p++) = hexlo[ipv6[8]];
        *(p++) = hexlo[ipv6[9]];
        *(p++) = hexlo[ipv6[9]];
        *(p++) = ':';
        *(p++) = hexhi[ipv6[10]];
        *(p++) = hexlo[ipv6[10]];
        *(p++) = hexlo[ipv6[11]];
        *(p++) = hexlo[ipv6[11]];
        *(p++) = ':';
        *(p++) = hexhi[ipv6[12]];
        *(p++) = hexlo[ipv6[12]];
        *(p++) = hexlo[ipv6[13]];
        *(p++) = hexlo[ipv6[13]];
        *(p++) = ':';
        *(p++) = hexhi[ipv6[14]];
        *(p++) = hexlo[ipv6[14]];
        *(p++) = hexlo[ipv6[15]];
        *(p++) = hexlo[ipv6[15]];
        *(p++) = '\0';

    } else {
        const uint8_t *const ipv4 = (const uint8_t *)address;

        if (ipv4[0] > 99) *(p++) = '0' + (ipv4[0] / 100);
        if (ipv4[0] > 9)  *(p++) = '0' + ((ipv4[0] / 10) % 10);
                          *(p++) = '0' + (ipv4[0] % 10);
                          *(p++) = '.';
        if (ipv4[1] > 99) *(p++) = '0' + (ipv4[1] / 100);
        if (ipv4[1] > 9)  *(p++) = '0' + ((ipv4[1] / 10) % 10);
                          *(p++) = '0' + (ipv4[1] % 10);
                          *(p++) = '.';
        if (ipv4[2] > 99) *(p++) = '0' + (ipv4[2] / 100);
        if (ipv4[2] > 9)  *(p++) = '0' + ((ipv4[2] / 10) % 10);
                          *(p++) = '0' + (ipv4[2] % 10);
                          *(p++) = '.';
        if (ipv4[3] > 99) *(p++) = '0' + (ipv4[3] / 100);
        if (ipv4[3] > 9)  *(p++) = '0' + ((ipv4[3] / 10) % 10);
                          *(p++) = '0' + (ipv4[3] % 10);
                          *(p++) = '\0';
    }

    return (const char *)buffer;
}

int main(int argc, char *argv[])
{
    struct timeval   now;
    double           duration = -1.0;
    int              arg;
    struct utmp     *u;

    /* Check command-line arguments. */    
    if (argc > 1) {
        if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
            fprintf(stderr, "\n"
                            "Usage: %s -h | --help\n"
                            "       %s [ DURATION [ USER(s)... ] ]\n"
                            "\n"
                            "This program lists all logged in users as\n"
                            "\tUSERNAME\tSECONDS\tTTY\tADDRESS\tHOST\n"
                            "using tab separators (\\t).\n"
                            "\n",
                            argv[0], argv[0]);
            return 0;
        }
    }

    /* Check the DURATION argument. */
    if (argc > 1) {
        double  value;
        char    multiplier = '\0';

        if (sscanf(argv[1], "%lf%c", &value, &multiplier) < 1) {
            fprintf(stderr, "%s: Invalid duration.\n", argv[1]);
            return 1;
        }
        if (value > 0.0) {
            switch (multiplier) {
            case '\t': case '\n': case '\v': case '\f': case '\r': case ' ':
            case 'S': case 's':
            case '\0':
                duration = value;
                break;
            case 'M': case 'm':
                duration = value * 60.0;
                break;
            case 'H': case 'h':
                duration = value * 60.0 * 60.0;
                break;
            case 'D': case 'd':
                duration = value * 60.0 * 60.0 * 24.0;
                break;
            default:
                fprintf(stderr, "%s: Unknown suffix '%c'.\n", argv[1], multiplier);
                return 1;
            }
        }
    }

    /* Get current time. */
    if (gettimeofday(&now, NULL) != 0)
        return 1;

    /* Scan the login records. */
    while (NULL != (u = getutent()))
        if (u->ut_type == USER_PROCESS) {
            const double seconds = (double)(now.tv_sec - u->ut_tv.tv_sec)
                                 + (double)(now.tv_usec - u->ut_tv.tv_usec) / 1000000.0;

            /* Not logged in long enough? */
            if (duration >= 0.0 && seconds < duration)
                continue;

            /* Not one of the listed users? */
            if (argc > 2) {
                /* Find a match. */
                for (arg = 2; arg < argc; arg++)
                    if (!strcmp(u->ut_user, argv[arg]))
                        break;
                /* No match? */
                if (arg >= argc)
                    continue;
            }

            printf("%s\t%.0f\t%s\t%s\t%s\n", u->ut_user, seconds, u->ut_line,
                                             ip(&u->ut_addr_v6), u->ut_host);
        }

    endutent();
    return 0;
}
If you save it as w2.c, you can compile and install it using
Code:
gcc w2.c -W -Wall -O3 -o w2
strip --strip-unneeded w2
sudo install -m 0755 /usr/local/bin/w2 w2
and you can trigger something like the following script from your crontab:
Code:
#!/bin/bash
logins="$(w2 15m root)"
if [ -n "$logins" ]; then
    # Yes, root has been logged for at least 15 minutes.

    # Send e-mail to dummy@example.com
    ( echo ""
      echo "Root has been logged in for over 15 minutes:"
      printf 'user\tseconds\ttty\tfrom-ip-address\tfrom-host\n'
      echo "$logins"
    ) | mailx -s "Root logged in too long" dummy@example.com
fi
Remember, however, that if you leave a console open for the weekend, you'll get something like two hundred emails.

Last edited by Nominal Animal; 05-22-2012 at 02:50 PM. Reason: Fixed the duration calculation!
 
Old 05-21-2012, 11:51 PM   #3
iffarrukh
LQ Newbie
 
Registered: May 2008
Posts: 18

Original Poster
Rep: Reputation: 0
Hi

Thanks for your scripts but seems its not really working.. If I jsut execute w2 binary it gives me some result but in scripts with arguments does not seems to be working. Even if I put it in a variable on shell and echo that I dont get anything..

Am I missing something.


[root@rhel6-64-soe bin]# w2
root 1236 pts/0 192.168.28.7 192.168.28.7
[root@rhel6-64-soe bin]# test=$(w2 20s root)
[root@rhel6-64-soe bin]# echo $test

[root@rhel6-64-soe bin]# w
14:50:17 up 24 days, 23:30, 1 user, load average: 0.00, 0.00, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root pts/0 192.168.28.7 14:28 0.00s 0.08s 0.02s w
You have new mail in /var/spool/mail/root


Regards
Sam
 
Old 05-22-2012, 12:44 AM   #4
iffarrukh
LQ Newbie
 
Registered: May 2008
Posts: 18

Original Poster
Rep: Reputation: 0
Also I am looking for generic bash code like using may be "last" as I need to run the same scripts on HP-UNIX as well and on production system I cannot install complier..



Regards
Sam
 
Old 05-22-2012, 01:08 AM   #5
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,842

Rep: Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308Reputation: 7308
what about the command last | grep 'root.*console' ?
 
Old 05-22-2012, 03:26 PM   #6
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by iffarrukh View Post
Thanks for your scripts but seems its not really working..
It was a bug in the duration calculation, now fixed. It understood the duration as fractional days, regardless of what suffix you gave it.

Quote:
Originally Posted by iffarrukh View Post
Also I am looking for generic bash code like using may be "last" as I need to run the same scripts on HP-UNIX as well and on production system I cannot install complier..
In that case, I'd use something like the following. Note that you may need to use last -a on the HP-UNIX; the -i option just turns the hostname into an IP address, which is safer to log since names may change.
Code:
#!/bin/bash

# Make sure we use the default POSIX locale (English).
export LC_ALL=C LANG=C

# Current time.
nowparts=($( date '+%a %b %d %H:%M' ))
nowwkday="${nowparts[0]}"
nowmonth="${nowparts[1]}"
nowday="${nowparts[2]}"
nowtime="${nowparts[3]}"

# Assume no action.
action=""

# Parse 'last' output.
while read user tty wkday month day time still logged in from ; do
    # Still logged in?
    [ "$still" = "still" ] || continue
    [ "$logged" = "logged" ] || continue
    [ "$in" = "in" ] || continue

    # $wkday is Sun Mon Tue Wed Thu Fri Sat
    # $month is Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
    # $day is 1 to 31
    # $now... have the same values for this moment in time.

    #
    # Todo: Compare the timestamps to see if there is at least
    #       15 minutes in between, and if so, set 'action' variable.
    #

done < <( last -ai root )

if [ -n "$action" ]; then

    #
    # root has been logged in for at least 15 minutes.
    #

fi
But, like I said, parsing those date and time values to see if there is at least DURATION minutes in between, is quite annoying. It is much easier if you can use GNU awk, but do you have that installed on HP-UNIX? Run gawk --version to check.
 
Old 05-22-2012, 11:39 PM   #7
iffarrukh
LQ Newbie
 
Registered: May 2008
Posts: 18

Original Poster
Rep: Reputation: 0
Hi


Yes awk is installed on HP-UNIX..


Thanks for your responses.. Really much appreciated.


Regards
Farrukh
 
Old 05-23-2012, 12:12 PM   #8
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by iffarrukh View Post
Yes awk is installed on HP-UNIX..
Standard awk does not have useful date/time manipulation functions, but GNU awk (gawk) does, as extensions. Can you rely on always having gawk available?
 
Old 05-23-2012, 05:56 PM   #9
schneidz
LQ Guru
 
Registered: May 2005
Location: boston, usa
Distribution: fedora-35
Posts: 5,313

Rep: Reputation: 918Reputation: 918Reputation: 918Reputation: 918Reputation: 918Reputation: 918Reputation: 918Reputation: 918
would something like this help:
Code:
ps auxw | grep root.*bash
 
Old 05-23-2012, 11:30 PM   #10
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by schneidz View Post
would something like this help:
Code:
ps auxw | grep root.*bash
It is not only likely to produce false positives (like scripts running under cron, for example), but it would miss any non-Bash sells (say if root ran exec zsh for example), but it also suffers from the exact same date/time parsing problem.

The TIME column is the accumulated CPU time, and will not grow if root just forgot to log out, and left the connection open.
______________________________________________________

GNU awk has support for mktime() which makes it possible to determine the number of seconds between two timestamps without horrendous amounts of code. With it, you only need to convert the month to a number (01 to 12).
______________________________________________________

Even better would be if you had date from GNU coreutils available. You could then use SECONDS=$(( $(date -d "later" +%s) - $(date -d "earlier" +%s) )) to calculate the number of seconds between the two timestamps. It supports quite freehand timestamps, stuff like 'now' and 'yesterday', and most inportantly, the wonky format these utilities use. Both May24 and 07:13 are be handled correctly, and give the expected result.

In this particular use case, SECONDS=$(( $(date +%s) - $(date -d "timestamp" +%s) )) would be perfect to parse the number of seconds since timestamp (i.e. login), but I do not know of any other date variants than GNU date that support +%s, -d May24, and -d 07:13.

@iffarrukh: Could you check if these work for you? In HP-UNIX?
Code:
date -d May23 +%s
date -d 00:00 +%s
date +%s
If the output is just three numbers in ascending order, and no errors, then the following should work for you:
Code:
#!/bin/bash

# Make sure we use the default POSIX locale (English).
export LC_ALL=C LANG=C

# Current time.
nowparts=($( date '+%a %b %d %H:%M' ))
nowwkday="${nowparts[0]}"
nowmonth="${nowparts[1]}"
nowday="${nowparts[2]}"
nowtime="${nowparts[3]}"

# Maximum login time in seconds by root.
loginsecs=0

# Parse 'last' output.
while read user tty wkday month day time still logged in from ; do

    # Still logged in?
    [ "$still" = "still" ] || continue
    [ "$logged" = "logged" ] || continue
    [ "$in" = "in" ] || continue

    # Number of seconds logged in
    seconds=$(( $(date +%s) - $(date -d "$month $day $time" +%s) ))

    # Update loginsecs, if seconds is larger.
    [ $seconds -gt $loginsecs ] && loginsecs=$seconds

done < <( last -ai root )

# At least 15 minutes logged in? (15 min = 900 seconds)
if [ $loginsecs -ge 900 ]; then

    #
    # root has been logged in for at least 15 minutes.
    #

fi
 
Old 05-24-2012, 01:12 AM   #11
iffarrukh
LQ Newbie
 
Registered: May 2008
Posts: 18

Original Poster
Rep: Reputation: 0
Hi Nominal


Thanks again for your help and support.

On HP-UNIX -d parameter is not available and on RedHat I get this



[root@edc-03-desktop-demo rhn]# date -d May23 +%s
1337695200
[root@edc-03-desktop-demo rhn]# date -d 00:00 +%s
1337781600
[root@edc-03-desktop-demo rhn]# date +%s
1337839870
[root@edc-03-desktop-demo rhn]#
 
Old 05-24-2012, 02:07 AM   #12
iffarrukh
LQ Newbie
 
Registered: May 2008
Posts: 18

Original Poster
Rep: Reputation: 0
Btw how you figured out the variables name from last command..

i mean "user tty wkday month day time still logged"

What's variable name for getting IP address as well


Regards
Sam
 
Old 05-24-2012, 03:35 AM   #13
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by iffarrukh View Post
On HP-UNIX -d parameter is not available
Just as I thought, you cannot rely on date on HP-UNIX.

Would it be acceptable if the check would be skipped for the duration after the midnight?

Assuming you use 15 minutes as the limit, it would mean that from midnight to 15 minutes past, the check would be skipped, and no e-mail or warning sent at all, in any case. It would also mean that if the root logged in 15 minutes before the midnight, and stayed logged in for at most 30 minutes, it would not be detected by this script. Logging at 15 minutes before the midnight, and staying logged in for 31 minutes or more, would be detected, however. The practical effect is that around midnight, the allowed root login duration would be temporarily doubled.

Consider the following:
Code:
#!/bin/bash

# Use POSIX locale explicitly.
export LANG=C
export LC_ALL=C

# Maximum age of root logins, after which action is taken.
MAXMINUTES=15

# Current date and time. Get a consistent result,
now=($(date '+%b %d %H %M'))

# and then split the fields.
currmonth="${now[0]}"        # abbreviation (locale!)
currday="${now[1]}"          # day number
currhour="1${now[2]}"        # hour + 100
currmin="1${now[3]}"         # minute + 100

# Current time in minutes since midnight:
currminutes=$(( currhour * 60 + currmin - 6100 ))

# No action is taken between midnight and MAXMINUTES past.
(( currminutes <= MAXMINUTES )) && exit 0
The above will simply exit (with success exit status) if run between midnight and MAXMINUTES past midnight.

Since hours and minutes will always have two digits, and a leading zero would cause the value to be interpreted as octal (making 08 and 09 fail!), I add a leading zero. For example, at 08:09, date will output '108*60+109', and the left side of the arithmetic expression will evaluate to 489, the number of minutes since midnight at 08:09.

At this point, you know that if root logged in on a different day (day number or month name differs), then it has been logged on for longer than MAXMINUTES minutes. Only if the date matches, is there any need to check. To check the number of minutes during the same day, we only need to manipulate the timestamp a bit.

So, let's continue:
Code:
# List of users who have been logged in for too long.
users=""

# List of users we consider. Use empty list for all users.
userlist="root"

# Parse 'last' output:
while read user tty wkday month day time still logged in from ; do
    # Still logged in?
    [ "$still" = "still" ] || continue
    [ "$logged" = "logged" ] || continue
    [ "$in" = "in" ] || continue

    # Different month? Different day?
    if [ "$month" != "$currmonth" ] || [ "$day" != "$currday" ]; then
        # Logged in for longer than allowed.
        users="$users $user"
        continue
    fi

    # Calculate the time after midnight in minutes.
    timepart=(${time//[:.]/ })
    hour="1${timepart[0]}"
    min="1${timepart[1]}"
    minutes=$(( currminutes - hour * 60 - min + 6100 ))

    # Logged in for too long?
    if (( minutes > MAXMINUTES )); then
        users="$users $user"
        continue
    fi
done < <( last -ai $userlist 2>/dev/null || last -a $userlist 2>/dev/null)

users="${users## }"

if [ -n "$users" ]; then

    #
    # $users have been logged in for too long.
    #

    echo "Logged for too long: $users"

fi
This one has a few additional tricks. If last -ai fails, it automatically tries last -a instead.

Just please remember that like I initially stated, this does not apply the MAXMINUTES limit consistently: before and after midnight, MAXMINUTES is effectively doubled. Whether that is acceptable to you I cannot say. It is certainly possible to calculate the number of minutes between two dates and times, but in my opinion, your HP-UNIX environment is too restricted to do it, unless you switch to Perl or some other full-featured programming language. Personally, I do not like to be confined to proprietary environments like your HP-UNIX, so I'm afraid I won't be of much help you with that. (To me, it's like being told that hammers are not allowed, use this frozen banana instead.)

Hope this helps,
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
sh script to auto root login MCLASS Programming 2 04-27-2012 09:51 AM
run script at login that requires root privileges Eredeath Programming 13 08-04-2010 10:44 PM
How to run a script as root upon login zugvogel Linux - Newbie 7 09-09-2005 11:10 AM
How is it possible to login as root with Perl script and used telnet.pm manu0573 Linux - Software 1 07-27-2005 05:22 PM
runing login script with root privilleges djgerbavore Linux - Networking 8 06-16-2005 02:36 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:26 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration