Showing posts with label last. Show all posts
Showing posts with label last. Show all posts

Monday, May 4, 2009

Getting The Year From Wtmpx/Wtmp - Part 2 - The Easy Way

Hey there,

Today's subject matter hearkens back to a post we did in 2007 about looking around inside Solaris' wtmpx. In that post, the ultimate objective was to get the "year" information, since commands like "last" don't include that information in their output and the lack of that year information can make for confusing output if you don't roll your wtmpx file every year.

Once I stumbled over this command (which is one thing that always amazes me, since I've been using Solaris since 2.4 - there's always something new to learn, even if it's old ;) I was extremely pleased. Of course, knowing how to crack open wtmpx and get at anything and everything inside it is probably a more powerful skill to possess, but, if you're a simplest-is-best kind of guy like me, using a command that will allow you to get the last output with the year attached is perfect if you're looking to get your last output with the year attached. Seems almost self-evident ;)

The command you can use is called "fwtmp" and exists in the "/usr/lib/acct" directory on Solaris and in many and varying places depending on your Linux distro (assuming your db is updated, commands like locate or slocate should make it easy to find). The command has actually been around since Solaris 7, but (yes, I did some research on it when I realized I'd never realized it existed before ;) had some issues that made it unacceptable. Those same issues persisted through most of Solaris 8, but were eventually ironed out by the time Solaris 9 came out (which is also like saying "By the time you had your Solaris 8 system patched to the gills," since a fully patched 64-bit installation of Solaris 8 was, basically, Solaris 9). The command still stands in good stead on Solaris 10 and is working well on every Linux distro I've been able to test.

Even better still, the command's application is extremely simple; allowing you to use its output for whatever diabolical purposes you have in mind (I believe it supports altruism as well ;) Many of the wtmp/btmp implementations on Linux already have a way around this by allowing you to specify time frames (with the -t flag) when you invoke last. Most distro's also include this command (and its fellow commands) by default, as well. This old post on getting the year from last on Linux also demonstrates how Linux makes this whole process less convoluted by allowing you to get the same information from the lastlog file.

For a quick example of how easy this command is to use, check out the following command line session. First we create a temporary file using fwtmp and then we read a line from it to verify that it worked correctly (it doesn't get much simpler than that... unless they just start including the year in last's output ;) :

host # /usr/lib/acct/fwtmp < /var/adm/wtmpx > tmp_wtmpx_file
host # head -1 tmp_wtmpx_file
user123 sshd 1258 7 0000 0000 1226072918 230489 0 29 host123.desktop.ourhost.com Fri Nov 7 09:48:38 2008


Simplicity at its finest. There's actually a whole separate binary to fix wtmpx, which basically takes care of cleaning operations you could do yourself.

As a for instance, assuming you had the privileges to do so, and wanted to remove that login listed above from the wtmpx file, you could run that initial command to create the tmp_wtmpx_file, delete that line in it (using vi, emacs, what-have-you) and then convert the text file you got from fwtmp back into a binary wtmpx file, using the same command (The -ic options instruct fwtmp that its input is in ASCII format and needs to be converted to binary):

host # /usr/lib/acct/fwtmp -ic tmp_wtmpx_file > /var/adm/wtmpx


Of course, this is a simplistic look at how to do something like that (and certainly not a recommendation to mess with your security logs). You should probably back up files before overwriting them, etc. Still, it's kind of cool to know you can get all this information from wtmpx without having to learn the Perl pack signature (or know anything about C :)

The programming possibilities are only limited to what you need to do and the information fwtmp can provide!

Cheers!

, Mike




Discover the Free Ebook that shows you how to make 100% commissions on ClickBank!



Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Wednesday, December 24, 2008

Taking Full Advantage Of "Who" On Solaris

Hey there,

First things first: Happy Christmas Eve :) Our apologies to our readers who celebrate different festivities this time of year. It's not that we don't want you to enjoy them (although our wishes are probably belated for some), just that we almost forgot about our own... What does that say about us? We don't know either ;) In the spirit of "it's never too late" - Happy Holidays (whatever they may be) from all of us :)

This post is more of an expansion, than a follow-up, to our May 2008 post on using who to find out what and when on Linux and Unix (picking only the options that appeared in most distro's version of "who"). It's also a bit more restrictive than our previous post, as we're restricting the discussion to newer Solaris' (8/9/10) implementation of the classic command (Okay, "according to Hoyle" (and I'm using that idiom loosely, since this post has nothing to do with the game of Whist ;), it can't be a classic for another 12 years, but if TNT can televise "new classics" that just came out last year, the meaning of the word has either been completely devalued or we're making liberal use of artistic license. We prefer the latter justification ;)

Solaris' implementation of who is a great deal better than in older versions, and turns what used to be a "mostly" informational command into a command that you can use to really get to the bottom of things. Following, a list of Solaris who's usage parameters and a brief discussion of each. One thing that should be noted is that the Solaris who command has no "help" switch (-h, --help, nothing), and it will produce output if you just run it without arguments. We usually go with "who -h" (since -h isn't an option) to get usage/help output. You'll have to subject yourself to the humiliating error message, but it's generally worth it ;)

Ladies and gentleman, The Options :)

1. Who straight-up (what better way to start a list of options than with the no-option option? This is what you get if you settle. Four columns: User ID (NAME), terminal (LINE), date (TIME) and local-screen/IP-address:

host # who
user001 console Dec 8 09:48 (:0)
user001 pts/3 Dec 8 09:50 (:0.0)
user001 pts/5 Dec 23 08:56 (10.99.99.99)


2. -a. This option is fairly obvious. It stands for "all" and is the equivalent to using "-Abdlprtu" which, the astute among you may have noted, does not comprise the entirety of the options available ;) We'll go over all the options that are lumped together in this option shortly (If I had more time to proofread, I wouldn't have made the word option a tag for this post ;).

host # who -a
. system boot Dec 8 09:46
. run-level 3 Dec 8 09:47 3 0 S
zsmon . Dec 8 09:47 old 307
LOGIN console Dec 8 09:47 old 331
user001 + console Dec 8 09:48 old 663 (:0)
user001 + pts/3 Dec 8 09:50 old 1015 (:0.0)
user001 pts/4 Dec 8 10:37 old 0 id= /4 term=0 exit=0
user001 + pts/5 Dec 23 08:56 . 17914 (10.99.99.99)
user001 pts/4 Dec 11 16:15 old 28003 id=ts/4 term=0 exit=0
user001 pts/6 Dec 19 10:39 old 11406 id=ts/6 term=0 exit=0
user001 pts/7 Dec 18 16:40 old 9997 id=ts/7 term=0 exit=0
user001 pts/8 Dec 16 14:05 old 5804 id=ts/8 term=0 exit=0


3. -b. This option will show you the last time the system booted. This can be very helpful (especially when used in combination with "last -x" to make use of last's full potential):

host # who -b
. system boot Dec 8 09:46


4. -d. This option purports to list out "dead" processes. The reason we phrase it in that way is that, generally, these processes (or pseudo terminals) may have been used before, but this doesn't mean that they're hanging around like a bunch of zombie processes. For instance, the following output lists 5 "dead" pseudo terminals, although none of them can be found in the output of either "ps" or "lsof" (???) In any event, it's a cool feature :)

host # who -d
user001 pts/4 Dec 8 10:37
user001 pts/4 Dec 11 16:15
user001 pts/6 Dec 19 10:39
user001 pts/7 Dec 18 16:40
user001 pts/8 Dec 16 14:05


5. -H. This one is a fantastically fun joyride through the land of the obvious. It forces who to print out the header for each column it reports on (although it does forget about the display/IP column noted in straight-up "who" output from point 1):

host # who -H
NAME LINE TIME
user001 console Dec 8 09:48 (:0)
user001 pts/3 Dec 8 09:50 (:0.0)
user001 pts/5 Dec 23 08:56 (10.99.99.99)


6. -l. Using who with the -l option lists out only "login" processes. Basically, it will only report on logins that are logged in (or appear to be logged in) to the localhost directly (no external pseudo terminals):

host # who -l
zsmon . Dec 8 09:47 old 307
LOGIN console Dec 8 09:47 old 331


7. -q. This will perform a quick who (only showing the NAME field), and is the only option that the -n option works with. If you use -n with -q, you can specify the number of returned processes you want to see per line of output, at most. By default, who -q tries to return as many results as possible on a single line:

host # who -q
user001 user001 user001

who -q -n 2
user001 user001
user001


8. -r. This option will let you know what run level your system is currently at (again, check out our previous post on using who for more specifics on all of the output "who -r" produces:

host # who -r
. run-level 3 Dec 8 09:47 3 0 S


9. -s. This option is considered the "short form," since it doesn't report any "time since last login," session activity status or PID output. who, run with this option alone is actually the default output. The one time this comes in handy is when you're using it with -a (and -H for the headers, if you want), and want to trim that output a bit. Otherwise, using this option wouldn't make sense, since you'd have to specify the flags to print the two fields you want removed ;)

host # who -s
user001 console Dec 8 09:48 (:0)
user001 pts/3 Dec 8 09:50 (:0.0)
user001 pts/5 Dec 23 08:56 (10.99.99.99)

host # who -asH

NAME LINE TIME
. system boot Dec 8 09:46
. run-level 3 Dec 8 09:47 3 0 S
zsmon . Dec 8 09:47
LOGIN console Dec 8 09:47
user001 + console Dec 8 09:48 (:0)
user001 + pts/3 Dec 8 09:50 (:0.0)
user001 pts/4 Dec 8 10:37
user001 + pts/5 Dec 23 08:56 (10.99.99.99)
user001 pts/4 Dec 11 16:15
user001 pts/6 Dec 19 10:39
user001 pts/7 Dec 18 16:40
user001 pts/8 Dec 16 14:05


10. -t: This option will show you all the times that your system clock was reset (and, yes, sometimes this output can be empty, for reasons that require no explanation ;)

host # who -t
host #


11. -T. This flag shows your tty status (referred to also, above, as session activity status). The + symbol indicates that the tty's status is "writable," the - symbol indicates that the tty's status is "not writable" and the ? symbol indicates general confusion ;) It just means that the system has no idea what the tty's status is, which generally means that it's hung:

host # who -T
user001 + console Dec 8 09:48 old 663 (:0)
user001 + pts/3 Dec 8 09:50 old 1015 (:0.0)
user001 + pts/5 Dec 23 08:56 . 17914 (10.99.99.99)


12. -u. This flag lists out (and I'm quoting from the "usage" output) "useful information." That isn't to say that any other output you can get from who is completely useless. Although, the terminology does seem to cast a shadow... ;)

host # who -u
user001 console Dec 8 09:48 old 663 (:0)
user001 pts/3 Dec 8 09:50 old 1015 (:0.0)
user001 pts/5 Dec 23 08:56 . 17914 (10.99.99.99)


13. -m. This flag limits the information to the current terminal session only. As you can see below, we're logged in using pseudo tty /dev/pts/5:

host # who -m
user001 pts/5 Dec 23 08:56 (10.99.99.99)


14. And, to begin the wrap-up, Solaris' who makes up for the fact that it doesn't have a built in handler to deal with being called as "whoami" by providing two different options to get that same information. In an alarming show of disregard for proper capitalization, both of these versions work ;) Note that this output is almost always exactly the same as the output from "who -m":

host # who am i
user001 pts/5 Dec 23 08:56 (10.99.99.99)
host # who am I
user001 pts/5 Dec 23 08:56 (10.99.99.99)


follow that up with the question that, statistically, follows "who am I?" most often, give it a little bit of "Talking Heads" flavour, and you've got yourself a command that's completely useless ;)

host # my God, what have I done?
-bash: my: command not found


15. Back off the Road To Nowhere (David Byrne, again. Make him stop!! ;)... Lastly (no pun intended, as you'll understand by the end of this paragraph), you can use who, using any combination of options (with the exception of -n, which only works with -q), and follow it all up with a different utmpx file (if, for instance, your old one got to big and you copied it off somewhere). Straight up who on Solaris makes use of /var/adm/utmpx, but you can tell who to use any utmpx-like file (including wtmpx, which can make the "who" command emulate the "last" command to a basic degree):

host # who /var/adm/wtmpx
root console Nov 6 13:39
root console Nov 6 13:48 (:0)
root pts/3 Nov 6 13:51 (:0.0)
root pts/3 Nov 6 14:38 (:0.0)
root pts/4 Nov 6 14:39 (:0.0)
user001 sshd Nov 7 09:48 (host.subnet.domain.com)
user001 pts/4 Nov 7 09:48 (host.subnet.domain.com)
user001 sshd Nov 7 09:54 (host1.subnet2.domain3.com)
user001 pts/5 Nov 7 09:54 (host1.subnet2.domain3.com)
...


Here's hoping today's post help shed a bit more light on Solaris' who options than the standard usage screen does, pointed out a number of reasons it can be a great tool to have in your troubleshooting arsenal and (perhaps) taught you a trick or two :)

Cheers,

, Mike




Please note that this blog accepts comments via email only. See our Mission And Policy Statement for further details.

Friday, May 23, 2008

Using Who To Find What And When On Linux and Unix

Hello again,

Today's post is yet another in a somewhat disjointed series of posts on "stuff you might not know and you might find interesting" regarding very common commands. And they don't get much more common than the "who" command.

Generally, "who" is used like the last command that we looked at in our previous post. It's generally issued at the command line to determine who (yes, it's not just a clever name ;) is logged on "right now," if anyone is at all.

Unlike "last," however, the "who" command has quite a number of options that make it a great troubleshooting, and statistics gathering, command. And, as luck would have it, the four options that we're going to look at today are exactly the same on SUSE Linux 9.x, Solaris 9 Unix and even Solaris 10 :) We'll go through the options from most to least used (in my experience). Not that it matters. We're only looking at four options, so it's going to be hard to get lost ;) All example output will be from SUSE Linux 9.x

