Python hashlib.md5 and ejabberd - python

I am using a python script as an external auth option in ejabberd 2.1.6.
I wanted to start encrypting the clear text passwords that come across in the auth verification, so that they are not being stored in plain text in the backend database. When I add the following code to my python script and restart ejabberd, it hangs:
import hashlib
clear = "barfoo"
salt = "foobar"
hash = hashlib.md5( salt + clear ).hexdigest()
Does hashlib require specific priviledges to run?
When I run it as a normal user (ejabberd) it works without issue. When the python script is run within the external auth of ejabberd it hangs.
I've attempted to have it write out the 'hash' to a file and it never gets there ... if i run it as the 'ejabberd' user, it writes out to file fine.
I've tried to find information about restrictions for using this library on ubuntu without any success. Any ideas?
-sd
** 22.02.2011: Here is the full script adapted from https://git.process-one.net/ejabberd/mainline/blobs/raw/2.1.x/doc/dev.html#htoc8 :
#!/usr/bin/python
import sys
from struct import *
import hashlib
def from_ejabberd():
input_length = sys.stdin.read(2)
(size,) = unpack('>h', input_length)
return sys.stdin.read(size).split(':')
def to_ejabberd(bool):
answer = 0
if bool:
answer = 1
token = pack('>hh', 2, answer)
sys.stdout.write(token)
sys.stdout.flush()
def auth(username, server, password):
clear = "barfoo"
salt = "foobar"
hash = hashlib.md5( salt + clear ).hexdigest()
if (password == hash): return True
else: return False
def isuser(username, server):
return True
def setpass(username, server, password):
return True
while True:
data = from_ejabberd()
success = False
if data[0] == "auth":
success = auth(data[1], data[2], data[3])
elif data[0] == "isuser":
success = isuser(data[1], data[2])
elif data[0] == "setpass":
success = setpass(data[1], data[2], data[3])
to_ejabberd(success)

I have been messing with the same problem. I Can't really track down the problem with openssl binings in _hashlib. Whatever the problem is, i will have to patch python distribution sources. Not really an feasible solution. So i ended up using a pycrypto wrapper for the crypto functions which don't block in this case.
pip install pycrypto
from Crypto.Hash import MD5
m = MD5.new()
m.update("%s%s" % (salt ,clear))
h.hexdigest()

I looked into the hashlib source and while it does not seem to require too much, it does import .so files as modules and one of them hits openssl. It all looks pretty safe but if ejabberd tries to fence itself against calls to 3rd party code (or if you have SELinux or something else to that effect running), stuff can conceivably get weird. I got this in a REPL:
>>> import _md5
>>> _md5.__file__
'/usr/lib/python2.7/lib-dynload/_md5module.so'
Try this on your box and then try putting
_md5 = imp.load_dynamic('_md5', '/usr/lib/python2.7/lib-dynload/_md5module.so')
Or just
import _md5
(with the appropriate path updated to yours) in your code before the offending line and with some trace statement afterwards. Try the same with _hashlib instead of _md5 (hashlib defaults to _hashlib which wraps openssl, but if it doesn't load or doesn't have the needed hash it falls back to _md5, _sha etc.). If it's not the imports that fail/hang then you can try calling _md5.new(salt + clear) and _hashlib.openssl_md5(salt + clear) and see if it's one of them.
If it is the import at fault, then possibly a similar problem was tackled here
I don't know ejabberd, so I can't relate their solution to your problem, unfortunately.
I do have to say it, though: in all python implementations I know, = instead of == in a condition would raise a SyntaxError and that's that - the program would never even enter the main while loop.

hashlib does not require anything special. What means hangs? Where does it hang? Use pdb.set_trace() to step trough the code or use 'strace' or 'ltrace' to investigate API calls.

Try to use logging module, it can help you to watch input and output data, also check scripts permissions, let executing by user which is ejabberd, or for debug just set chmod 777 external.py.

Related

Non-ASCII character '\x97' in file, but no encoding declared

