Ensure that webmail tokens are in sync with sessions
master
Florent Daigniere 3 years ago
parent c6aefd8e04
commit fe18cf9743

@ -1,4 +1,4 @@
from mailu import models from mailu import models, utils
from flask import current_app as app from flask import current_app as app
import re import re
@ -32,8 +32,8 @@ def check_credentials(user, password, ip, protocol=None, auth_port=None):
return False return False
is_ok = False is_ok = False
# webmails # webmails
if len(password) == 64 and auth_port in ['10143', '10025']: if auth_port in ['10143', '10025'] and password.startswith('token-'):
if user.verify_temp_token(password): if utils.verify_temp_token(user.get_id(), password):
is_ok = True is_ok = True
# All tokens are 32 characters hex lowercase # All tokens are 32 characters hex lowercase
if not is_ok and len(password) == 32: if not is_ok and len(password) == 32:

@ -68,8 +68,9 @@ def user_authentication():
if (not flask_login.current_user.is_anonymous if (not flask_login.current_user.is_anonymous
and flask_login.current_user.enabled): and flask_login.current_user.enabled):
response = flask.Response() response = flask.Response()
response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, flask_login.current_user.get_id(), "") email = flask_login.current_user.get_id()
response.headers["X-User-Token"] = models.User.get_temp_token(flask_login.current_user.get_id()) response.headers["X-User"] = models.IdnaEmail.process_bind_param(flask_login, email, "")
response.headers["X-User-Token"] = utils.gen_temp_token(email, flask.session)
return response return response
return flask.abort(403) return flask.abort(403)

@ -16,7 +16,6 @@ import passlib.hash
import passlib.registry import passlib.registry
import time import time
import os import os
import hmac
import smtplib import smtplib
import idna import idna
import dns.resolver import dns.resolver
@ -645,15 +644,6 @@ in clear-text regardless of the presence of the cache.
user = cls.query.get(email) user = cls.query.get(email)
return user if (user and user.enabled and user.check_password(password)) else None return user if (user and user.enabled and user.check_password(password)) else None
@classmethod
def get_temp_token(cls, email):
user = cls.query.get(email)
return hmac.new(app.temp_token_key, bytearray("{}|{}".format(time.strftime('%Y%m%d'), email), 'utf-8'), 'sha256').hexdigest() if (user and user.enabled) else None
def verify_temp_token(self, token):
return hmac.compare_digest(self.get_temp_token(self.email), token)
class Alias(Base, Email): class Alias(Base, Email):
""" An alias is an email address that redirects to some destination. """ An alias is an email address that redirects to some destination.

@ -121,7 +121,6 @@ proxy = PrefixMiddleware()
# Data migrate # Data migrate
migrate = flask_migrate.Migrate() migrate = flask_migrate.Migrate()
# session store (inspired by https://github.com/mbr/flask-kvsession) # session store (inspired by https://github.com/mbr/flask-kvsession)
class RedisStore: class RedisStore:
""" Stores session data in a redis db. """ """ Stores session data in a redis db. """
@ -232,7 +231,8 @@ class MailuSession(CallbackDict, SessionMixin):
def destroy(self): def destroy(self):
""" destroy session for security reasons. """ """ destroy session for security reasons. """
if 'webmail_token' in self:
self.app.session_store.delete(self['webmail_token'])
self.delete() self.delete()
self._uid = None self._uid = None
@ -273,6 +273,11 @@ class MailuSession(CallbackDict, SessionMixin):
if self._sid is None: if self._sid is None:
self._sid = self.app.session_config.gen_sid() self._sid = self.app.session_config.gen_sid()
set_cookie = True set_cookie = True
if 'webmail_token' in self:
app.session_store.put(self['webmail_token'],
self.sid,
int(app.config['PERMANENT_SESSION_LIFETIME']),
)
# get new session key # get new session key
key = self.sid key = self.sid
@ -477,3 +482,24 @@ class MailuSessionExtension:
cleaned = Value('i', False) cleaned = Value('i', False)
session = MailuSessionExtension() session = MailuSessionExtension()
# this is used by the webmail to authenticate IMAP/SMTP
def verify_temp_token(email, token):
try:
if token.startswith('token-'):
sessid = app.session_store.get(token)
if sessid:
session = MailuSession(sessid, app)
if session.get('_user_id', '') == email:
return True
except:
pass
def gen_temp_token(email, session):
token = session.get('webmail_token', 'token-'+secrets.token_urlsafe())
session['webmail_token'] = token
app.session_store.put(token,
session.sid,
int(app.config['PERMANENT_SESSION_LIFETIME']),
)
return token

Loading…
Cancel
Save