1. "who -r" - Prints the current runlevel. This is somewhat similar to the functionality of the last command that we posted about before, but it gives more limited information. This command is excellent for a quick overview of the system's current runlevel, previous state and last state-transition time. For instance, take the following example:

host # who -r
run-level 3 Feb 27 16:06 last=S


This shows us that our system is currently at "run level 3," was in "Single User" mode (S) previous to that, and that the transition from "Single User" to "run level 3" occurred approximately February 27th at 16:06. I say approximately, because (if we look at last's output, as we did in our previous post on using last to its full potential, we could see that this was actually a reboot).

The last state will usually appear as "S" on a reboot, since it's the last recorded state the system is at before it switches to "run level 3" (Of course run level 2 is executed on a normal boot to run level 3). All the information about switching from "run level 3" to "run level 6," and from "run level 6" to "run level S", and all the reboot and shutdown commands are not reported. Again, we don't know the year, but, since this command reads from wtmpx, you can check out a few older posts on user deletion with Perl and the relevant mods for Linux if you want to use Perl to grab that information, as well.

2. "who -b" - Prints the system boot time. Didn't I just get through a really long-winded explanation of all the information missing from "who -r"? ;) Well, here's some of that. This invocation of "who" prints out the last time the system was booted. Note that this doesn't differentiate between a reboot and a power-cycle:

host # who -b
system boot Feb 27 16:06


3. "who -d" - Prints out a list of all the dead processes on your system. This invocation of the who command is really only useful if you're looking for a problem process and can't seem to find it. Generally, you'd use either lsof or ptree/pfiles to find the rogue process, but, if you don't have those (or find them too messy), this command can sometimes help. Mostly though, it's just a listing of processes which are no longer running and still in memory. Note that, for our example below, all of these processes aren't even in the process table anymore!

host # who -d
Feb 27 16:06 2134 id=si term=0 exit=0
Feb 27 16:07 4410 id=l3 term=0 exit=0
pts/2 Apr 14 10:40 24532 id=ts/2 term=0 exit=0
pts/1 May 2 20:29 20407 id=ts/1 term=0 exit=0


4. "who -t" - Prints out the last time the System Clock was changed. Like I mentioned, I saved the least used, and/or obvious, invocation of who for last. You may never have to run the who command with this argument. Still, it's nice to know it's there. As far as I can tell, this setting is not affected by the NTP protocol or any similar software you might have running on your machine (xnptd, etc) to keep the OS clock set correctly. If someone with root (or equivalent) privilege decides to run the "date" command on the server to set an incorrect (or correct) time, this command's output will note it. Unfortunately, it's been a while on the machine I'm using as a test case, and the default output (assuming no change) is nothing. On the bright side, we can be reasonably certain that no one's been goofing with the system clock :)