I'm trying to encode the contents of a Python script in Linux. I just started off with a simple script called test.py -
# !/app/logs/Python/bin/python3
# -*- coding: ascii -*-
print ("hi")
Once I have the script, I execute the vim -x test.py and enter the encryption key twice. Then save the file as normal and then execute the script using python test.py
I tried almost all the examples provided in the link here but still i end up getting the below error -
SyntaxError: Non-ASCII character '\x97' in file test.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details
I checked the default encoding using print sys.getdefaultencoding() and it is acsii.
What am i missing here. kindly clarify. I know this question is a duplicate but none of the solutions helped.
Python knows how to execute clear text Python source code. If you encrypt the source file, it no longer contains valid Python source and cannot be directly executed.
There are 2 possible ways here. First is to only obfuscate your source. You should be aware that obfuscation is not security, and a user could recover some Python source with some work (not necessarily the original source, because comments and doc strings could have been stripped off and variable names could have been changed). You could read How do I protect Python code? and google for python obfuscate to find some possible ways of obfuscating Python source and their trade-offs.
The good news with obfuscated source is that it can be used by anybody without any password.
Second way is to encrypt the source. If you use a decent tool, you can assume that it will be impossible to read or execute the file without knowing the key. In that sense, vim crypto has not the highest possible reputation. In the simplest way (for example with your example vim -x) you will have to decrypt the file to execute it. Unfortunately, good crypto modules are not shipped in a standard Python installation and must be downloaded from pypi. Well known crypto modules include pycrypto and cryptography.
You can then encrypt the most part of the code, and then at run time ask for the key, decrypt it and execute it. Still a serious job but feasible.
Alternatively, you could build in another language (C/C++) a decryptor that decrypts the remaining of the file and feed it into a python interpretor, but functionally, this is only a variant of the above method.
As per your comment I assume that you want to encrypt the source code and decrypt it (with the password) at run time. The princips are:
build a Python script that will take another arbitrary Python script, encode it with a secure crypto module and prepend it with some decrypting code.
at run time, the prepended code will ask for the password, decrypt the encrypted code exec it
The builder could be (this code uses the cryptography module):
import cryptography.fernet
import cryptography.hazmat.primitives.kdf.pbkdf2
import cryptography.hazmat.primitives.hashes
import cryptography.hazmat.backends
import base64
import os
import sys
# the encryption function
def encrypt_source(infile, outfile, passwd):
with open(infile, 'rb') as fdin: # read original file
plain_data = fdin.read()
salt, key = gen_key(passwd) # derive a key from the password
f = cryptography.fernet.Fernet(key)
crypted_data = f.encrypt(plain_data) # encrypt the original code
with open(outfile, "w") as fdout: # prepend a decoding block
fdout.write("""#! /usr/bin/env python
import cryptography.fernet
import cryptography.hazmat.primitives.kdf.pbkdf2
import cryptography.hazmat.primitives.hashes
import cryptography.hazmat.backends
import base64
import os
def gen_key(passwd, salt): # key derivation
kdf = cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC(
algorithm = cryptography.hazmat.primitives.hashes.SHA256(),
length = 32,
salt = salt,
iterations = 100000,
backend = cryptography.hazmat.backends.default_backend()
)
return base64.urlsafe_b64encode(kdf.derive(passwd))
passwd = input("Password:") # ask for the password
salt = base64.decodebytes({})
key = gen_key(passwd.encode(), salt) # derive the key from the password and the original salt
crypted_source = base64.decodebytes( # decode (base64) the crypted source
b'''{}'''
)
f = cryptography.fernet.Fernet(key)
plain_source = f.decrypt(crypted_source) # decrypt it
exec(plain_source) # and exec it
""".format(base64.encodebytes(salt),
base64.encodebytes(crypted_data).decode()))
# derive a key from a password and a random salt
def gen_key(passwd, salt=None):
if salt is None: salt = os.urandom(16)
kdf = cryptography.hazmat.primitives.kdf.pbkdf2.PBKDF2HMAC(
algorithm = cryptography.hazmat.primitives.hashes.SHA256(),
length = 32,
salt = salt,
iterations = 100000,
backend = cryptography.hazmat.backends.default_backend()
)
return salt, base64.urlsafe_b64encode(kdf.derive(passwd))
if __name__ == '__main__':
if len(sys.argv) != 3:
print("Usage {} infile outfile".format(sys.argv[0]))
sys.exit(1)
passwd = input("Password:").encode() # ask for a password
encrypt_source(sys.argv[1], sys.argv[2], passwd) # and generates an encrypted Python script

Dictionary reference keeps throwing an unsolved reference

