-1

Trying (In theory) to tail a log file, of the last two lines (-n 2), then proceed using if/then statements. This of course, would be a script that would be called from the launchctl .plist.

Basically it looks like this in my head, although it's not right...

#!/bin/sh

last-entry= tail -n 2 command # show only last two lines of log file

if last-entry is less than (<) two lines, then
    execute command here
fi
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
James Dean
  • 155
  • 1
  • 17
  • You want to do something if the file has less then two lines in it? – Etan Reisner Oct 12 '15 at 19:30
  • yes. my understanding of tail is that it's continuously looking for new entries, like a (keep alive trigger)? -- less than two is ideal, however, if there is ONLY one new entry, I would like to run a command. If there is no entry, then loop or (in my case) I'll create a .plist to keep my script alive. [UPDATE] the log file is the accountpolicy.log -- which prints one line if it's a false attempt & two lines (always duplicate timecode) for success. :) – James Dean Oct 12 '15 at 19:43
  • `tail` by itself does not wait or loop. `tail -f` (and the other related options) does however. That said you explicitly *don't want* that behavior in this case. And any way you test this will "race" the logfile being written to again. That log file is updated when/how often? – Etan Reisner Oct 12 '15 at 20:07
  • "race"? note sure what you mean. To answer you question, the log file is capturing (from: apple's accountpolicy.log) "failed & successful" login attempts. The log file rarely updates unless my computer goes to sleep and I need to log into my account. – James Dean Oct 12 '15 at 20:17
  • Or someone else tries to log in to your computer (possibly remotely assuming that logs there also) while you are doing something. And to "race" in this context means that between the time you check the file's contents to the time you do whatever action you planned on doing the file's contents may have changed see [TOCTOU](https://cwe.mitre.org/data/definitions/367.html) and [on wikipedia](https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use) for example. What's your ultimate goal here? What's `execute command here` going to *actually* do? – Etan Reisner Oct 12 '15 at 20:21
  • -- **[UPDATE]** seeing that successful attempts have two lines of entry, maybe I can use a command like, `sort file.log | uniq -u`? because the successful attempts come through with same (datestamp) – James Dean Oct 12 '15 at 20:24
  • the command is calling a _.command_ script, _imagesnap_ which would snap a photo using iSight. This has to be easy to execute. I can't imagine this would be that difficult ha ha. – James Dean Oct 12 '15 at 20:26
  • If you just want to check line count then use `wc -l`. That's what it does. – Etan Reisner Oct 12 '15 at 20:28
  • I've read about the _wc -1_ handler. Can you help me understand by illustrating an example @EtanReisner? would the wc work with the tail command? is that what you're implying? – James Dean Oct 12 '15 at 20:39
  • Not dash-one. dash-ell (the letter). And no, you don't need tail here. You don't actually care about the contents if you **know** it will always either be a single line or two lines in the file. – Etan Reisner Oct 12 '15 at 20:41
  • makes sense. Okay. You said that every time I (myself) _tail_ a file, it will literally erase the log file? if so, I'm not sure if this will work seeing that the log file lives in `/private/var/log/accountpolicy.log` and needs sudo privileges... – James Dean Oct 12 '15 at 20:49
  • No. `tail` will not erase the file. I didn't say that. I said that between the time when you run `tail` and when your next command runs *something else* could change the files contents. So if you expected the contents to be what they were when you last checked them you might end up surprised (and this is a **classic** vulnerability as indicated by that mitre link I showed). If you don't care about the contents once you've checked them then it doesn't matter as much (or potentially at all). – Etan Reisner Oct 12 '15 at 20:54
  • Okay. What would be the best way to layout this command as an example? Still somewhat confuse here. Please bare with me. – James Dean Oct 12 '15 at 20:59

1 Answers1

0

If what you are trying to do is test that the file contains exactly two lines then you just want to use wc -l and feed it the contents on standard input (to avoid wc printing out the filename as it normally does).

#!/bin/sh

if [ "$(wc -l < /private/var/log/accountpolicy.log)" -ne 2 ]; then
    exit
fi

# Do whatever you want when it does contain exactly two lines here.

Feeding wc -l the file via standard input is important here because when called normally (i.e. wc -l filename) wc "helpfully" prints the line count and the filename to standard output and thus requires field splitting/etc. to obtain a number suitable for comparison. When wc is reading standard input it doesn't have a filename and so doesn't do this.

Note:

This practice is only safe when you do not care about the actual contents of the file once wc has finished executing.

If you use the contents of the file in the rest of the script then this is a classic Time-of-Check Time-of-Use vulnerability. See this page on MITRE's website and this Wikipedia entry for more on this topic.

Community
  • 1
  • 1
Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • yeah, but if I have this script running because it's being called by a .plist every second, wouldn't `wc -l` always show two lines because the log file hasn't been deleted? so what I'm saying is, how would `wc` know if there has been new entries? – James Dean Oct 12 '15 at 22:27
  • `wc` will count the lines in the file at the *exact* moment that it is counting them. However many there are at that moment will be returned. If that changes even a microsecond later it won't notice until it runs again and you get the new number. That's the point. Also running a script every second like this is both wildly excessive and wildly not quick enough to catch brute-force attempts that happen to succeed, etc. (again assuming remote access counts too). – Etan Reisner Oct 12 '15 at 23:37
  • Yeah, I hear ya on that. I think I'm getting the jiss of it...What would be the parameters, say, the `-ne 2` -- is there something that would command if there is something **more** than 2? I'm running into a problem. every time the .sh file is executed by .plist event; it's executing the script because the entry is always being reset with `>` there for, it thinks there is always less than 2...see where I'm going with this? – James Dean Oct 13 '15 at 00:22
  • What "entry is always being reset"? You said the file is constantly being recreated from scratch and either has a single line in it or has two lines in it. Is that not the case? Does it just get appended to normally (so gets longer constantly until something truncates or rotates it)? Other flags to `[`/`test` are documented [here](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html), [here](http://www.gnu.org/software/bash/manual/bashref.html#Bash-Conditional-Expressions) and [here](http://www.gnu.org/software/bash/manual/bashref.html#index-test). – Etan Reisner Oct 13 '15 at 01:44
  • it resets every 24 hours as apple adds it into .gz file for storage. When I used your code, epscially the `>` command; which resets or erases the content of the file. I don't mind that, however, if that's the case, then the code would just keep executing itself. It's weird. I'm trying all sorts of ifelse commands and nothing is working. It seems non of the parameters (-nq -eq -le -gt) aren't working. I even put `wc` into a variable and yet, nothing. :( – James Dean Oct 13 '15 at 02:21
  • `>` isn't a command it is a redirection and my code didn't have `>` in it anywhere. Those test flags definitely work. If they aren't for you you aren't using them correctly. But if the file rotates once a day how are you expecting to test for line counts more than immediately after the rotation. Presumably what you need to do is check whether the *last two lines* are identical and that is, inherently, a racy proposition. There is almost certainly a better way to hook into login failures I would imagine. – Etan Reisner Oct 13 '15 at 02:28
  • Okay! I got it working. How can I add an else within the function? Like, if less than (`-le 1`) do command; else (`-gt 1`) do this command. Thanks for sticking this out @EtanResiner, you're the man! – James Dean Oct 13 '15 at 02:53
  • `if [ $i -gt 5 ]; then ... elif [ $i -lt 2 ]; then ... else ... fi` But line counts really aren't going to help you here **at all** if the file is appended to during the course of a single day. – Etan Reisner Oct 13 '15 at 02:57
  • Excellent! Works like a charm; and you're right once again. The .plist isn't fast enough to run (4 seconds). Is there a way to run the service faster? Like have a loop inside the script? – James Dean Oct 13 '15 at 05:15
  • You are just approaching this in entirely the wrong direction. If you want to take an action when a login attempt fails you should look into whether there are *official* ways to be notified of that event from the system. Then you don't have a polling problem. You don't have a file content race problem. You don't have all sorts of other problems either. – Etan Reisner Oct 13 '15 at 22:40