host # who -t
host #


Enjoy the rest of your day, and have a great Memorial Day weekend ;)

Cheers,

, Mike

Thursday, May 22, 2008

Using Last To Its Full Potential On Linux

Hey There,

This probably comes as no surprise to most Unix or Linux administrators out there (at least this first thing), but I find it's always interesting how rarely the "last" command is used to determine anything other than the users logged in "now" and the "last" time a user logged in.

Granted; the last command doesn't offer too much in the extra-functionality department, but it does have one very useful feature. Normally, if you were to run last, you'd see output like the following:

reboot system boot 2.6.5-7.283-smp Thu Jan 25 18:06 (00:21)
user1 pts/1 host.xyz.com Thu Jan 25 08:03 - down (00:27)
reboot system boot 2.6.5-7.283-smp Thu Jan 25 08:01 (00:29)
user1 pts/1 host.xyz.com Thu Jan 25 07:50 - down (00:06)


But, if you add the "-x" switch to the "last" command, it gives you a lot more detailed information about system run-level changes, which makes it a more accurate way to determine what happened if, and when, your system ever goes down unexpectedly! Here's output from that same swatch of time using "last -x":

runlevel (to lvl 3) 2.6.5-7.283-smp Thu Jan 25 18:06 - 18:28 (00:21)
reboot system boot 2.6.5-7.283-smp Thu Jan 25 18:06 (00:21)
shutdown system down 2.6.5-7.283-smp Thu Jan 25 08:31 - 18:28 (09:56)
runlevel (to lvl 6) 2.6.5-7.283-smp Thu Jan 25 08:31 - 08:31 (00:00)
user1 pts/1 host.xyz.com Thu Jan 25 08:03 - down (00:27)
runlevel (to lvl 3) 2.6.5-7.283-smp Thu Jan 25 08:01 - 08:31 (00:29)
reboot system boot 2.6.5-7.283-smp Thu Jan 25 08:01 (00:29)
shutdown system down 2.6.5-7.283-smp Thu Jan 25 07:57 - 08:31 (00:33)
runlevel (to lvl 6) 2.6.5-7.283-smp Thu Jan 25 07:57 - 07:57 (00:00)
user1 pts/1 host.xyz.com Thu Jan 25 07:50 - down (00:06)


