Compare commits

..

3 commits

Author SHA1 Message Date
Daniel A. Maierhofer 9b74fcabee Remove Message: 2020-04-27 00:35:08 +02:00
Daniel A. Maierhofer 7fafc8f150 Merge branch 'master' of https://github.com/phoerious/vmailmgr-chpw-cgi into german 2020-04-27 00:33:03 +02:00
Daniel A. Maierhofer a27349e736 Add german translation 2017-06-18 13:59:31 +02:00
7 changed files with 66 additions and 103 deletions

View file

@ -1,19 +1,7 @@
# required if using apache2: a2enmod authnz_ldap
AuthType Basic
AuthName "Login"
AuthBasicProvider ldap
#AuthLDAPBindAuthoritative off
AuthLDAPBindDN UID=bind,OU=Users,DC=ldap,DC=freiesnetz,DC=at
AuthLDAPBindPassword ldapbindpassword
AuthLDAPURL ldap://localhost/OU=Users,DC=ldap,DC=freiesnetz,DC=at?uid
LDAPReferrals off
Require valid-user
# disabled because of client error: ERR_TOO_MANY_REDIRECTS
#RewriteEngine on
#RewriteCond %{HTTPS} !=on
#RewriteCond %{ENV:HTTPS} !=on
#RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]
RewriteEngine on
RewriteCond %{HTTPS} !=on
RewriteCond %{ENV:HTTPS} !=on
RewriteRule .* https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]
Options +ExecCGI
AddHandler cgi-script .py

View file

