...making Linux just a little more fun! |
By Ben Okopnik |
The e-mail was short, succinct, and got right to the point.
Woomert - I'll be short, succinct, and get right to the point. Three-company merger. Nervous sysadmin. 3000+ users. /etc/passwd. UIDs. Regards, Frink Ooblick
Woomert Foonly, the Hard-Nosed Computer Detective, chuckled to himself. The client had been rather loud and incoherent on the phone, with "It doesn't work!" and "I need help!" being the chief features of his conversation. Woomert had sent Frink to the site to reconnoiter, and the above was the highly satisfactory result. All that remained was to come up with the solution; given that only a few short hours remained before the client shut down for the day, Woomert decided to use his time productively. Let's see - where was his favorite pillow?...
Refreshed and ready, Woomert appeared at the site, and immediately encountered a rather excited Frink.
- "Woomert, it's terrible! The file is far too long to search manually, and the UIDs are all over the map. The sysadmin is contrite, frantic, and panicked by turns, and his hair is almost all gone. What can we do?"
- "No worries, mate... oh, sorry. I was just in Canberra a few hours ago, and some of the influence is still with me. I can tell you from horrible experience that tomorrow will be even worse: I've got to be in Dallas in the morning, New York in the afternoon, and Tel Aviv in the evening. I would advise you to wear earplugs, or absent yourself from my environs until the accents fade. Ah, the perils of travel..."
Frink was becoming visibly upset.
- "Woomert - you're not taking this seriously. Can't you see that this is a major problem?"
- "Oh, this? Relax, take it easy. It's not nearly as bad as it looks, Frink; in fact..."
Woomert deftly extracted his favorite typing gloves from his pocket and slipped them on.
- "...Perl makes it rather trivial. What we'll do is give the sysadmin a couple of command-line tools that he can use to resolve this problem, and - since he's using 'bash' - he'll be able to pull them up with the 'up-arrow' key as he needs them. Here we go!"
A list of duplicate UIDs, along with their related usernames scrolled down the screen after Woomert pressed the "Enter" key. Both Woomert and Frink noted with interest that there was a triple entry for UID0 -
perl -F: -walne'$h{$F[2]}.="$F[0] ";END{$h{$_}=~/ ./&&print"$_: $h{$_}"for keys%h}' /etc/passwd
0: root sashroot kill3r
- "Well, well. Looks like somebody managed to break in and give themselves a UID0 (root) account. 'sashroot' is OK - that's the 'standalone shell' for those rough repair jobs - but 'kill3r'? Well, we'll let the client know; meanwhile, on with the current problem. The sysadmin will now have a list of all the duplicates - there don't seem to be all that many - but searching for the next available UID could be a pain. So, here's a second tool -"
- "That should give him a good start on getting it all straightened out. As for us - we're homeward bound!"
perl -wle'{getpwuid++$n&&redo;print$n}'
When they had returned to Woomert's house and were seated in front of the fireplace - the night had been a cold one, and the wind whistled outside the window - Frink looked expectantly at Woomert. Noting the look, Woomert laughed.
- "I know, I know. I should explain, shouldn't I? The air of mystery is a sharp, pleasant thing, but it is as nothing compared to the pleasure of learning. Here, let's start with the first one:
"First, take a look at the command-line switches I used:"
perl -F: -walne'$h{$F[2]}.="$F[0] ";END{$h{$_}=~/ ./&&print"$_: $h{$_}"for keys%h}' /etc/passwd
-w Enable warnings -a Autosplit (see "-F") -l Enable line-end processing -n Implicit non-printing loop -e Execute the following commands -F: Use ':' as the separator for the '-a' autosplit"If you remember our last adventure, all of the above except '-a' and '-F' are already familiar to you. Autosplitting splits the lines read in by '-n' or '-p', using whitespace as a default separator and saving the result in the '@F' array. '-F' optionally redefines the separator by which to split."
"Since we're reading in '/etc/passwd', let's look at the format of the individual lines in it:"
borg:x:1026:127:All your base are belong to us!:/home/borg:/bin/bash"There are seven standard fields, laid out as 'name - passwd - UID - GID - GECOS - dir - shell'. The only things we're interested in for the moment are name and UID; what I'm going to do is build a hash - a very important data structure in Perl, one of the three basic ones - that contains the UID (3rd field) as the key, and the name (1st field), followed by a space, as the value, for all the entries in '/etc/passwd':
$h{$F[2]}.="$F[0] "Since usernames can't have spaces in them, it makes a convenient separator. Once that's done, I'll loop over the hash and print out any value which contains a space followed by any character:"
$h{$_}=~/ ./&&print"$_: $h{$_}"for keys%h}"I see you still look puzzled. Here, let me write out the above in a more readable form:"
for ( keys %h ){
# Loop over the "%h" hash
if ( $h{$_} =~ / ./ ){
# Does the value contain a space followed by anything?
print "$_: $h{$_}\n";
# If so, print the UID, a colon, a space, and the value
}
}
"If you think about it, you'll see that the only thing that will match the above regex is a value with more than one name in it - meaning a duplicate UID."
- "All right - now I can see how you got the results. What about the second expression, the 'next available UID' tool?"
- "Ah, you mean this one:"
"It's nothing but a short loop in which I check if the UID specified by '$n' exists. If that test succeeds - meaning that there is a UID equal to '$n' in use - 'redo' gets invoked, '$n' is incremented, and the test happens again. If it fails, however, '$n' is printed to STDOUT and the program exits. Useful, and not too complicated. Just a bit of work, and they should have it all done. The security breach is something else, but at least now they know about it..."