Interestingly enough, the "-x" flag still isn't available in Solaris, even in all the versions of the 10.x strain that I've checked out. There are other methods to get the information, but they are more tedious and require the user, or admin, to do enough work that they may as well script it out (or write a wrapper for "last" that allows for a "-x" flag ;)

Generally, you'll notice that this extra information is assigned to the "user" with the name of your "kernel" revision ( usually the value of "uname -r" or "uname -k." 2.6.5-7.286-smp, in our case) so you can run:

last -x|grep `uname -r`


to restrict your output to this system information and ignore all the user logins/logouts :)

While the information that "last -x" provides may seem extraneous and not generally worthwhile, I'd say that it's exactly the opposite. For instance, in our first, straight-up, last command, we only get the reboot time of (we'll take the last one) January 25th at 8:01 a.m. ( The year is 2008 since we're taking this from the top of the output).

Interestingly enough, again, last does not print the year, although you can get that information if you really want it. For more info on that, check out our previous posts on scripting out user deletion on Unix and the modifications for Linux, which both include Perl routines for tearing open wtmpx so you "can" get the "year" data if you want it :)

With "last -x," for that very same reboot, we know that the reboot command was issued by the system on January 25th at 8:01 a.m. (this helps put into perspective what last, without arguments, is "really" reporting. The "beginning" of the reboot process). We can then see that (and, just as a reminder, we're reading from the bottom of the output up!) the request to switch to "run level 6" (which is "reboot") was actually issued at 7:57 a.m.

The "shutdown" information on the next line is an all-encompassing time. It should always match the entire amount of time spent in all of the states we're looking at. It starts with the switch to "run level 6" at 7:57 a.m. and ends with the switch to "run level 3" (this system's default run level) at 8:31 a.m. Finally, after the "reboot" line, we see the switch to "run level 3" which happens from 8:01 a.m. (the time the "reboot" was called) until 8:31 a.m. (the time the system fully got back to "run level 3").