@ -1,34 +1,22 @@
# LDAP chpw CGI
# VMailMgr chpw CGI
This is a Python CGI script that lets ldap users change
their own ldap passwords via a web interface.
This is a Python CGI script that lets virtual `VMailMgr`/`qmail` users change
their own mail passwords via a web interface.
If users with same UID and same oldpassword are found in other OUs,
those passwords are updated too.
This script is specifically tailored to work on hosts at
[uberspace.de](https://uberspace.de). But it may also work on other qmail-based
systems as long as virtual mail users are managed via `VMailMgr`.
## Installation
To install the script, simply extract all the repository contents into a folder
under your document root. No paths need to be configured. Only make sure that the
location is reachable via HTTPS. If used with Apache2, this module is required: `a2enmod authnz_ldap`
location is reachable via HTTPS.
Configure LDAP settings for your LDAP server in index.py:
```
ldap_proto = 'ldap://'
ldap_server = 'localhost'
ldap_basedn = 'dc=ldap,dc=freiesnetz,dc=at'
ldap_userdn = 'ou=Users' +','+ ldap_basedn
ldap_bind_attr = 'uid'
```
Configure LDAP settings for your LDAP server in .htaccess:
```
AuthLDAPBindDN UID=bind,OU=Users,DC=ldap,DC=freiesnetz,DC=at
AuthLDAPBindPassword ldapbindpassword
AuthLDAPURL ldap://localhost/OU=Users,DC=ldap,DC=freiesnetz,DC=at?uid
```
In case you are using a U7 uberspace, you have to add a SELinux permission to allow apache to access both your home directory and the VMailMgr user database:
`chcon -t httpd_sys_content_t ~ ~/passwd.cdb`
## Acknowledgements
This is a majorly for ldap-support rewritten version of a script originally developed by Dirk Boye.
This is a majorly refined version of a script originally developed by Dirk Boye.
See [dirkboye/mailpw_change](https://github.com/dirkboye/mailpw_change) at GitHub
for the original source code.

View file

@ -2,16 +2,12 @@
import cgi, cgitb
import re
import sys, os
import ldap
ldap_proto = 'ldap://'
ldap_server = 'localhost'
ldap_basedn = 'dc=ldap,dc=freiesnetz,dc=at'
ldap_userdn = 'ou=Users' +','+ ldap_basedn
ldap_bind_attr = 'uid'
cgitb.enable(display=0, logdir='logs/')
from subprocess import check_output, Popen, PIPE, STDOUT, CalledProcessError
from os.path import expanduser
cgitb.enable()
home_dir = expanduser("~")
os.environ['HOME'] = home_dir
def check_form(formvars, form):
for varname in formvars:
@ -22,7 +18,6 @@ def check_form(formvars, form):
return None
return True
def read_template_file(filename, **vars):
with open('tpl/' + filename, mode='r', encoding='utf-8') as f:
template = f.read()
@ -30,30 +25,38 @@ def read_template_file(filename, **vars):
template = template.replace('{$' + key + '}', vars[key])
return template
def check_oldpw(accountname, oldpass):
try:
conn = ldap.initialize(ldap_proto+ldap_server)
conn.set_option(ldap.OPT_REFERRALS, 0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
if conn.simple_bind(ldap_bind_attr+'='+accountname+','+ldap_userdn, oldpass) == True:
return True
except ldap.INVALID_CREDENTIALS:
conn.unbind()
dumpvuserargs = ['dumpvuser', accountname]
userdump = check_output(dumpvuserargs).strip().decode('utf-8')
m = re.search('Encrypted-Password: (\$([^\$]+)\$([^\$]+)\$([^\$\n]+))', userdump)
if None == m:
return False
oldhash = m.group(1)
hashtype = m.group(2)
salt = m.group(3)
except CalledProcessError:
return False
opensslargs = ['openssl', 'passwd', '-' + hashtype, '-salt', salt, '-stdin']
p = Popen(opensslargs, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
p.stdin.write(oldpass.encode('utf-8') + b'\n')
p.stdin.close()
if p.wait() == 0:
newhash = p.stdout.readline().strip().decode('utf-8');
if newhash == oldhash:
return True
return False
def generate_headers():
return "Content-Type: text/html; charset=utf-8\n"
def main():
main_content = ''
form = cgi.FieldStorage()
http_host = os.environ.get('HTTP_HOST')
if 'submit' in form.keys():
formvars = ['accountname', 'oldpass', 'newpass', 'newpass2']
form_ok = check_form(formvars, form)
@ -65,49 +68,34 @@ def main():
newpass2 = form['newpass2'].value
if newpass == newpass2:
if check_oldpw(accountname, oldpass):
conn = ldap.initialize(ldap_proto+ldap_server)
conn.set_option(ldap.OPT_REFERRALS, 0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
conn.simple_bind(ldap_bind_attr+'='+accountname+','+ldap_userdn, oldpass)
results = conn.search_s(ldap_basedn, ldap.SCOPE_SUBTREE, '('+ldap_bind_attr+'='+accountname+')', ['dn'])
conn.unbind()
for dn in results:
conn = ldap.initialize(ldap_proto+ldap_server)
conn.set_option(ldap.OPT_REFERRALS, 0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
# do a synchronous ldap bind
conn.simple_bind_s(dn[0], oldpass)
conn.passwd_s(dn[0], oldpass, newpass)
conn.unbind_s()
conn = ldap.initialize(ldap_proto+ldap_server)
conn.set_option(ldap.OPT_REFERRALS, 0)
conn.set_option(ldap.OPT_PROTOCOL_VERSION, 3)
if conn.simple_bind(ldap_bind_attr+'='+accountname+','+ldap_userdn, newpass) == True:
vpasswdargs = ['vpasswd', accountname]
p = Popen(vpasswdargs, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
p.stdin.write(newpass.encode('utf-8') + b'\n')
p.stdin.write(newpass2.encode('utf-8') + b'\n')
p.stdin.close()
if p.wait() == 0:
# We did it
conn.unbind()
main_content = read_template_file('success.tpl', http_host=http_host)
main_content = read_template_file('success.tpl')
else:
conn.unbind()
main_content = read_template_file('fail.tpl', message=cgi.escape(ldap.LDAPError))
main_content = read_template_file('fail.tpl', message=cgi.escape(p.stdout.read()))
else:
main_content = read_template_file('fail.tpl', message='User not found or wrong password entered.')
main_content = read_template_file('fail.tpl', message='Benutzer nicht gefunden oder Passwort falsch.')
else:
main_content = read_template_file('fail.tpl', message='Passwords do not match.')
main_content = read_template_file('fail.tpl', message='Passwörter stimmen nicht überein.')
elif form_ok == False:
main_content = read_template_file('fail.tpl', message='All fields are required.')
main_content = read_template_file('fail.tpl', message='Alle Felder müssen ausgefüllt werden.')
else:
main_content = read_template_file('fail.tpl', message='Invalid data type supplied.')
main_content = read_template_file('fail.tpl', message='Ungültiger Datentyp.')
else:
# Submit button not pressed, show form
formaction = cgi.escape("https://" + os.environ["HTTP_HOST"] + os.environ["REQUEST_URI"])
#accountname = os.environ.get('REMOTE_USER')
accountname = os.environ.get('AUTHENTICATE_UID')
form = read_template_file('form.tpl', formaction=formaction, accountname=accountname, http_host=http_host)
form = read_template_file('form.tpl', formaction=formaction)
main_content = form
response = generate_headers() + "\n"
response += read_template_file('main.tpl', main_content=main_content)
sys.stdout.buffer.write(response.encode('utf-8'))
if __name__ == "__main__":
main()

View file

@ -1,5 +1,5 @@
<div class="error">
<h2>Oops, something went wrong</h2>
<p>Message: <span class="message">{$message}</span></p>
<a href="" class="back">Try again</a>
<h2>Euje, da ist etwas schief gelaufen…</h2>
<p><span class="message">{$message}</span></p>
<a href="" class="back">Nochmal probieren</a>
</div>

View file

@ -1,14 +1,13 @@
<form action="{$formaction}" method="post">
<div id="PasswordForm">
<p><label for="accountname">Account Name:</label>
<input type="text" name="accountname" id="accountname" placeholder="Your account name, including internal prefix" required value="{$accountname}"></p>
<p><label for="oldpass">Old Password:</label>
<p><label for="accountname">E-Mail-Adresse:</label>
<input type="text" name="accountname" id="accountname" required></p>
<p><label for="oldpass">Altes Passwort:</label>
<input type="password" name="oldpass" id="oldpass" required></p>
<p><label for="newpass">New Password:</label>
<p><label for="newpass">Neues Passwort:</label>
<input type="password" name="newpass" id="newpass" required></p>
<p><label for="newpass2">Repeat New Password:</label>
<p><label for="newpass2">Neues Passwort nochmals:</label>
<input type="password" name="newpass2" id="newpass2" required></p>
<p><input type="submit" name="submit" value="Change Password"></p>
<p><a href="https://log:out@{$http_host}/">Logout</a> (after logout click cancel and close browser/tab!)
<p><input type="submit" name="submit" value="Passwort ändern"></p>
</div>
</form>

View file

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>LDAP Change Password</title>
<title>Mailbox Passwort</title>
<meta charset="utf-8">
<style>
html {
@ -49,6 +49,7 @@
a:hover, a:focus {
text-decoration: none;
}
#Main {
margin: 3rem auto;
padding: 1rem;
@ -74,7 +75,7 @@
</style>
<body>
<div id="Main">
<h1>Change LDAP Password</h1>
<h1>Mailbox Passwort ändern</h1>
{$main_content}
</div>
</body>

View file

@ -1,2 +1 @@
<p class="success">Your password was changed successfully.</p>
<p><a href="https://log:out@{$http_host}/">Logout</a> (after logout click cancel and close browser/tab!)
<p class="success">Passwort erfolgreich geändert.</p>