I am building a mock terminal-like program using python, and am trying to build a login system for it.
My directory setup, after going through multiple revisions, eventually came out to look like this:
pythonapp-harlker/
__init__.py
loginCheck.py
main.py
userlist.py
__init__.py is empty, and main.py's main code chunk looks like this:
from loginCheck import *
loginFunc = Login()
loginFunc.login()
if loginFunc.login().checkPass == True:
print("Welcome %s!" % (username))
Importing loginCheck returns no error, so naturally, I looked at loginCheck.py.
import sys, platform, importlib
import hashlib
from userlist import *
class Login:
def login(self):
username = input("Username: ")
password = input("Password: ")
password = str.encode(password)
password = str(hashlib.md5(password).hexdigest())
if username in users:
userPassAndIndex = users.get(username)
if password == userPassAndIndex[0]:
checkPass = True
value = userPassAndIndex[1]
else:
self.login()
else:
self.login()
Looking at a debugger, it keeps telling me that loginCheck.py is unable to import a dictionary from userlist.py.
userlist.py
users = {
'localAdmin10': ["086e717a4524329da24ab80e0a9255e2", 0],
'orlovm': ["505ec2b430fa182974ae44ede95ca180", 1],
'testUser10': ["90e611f269e75ec5c86694f900e7c594", 2],
'torradiRemote': ["0b841ebf6393adac25950a215aecc3d1", 3],
}
Additionally, while running the python code (from main.py), the code seems unable to detect if the input username and passwords are correct.
I've looked at tons of stackOverflow pages and external sources but I'm at a kind of "programmer's block" now.
Your code runs fine on Python 3+, and breaks on Python 2.7, let me explain why:
the input function you want to use with Python 2.7 is raw_input, not input. The input function in Python 2.7, evaluates the user input as a Python expression, so in your case, can't find it and send an exception
raw_input was renamed input starting Python 3.0
So to sum it up, you just have to pick the right function depending on which version of Python you'd like to use. raw_inputfor Python 2.7, input for Python 3.

Generalized Desktop directory

Simple enough, off of my last question, I am trying to make a directory change to a players desktop or file that is similar for all, as in C:\\Users\\USERNAME\\Desktop\\Tester File but the how would I make it so that USERNAME is the username of the person's computer? I tried using %USERNAME% but I don't really know how to do that, and it didn't work, and anyway the % gave an error message (I cannot remember the message, I think it was syntax error)
I also tried using ~, but it proved to be ineffective, but it may be due to my lack of experience.
EDIT
I solved this issue, thanks to some very great help from #pstatix so thank you.
By using user = getpass.getuser() I was able to do something like 'C:\Users' + user + '\Documents' it made this all user friendly! Thanks!
Have you tried the getpass module? getpass documentation here.
import getpass
usr = getpass.getuser()
print usr
Edit: For user specified example
You may also be interested in using the os module? os documentation here.
import os
usr = os.getlogin()
path = os.path.join('..', 'Users', usr, 'Desktop', 'Tester File')
os.chdir(path)
Using os.environ for environment variables may also prove useful. os.environ documentation here For example:
import os
def getUserName():
# set possible environment variables
for name in ('LOGNAME', 'USER', 'LNAME', 'USERNAME'):
usr = os.environ.get(name)
if user:
return usr #return the variable
if __name__ == '__main__':
usr = getUserName()
# do remainder below

Opens same registry twice?

I am trying to get all installed programs of my windows computer, therefore I read out the registry.
But somehow python reads the 32bit programs out twice (even though I give him another registry entry)
Here is the code snipped:
def get_programs(registry):
reg = ConnectRegistry(None, HKEY_LOCAL_MACHINE)
programList = []
key = OpenKey(reg, registry)
print(QueryInfoKey(key))
for i in range(0, QueryInfoKey(key)[0]):
programList.append(EnumKey(key, i))
CloseKey(key)
CloseKey(reg)
return programList
I call this function like this:
registry32bit = "SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
registry64bit = "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
programs32bit = get_programs(registry32bit)
programs64bit = get_programs(registry64bit)
Why does python open and read out the same registry (for 32 bit) twice and return the exactly same list?
This appears to work and uses #eryksun suggestion in a comment below about just letting the redirection happen and not explicitly referencing the Wow6432Node registry key. The central idea is to just specify either the KEY_WOW64_32KEY or KEY_WOW64_64KEY flag when opening the uninstall subkey and let the magic happen.
Note: I also Pythonized the code in the get_programs() function some. This made it shorter and more readable in my opinion.
import sys
from _winreg import *
# Assure registry handle objects with context manager protocol implemented.
if sys.version_info.major*10 + sys.version_info.minor < 26:
raise AssertionError('At least Python 2.6 is required.')
def get_programs(subkey, regBitView):
with ConnectRegistry(None, HKEY_LOCAL_MACHINE) as hive:
with OpenKey(hive, subkey, 0, regBitView | KEY_READ) as key:
return [EnumKey(key, i) for i in range(QueryInfoKey(key)[0])]
UNINSTALL_REG_KEY = r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall'
programs32bit = get_programs(UNINSTALL_REG_KEY, KEY_WOW64_32KEY)
programs64bit = get_programs(UNINSTALL_REG_KEY, KEY_WOW64_64KEY)
print('32-bit programs:\n{}'.format(programs32bit))
print('')
print('64-bit programs:\n{}'.format(programs64bit))
Many thanks to #eryksun for the clues and many implementation strategy suggestions.