As you can see, just knowing the "reboot" time doesn't give a very accurate report of the time involved in the reboot, at a glance. We just know that it happened at 8:01 a.m. If we wanted more information, we might need to go look at system logs.

"last -x," however, makes it so that we can, just by reviewing that output, see that the reboot process actually began at 7:57 a.m. and didn't complete until 8:31 a.m. That may not be a long time for this machine (If it is, you'll know to look at the system logs, now :), but the length of time required for a normal reboot is very system-independent and, also, dependant on what sorts of scripts and programs are run on a controlled reboot, etc.

And that's the last I have to say about that ;)

Best wishes,

, Mike

Thursday, December 6, 2007

Taking a Look Inside Solaris' wtmpx

This is a Bonus Post, for those admins among us who aren't satisfied with the kind of information you can get from Solaris' standard commands that use /var/adm/wtmpx (like "last"). This is in line with the script we're also building up, in this post, to try and determine whether or not users have logged in within an arbitrary period of time and automatically lock up accounts and/or delete them completely based on that information.

I think this is still one post per day; so why am I calling it a Bonus Post? Partially because it was the first thing that came into my mind when I was trying to describe this blog-post and partially because the Perl scriplet that we're going to look at today is made up of constructs and functions that are what they are, and nothing more. I checked around and saw that there are variations on this script on other sites; mostly because there's only one un-convoluted way to do it (Even the argument to pack can't vary when tearing open the wtmpx structure(A32 A4 A32 l s s2 x2 l2 l x20 s A257 x). For that reason, I can only take credit for slapping together these predefined functions in a way that's slightly different than most. Some of it's in the coding and some of it's in what I'm trying to get out of the /var/adm/wtmpx file (nobody else seems to want it). This process is so rigid, I can no more take credit for it than I could take credit for inventing the "while loop" ;)

What we're looking for here is the "year." Yes, the "year." It's one of those things that I've never been able to figure out: Why was it left out of the "last" command? Great confusion can come of trying to pull auditing information and only knowing the months and dates people logged on. The "year" just seems like it should be included by default. I have no answer as to why it's not, but I do have a way for you to get it. Except for the initial command line, I've left out the error checking to save on space.

Note that the parts of the unpacked /var/adm/wtmpx are all the same, as well. We can change the names of the variables, but when you get right down to it, the file is composed of parts, and those parts are parts of a predefined structure. Note that in the below script, I'm looking for a user name and a year, like so:

$wtmpx_username holds the user name variable( I tried to name my variables in this script so that I would be reminded of what they represent later, when I've long since forgotten). Note that if we were coding this in "c" and accessing the actual utmp struct it would be a lot more work to make the variable names more easily memorable. The great thing about Perl is that it mimics the "c" behaviour but is actually only passing you an array of individual variables, so you can call them whatever you want.

$wtmpx_seconds holds the number of seconds since the epoch at which the login occurred. We pass that number to the Perl localtime() function, and it, in turn, can be parsed out to provide the "year!" If you want to read more about using the localtime() function in a script, check out this previous post.

That being said, you can find lots of different things inside /var/adm/wtmpx, like $wtmpx_line_device (usually the tty/pty), which can be compared against the string "system boot" to figure out every time your system has been rebooted. Of course, for the most part, the "last" command, and it's companions, get you all the information you really need. But it's nice to know you can get more specific data if you want it :)

Below; the Bonus Post Perl scriplet, and a promise to dive into understanding the pack and unpack functions in a future post!


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/usr/bin/perl

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#

if ( $#ARGV != 0 ) {
print "Usage: $0 accountName\n";
exit(1);

$account_name = $ARGV[0];
$recordsize = length(pack("A32 A4 A32 l s s2 x2 l2 l x20 s A257 x",( )));

open(WTMPX,"/var/adm/wtmpx");
while (read(WTMPX,$record,$recordsize)) {
($wtmpx_username,$wtmpx_userid,$wtmpx_line_device,$wtmpx_process_id,$wtmpx_type,$wtmpx_termination_status,$wtmpx_exit_status,$wtmpx_seconds,$wtmpx_useconds,$wtmpx_session,$wtmpx_syslen,$wtmpx_hostname) = unpack($template,$record);
if ($wtmpx_username eq "$account_name"){
print "$account_name ".scalar localtime($wtmpx_seconds)."\n";
}
}


, Mike





Monday, December 3, 2007

Finding User Accounts To Disable - Follow-Up Post

Hey there,

This is a follow up to a previous post where we're walking through the process of developing a decent script, from scratch; examining how to figure out what user accounts you could disable or delete (times vary, depending on your company's policy) on any particular box you have to administrate.

Today I've taken it a step farther. At this point the below script will check the modification time of all users listed in /etc/passwd and compare it against the current Unix time. If the mtime on the home directory is over 45 days and less than 90, we warn to disable (actual code not included) and if it's over 90 days, we warn to delete (same "no code" policy for that ;). If the time on the home directory is under 45 days, the account is consider active and OK.

Note that I found a few incorrect things and changed them, as I augmented this script; so this isn't just a bigger version of the original. First of all, it seems that the user's home directory is just as reliable as the existence of a history file. Also, it's much more likely to exist ;) Also, by verifying, I noticed that the mtime (Last Modification Time) was more reliable than the atime (Last Access Time) that we used before. That seems a bit counter-intuitive, but so far, it's proving out.

We've also added some extra functionality to remove users that we don't want to check. The first group are in a file we've called locked.txt, the second group are users without a valid shell and the final group are users who are in a particular group (like administrators who need accounts on all boxes, but might not login to any particular one for a long time).

Hope this script helps, at least, to point a few people in the right direction. This is a work in progress since it still contains several flaws, including no cross-referencing of possibly-available data that the system can provide with "last" and other such commands, as well as still having to deal with "touch."

Note that, on the lines ---> @locked_text = ###LOCKEDTEXT###;
and
open(LOCKEDTEXT, "###locked.txt");

the ### space-holders should be greater than and equal signs <>, in the first instance, and a < sign in the second, but the blog software interprets them as a unclosed and unallowed tags!

Have fun :)


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/perl

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
# switching to mtime 9 - from atime - 8
#
# We don't need to check for errors on the command line since
# we won't be accepting any arguments
#

