#! /usr/bin/env python
"""Simple test script for cryptmodule.c
   Roger E. Masse
"""

import crypt, sys, getopt

class Passwd:
	def __init__(self):
		class PasswdEntry:
			def __init__(self, user, account):
				self.username = user
				self.salt = account[:12]
				self.passwd = account
				self.clear = None

			def match(self, string):
				if self.clear == string:
					if not show_nothing:
						print 'Another entry for user', self.username
					return 1
				crypted = crypt.crypt(string, self.salt)
				if crypted == self.passwd:
					self.clear = string
					if not show_nothing:
						print 'Found password of', self.username
					if not show_nothing and show_pass:
						print 'Password of', self.username, ':', string
					return 1
				return 0

		self.accounts = []
		self.cache = {}
		f = open('/etc/shadow')
		for line in f:
			fields = line.split(':')
			if fields[1] != '!' and fields[1] != '*':
				self.accounts.append(PasswdEntry(fields[0],fields[1]))
		if len(self.accounts) == 0:
			print 'Unable to parse shadow file ... exiting'
			sys.exit(1)

		f.close()
	
	def match(self, string):
		if use_cache and string in self.cache:
			val = self.cache[string]
			if val != None:
				return val
	
		is_passwd = 0
		for user in self.accounts:
			is_passwd = user.match(string)
			if is_passwd:
				break

		if use_cache:
			self.cache[string] = is_passwd
		return is_passwd


class LogFile:
	def __init__(self, fn):
		f = open(fn)
		self.buf = f.read()
		self.fn = fn
		f.close()
		self.pos = 0

	def write(self):
		f = open(self.fn, 'w')
		f.write(self.buf)
		f.close()

	def scan_byte(self):
		for line in self.buf.split('\n'):
			begin = 0
			end = 1
			l = len(line)
			while begin != l:
				if p.match(line[begin:end]):
					self.replace(end - begin)
					self.pos += end-begin
					begin = end
				end = end + 1
				if end == l + 1:
					begin = begin + 1
					end = begin +1
					self.pos = self.pos + 1
			self.pos = self.pos + 1


	def scan_word(self):
		for line in self.buf.split('\n'):
			begin = 0
			end = 1
			words = line.split(' ')
			l = len(words)
			while begin != l:
				if p.match(' '.join(words[begin:end])):
					self.replace(len(' '.join(words[begin:end])))
					self.pos += len(' '.join(words[begin:end])) + 1
					begin = end
					end = end + 1
					continue

				end = end + 1
				if end == l + 1:
					self.pos += len(words[begin]) + 1
					begin = begin + 1
					end = begin +1

	def scan_word_no_space(self):
		for line in self.buf.split('\n'):
			for word in line.split(' '):
				if p.match(word):
					self.replace(len(word))
				self.pos += len(word) + 1

	def replace(self, p_len):
		if not replace_pass:
			return
		p_str = '##censored_password##'
		old_buf = self.buf
		self.buf = old_buf[:self.pos]
		self.buf += p_str
		self.buf += old_buf[self.pos + p_len:]
		self.pos += len(p_str)
		self.pos -= p_len
#		print 'was_before:'
#		print old_buf
#		print 'is:'
#		print self.buf

def show_usage():
	print "Usage: censor [-hdsrbwn] <file>"
	print "Options:"
	print "  -h\t\tShow this help"
	print "  -d\t\tDisplay found passwords"
	print "  -s\t\tPerform silently (overrides -d)"
	print "  -r\t\tReplace found passwords in the file by junk"
	print "  -b\t\tSearch password byte by byte (really slow)"
	print "  -w\t\tSearch password word by word (slow)"
	print "  -n\t\tSearch password word by word considering passwords don't contain spaces(usable/default)"
	sys.exit(1)


opt, arg = getopt.getopt(sys.argv[1:], "hdsrbwn")

if not arg:
	show_usage()

show_pass = 0
show_nothing = 0
replace_pass = 0
mode = "word_no_space"

for option in opt:
	if option[0] == "-h":
		show_usage()
	elif option[0] == "-d":
		show_pass = 1
	elif option[0] == "-s":
		show_nothing = 1
	elif option[0] == "-r":
		replace_pass = 1
	elif option[0] == "-b":
		mode = "byte"
	elif option[0] == "-w":
		mode = "word"
	elif option[0] == "-n":
		mode = "word_no_space"
	
l = LogFile(arg[0])
p = Passwd()

if mode == "byte":
	use_cache = 0
	l.scan_byte()
elif mode == "word":
	use_cache = 1
	l.scan_word()
else:
	use_cache = 1
	l.scan_word_no_space()

if replace_pass:
	l.write()