How to determine if win32api.ShellExecute was successful using hinstance?

I've been looking around for an answer to my original issue.. how do i determine (programmatically) that my win32api.ShellExecute statement executed successfully, and if a successful execution occurs, execute an os.remove() statement.
Researching I found out that the ShellExecute() call returns the HINSTANCE. Further digging I found that ShellExecute() will return an HINSTANCE > 32 if it was successful. My problem/question now is, how do i use it to control the rest of my program's flow? I tried using an if HINSTANCE> 32: statement to control the next part, but I get a NameError: name 'hinstance' is not defined message. Normally this wouldn't confuse me because it means i need to define the variable 'hinstance' before referencing it; however, because i thought ShellExecute is supposed to return HINSTANCE, i thought that makes it available for use?
Here is my full code where i am trying to implement this. Note that in my print_file() def i am assigning hinstance to the full win32api.ShellExecute() command in attempt to capture the hinstance along with explicitly returning it at the end of the function.. this isn't working either.
import win32print
import win32api
from os.path import isfile, join
import glob
import os
import time
source_path = "c:\\temp\\source\\"
def main():
printer_name = win32print.GetDefaultPrinter()
while True:
file_queue = [f for f in glob.glob("%s\\*.txt" % source_path) if isfile(f)]
if len(file_queue) > 0:
for i in file_queue:
print_file(i, printer_name)
if hinstance > 32:
time.sleep(.25)
delete_file(i)
print "Filename: %r has printed" % i
print
time.sleep(.25)
print
else:
print "No files to print. Will retry in 15 seconds"
time.sleep(15)
def print_file(pfile, printer):
hinstance = win32api.ShellExecute(
0,
"print",
'%s' % pfile,
'/d:"%s"' % printer,
".",
0
)
return hinstance
def delete_file(f):
os.remove(f)
print f, "was deleted!"
def alert(email):
pass
main()
With ShellExecute, you will never know when the printing is complete, it depends on the size of the file and whether the printer driver buffers the contents (the printer might be waiting for you to fill the paper tray, for example).
According to this SO answer, it looks like subprocess.call() is a better solution, since it waits for the command to complete, only in this case you would need to read the registry to obtain the exe associated with the file.
ShellExecuteEx is available from pywin32, you can do something like:
import win32com.shell.shell as shell
param = '/d:"%s"' % printer
shell.ShellExecuteEx(fmask = win32com.shell.shellcon.SEE_MASK_NOASYNC, lpVerb='print', lpFile=pfile, lpParameters=param)
EDIT: code for waiting on the handle from ShellExecuteEx()
import win32com.shell.shell as shell
import win32event
#fMask = SEE_MASK_NOASYNC(0x00000100) = 256 + SEE_MASK_NOCLOSEPROCESS(0x00000040) = 64
dict = shell.ShellExecuteEx(fMask = 256 + 64, lpFile='Notepad.exe', lpParameters='Notes.txt')
hh = dict['hProcess']
print hh
ret = win32event.WaitForSingleObject(hh, -1)
print ret
The return value of ShellExecute is what you need to test. You return that from print_file, but you then ignore it. You need to capture it and check that.
hinstance = print_file(i, printer_name)
if hinstance > 32:
....
However, having your print_file function leak implementation detail like an HINSTANCE seems bad. I think you would be better to check the return value of ShellExecute directly at the point of use. So try to move the > 32 check inside print_file.
Note that ShellExecute has very weak error reporting. If you want proper error reporting then you should use ShellExecuteEx instead.
Your delete/sleep loop is very brittle indeed. I'm not quite sure I can recommend anything better since I'm not sure what you are trying to achieve. However, expect to run into trouble with that part of your program.

Categories

Resources