if ( -f "moreinfo") {
print "Removing old temporary file \"moreinfo\"\n";
unlink("moreinfo");
}

$today=time();

@no_shells=`egrep -i 'nologin|false' /etc/passwd|awk -F":" '{print \$1}`;
@in_unixtech=`grep 1621 /etc/passwd|awk -F":" '{print \$1}`;
@user_names=`awk -F":" '{print \$1}' /etc/passwd`;
open(LOCKEDTEXT, "###locked.txt");

@locked_text = ###LOCKEDTEXT###;
close(LOCKEDTEST);
print "\nRemoving LOCKED ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@locked_text ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n";
print "\nRemoving NO_SHELL ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@no_shells ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n";
print "\nRemoving UNIXTECH ACCOUNTS ";
foreach $locked_user (@user_names) {
print ".";
chomp($locked_user);
if ( grep /$locked_user/,@in_unixtech ) {
push(@tmp_user_names,$locked_user);
print "$locked_user ";
}
}
print "\n\n";
@total = @both = @unique_names = ();
%count = ();
foreach $possible_dupe (@user_names, @tmp_user_names) {
$count{$possible_dupe}++
}
foreach $possible_dupe (keys %count) {
push @total, $possible_dupe;
push @{ $count{$possible_dupe} > 1 ? \@both : \@unique_names }, $possible_dupe;
}
@user_names = @unique_names;
foreach $diruser (@user_names) {
$filer = `grep $diruser /etc/passwd|awk -F":" '{print \$6}'`;
push(@user_dirs,$filer);
}
print "FIRST RUN - Checking Home Directories...\n\n";
foreach $history_file (@user_dirs) {
chomp($history_file);
chomp($user_name = $user_names[$counter]);
print "${user_name}: ";
$counter++;
if ( -d "$history_file" ) {
@file_dates=stat("$history_file");
$file_date=$file_dates[9];
} else {
$file_date=0;
}
$difference=$today - $file_date;
if ( $difference >= 3888000 && $difference < 7776000 ) {
print "User needs to be disabled - ";
print "45 days old or older, but under 90\n";
# YOUR CUSTOM CODE HERE - "passwd -l" would probably suffice"
} elsif ( $difference >= 7776000 ) {
print "User needs to be deleted - ";
print "90 days old or older\n";
# YOUR CUSTOM CODE HERE -
} else {
print "OK\n";
}
}


, Mike





Saturday, December 1, 2007

Disabling and Removing Old User Accounts

Depending upon the size of your network, keeping track of user accounts on all of your Unix and Linux boxes can range from fairly simple to unbelievably hard to keep track of. I've noticed on the internet boards that lots of people are looking for the "one" way to semi-automate determining if user accounts need to be disabled, or if they're so old they need to be deleted.

I'm not a believer in the "one" solution theory. One of the things I love about Linux and Unix is, depending on how creative you're willing to be, there are probably 50 or more ways to do anything.

In any event I started looking at this problem, and noticed that almost everyone was using the "finger," "logins" or "last" commands to figure out how long it had been since someone logged in. In some extreme cases, I've even found examples where folks "pack" and "unpack" /var/adm/wtmpx with Perl to get the information. So, I naturally thought: Is it possible to do this without using the system's login-logging facilities?

The more I thought about this, the more I came to understand that both methods had their virtues and their faults. For instance, if /var/adm/wtmpx is missing, a lot of regular system accounting programs won't work. If you corrupt that file and remove /var/adm/lastlog (I'm using Solaris as my model - substitute file locations and names as necessary), "logins," "finger" and "last" don't report correctly unless they're reporting an error. The alternative approach also has its flaws. If, for instance, a user utilizes SSH to run a single command from a remote host using key-based passworless login, that shell won't be interactive and it won't update any shell history files!

Tackling this problem will take more than one post, so we'll start out with the core; treat this as a sort of tutorial on the phases that you go through from scratch to a decent script. Our assumption here will be that we have no access to any of the regular utilities like "last," "finger" and "logins." We'll use Perl to check out all users' .sh_history or .bash_history files (just covering sh, ksh and bash for now) and use the "stat" and "time" functions to determine how long it's been since the user last logged in.

Here's the core of the code, to get started. I've written it so it can be run on its own for now:


Creative Commons License


This work is licensed under a
Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License

#!/bin/perl

#
# 2007 - Mike Golvach - eggi@comcast.net
#
# Creative Commons Attribution-Noncommercial-Share Alike 3.0 United States License
#
#
# We don't need to check for errors on the command line since
# we won't be accepting any arguments
#

$today=time();

@user_dirs=`awk -F":" '{print \$6}' /etc/passwd`;
foreach $history_file (@user_dirs) {
chomp($history_file);
print "${history_file}: ";
if ( -f "$history_file/.sh_history" ) {
@file_dates=stat("$history_file/.sh_history");
$sh_file_date=$file_dates[8];
} else {
$sh_file_date=0;
}
if ( -f "$history_file/.bash_history" ) {
@file_dates=stat("$history_file/.sh_history");
$bash_file_date=$file_dates[8];
} else {
$bash_file_date=0;
}
if ( $sh_file_date > $bash_file_date ) {
$file_date = $sh_file_date;
} elsif ( $bash_file_date > $sh_file_date ) {
$file_date = $bash_file_date;
} else {
print "Cannot find .bash_history or .sh_history for $history_file. Moving on!\n";
next;
}
$difference=$today - $file_date;
if ( $difference >= 3888000 && $difference < 7776000 ) {
print "User needs to be disabled- ";
print "45 days old or older, but under 90\n";
# YOUR CUSTOM CODE HERE - "passwd -l" would probably suffice"
} elsif ( $difference >= 7776000 ) {
print "User needs to be deleted - ";
print "90 days old or older\n";
# YOUR CUSTOM CODE HERE -
} else {
print "OK\n";
}
}


Within the next post or two, we'll follow up on this, so that we can add functionality to check system files, using system utilities, to verify that our initial information is correct. It's possible that someone just "touch"ed a user's history file and that access time information isn't necessarily correct. Also, as we noted above, folks can be using the host remotely, in a non-interactive shell, and never alter the access time of their history file.

For the time being, this code should be a good start, and something to think about and improve upon. Until next time :)

, Mike