Почтовый сервер Лаборатории МБК
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

124 lines
4.7KB

  1. #!/usr/bin/env python3
  2. import asyncio
  3. uvloop_exist = False
  4. try:
  5. import uvloop
  6. uvloop_exist = True
  7. except ImportError:
  8. pass
  9. import os, sys
  10. sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
  11. from mbkmail import ldap
  12. ERR_OK=0
  13. ERR_PERMFAIL=1
  14. ERR_LOOKUP_OK=2
  15. ERR_NOUSER=3
  16. ERR_TEMPFAIL=111
  17. async def credentials_lookup(username, none_password, scheme='PLAIN'):
  18. return False # FIXME: Не реализовано
  19. async def user_lookup(username, password, scheme='PLAIN'):
  20. u = ldap.UserDB()
  21. if os.environ.get('AUTH_LOGIN_USER', None) and os.environ.get('AUTH_LOGIN_USER', None) != os.environ.get('AUTH_ORIG_USER', None): # Masteruser login
  22. user = await u.user_by_email(os.environ['AUTH_LOGIN_USER'])
  23. if user:
  24. uq = await u.user_quota_mb(os.environ['AUTH_LOGIN_USER'])
  25. ugroups = await u.user_groups(os.environ['AUTH_LOGIN_USER'])
  26. return (os.environ['AUTH_LOGIN_USER'], password, scheme, uq, ugroups)
  27. else:
  28. return False
  29. else:
  30. user = await u.user_by_email(username)
  31. if user:
  32. uq = await u.user_quota_mb(username)
  33. ugroups = await u.user_groups(username)
  34. return (username, password, scheme, uq, ugroups)
  35. else:
  36. return False
  37. async def credentials_verify(username, password, scheme):
  38. u = ldap.UserDB()
  39. if os.environ.get('AUTH_LOGIN_USER', None) and os.environ.get('AUTH_LOGIN_USER', None) != os.environ.get('AUTH_ORIG_USER', None): # Masteruser login
  40. if await u.check_auth(os.environ['AUTH_ORIG_USER'], password, master=True):
  41. uq = await u.user_quota_mb(os.environ['AUTH_LOGIN_USER'])
  42. ugroups = await u.user_groups(os.environ['AUTH_LOGIN_USER'])
  43. return (os.environ['AUTH_LOGIN_USER'], password, scheme, uq, ugroups)
  44. else:
  45. return False
  46. if await u.check_auth(username, password):
  47. uq = await u.user_quota_mb(username)
  48. ugroups = await u.user_groups(username)
  49. return (username, password, scheme, uq, ugroups)
  50. else:
  51. return False
  52. if __name__ == '__main__':
  53. if len(sys.argv) != 2:
  54. print(f'usage:\n\tprintf \'%s\\x0%s\\x0\' <user> <password> | {sys.argv[0]} test; echo "$?"')
  55. exit(1)
  56. chpw_repl_bin = sys.argv[1]
  57. if chpw_repl_bin == 'test':
  58. chpw_repl_bin = '/usr/bin/test'
  59. input_fd = 0
  60. else:
  61. input_fd = 3
  62. inp_f = os.fdopen(input_fd)
  63. cred_data = inp_f.read(80000)
  64. inp_f.close()
  65. username, password = cred_data.split('\x00')[:2]
  66. username = username.lower()
  67. if os.environ.get('AUTH_LOGIN_USER', None) and os.environ.get('AUTH_LOGIN_USER', None) != os.environ.get('AUTH_ORIG_USER', None): # Masteruser login
  68. os.environ['USER'] = os.environ['AUTH_LOGIN_USER'].lower()
  69. else:
  70. os.environ['USER'] = username.lower()
  71. try:
  72. loc, dom = username.split('@')
  73. except ValueError:
  74. sys.exit(ERR_NOUSER)
  75. os.environ['HOME'] = f'/var/mail/{dom.lower()}/{loc.lower()}'
  76. if int(os.environ.get('CREDENTIALS_LOOKUP', 0)) == 1:
  77. action = credentials_lookup
  78. elif int(os.environ.get('AUTHORIZED', 0)) == 1:
  79. action = user_lookup
  80. else:
  81. action = credentials_verify
  82. try:
  83. if uvloop_exist:
  84. uvloop.install()
  85. lookup_result = asyncio.run(action(username, password, os.getenv('SCHEME', None)))
  86. except:
  87. print(sys.exc_info())
  88. exit(ERR_TEMPFAIL)
  89. if lookup_result:
  90. ex_code = ERR_OK
  91. if int(os.environ.get('AUTHORIZED', 0)) == 1:
  92. os.environ['AUTHORIZED'] = '2'
  93. ex_code = ERR_LOOKUP_OK
  94. if int(os.environ.get('CREDENTIALS_LOOKUP', 0)) == 1:
  95. found_password = lookup_result[1]
  96. scheme = lookup_result[2]
  97. os.environ['password'] = f'{{{scheme}}}{found_password}'
  98. os.environ['EXTRA'] = f'password {os.environ.get("EXTRA", "")}'
  99. os.environ['userdb_uid'] = 'dovecot'
  100. os.environ['userdb_gid'] = 'dovecot'
  101. env_extra = ['userdb_uid', 'userdb_gid']
  102. uq = lookup_result[3]
  103. if uq:
  104. os.environ['userdb_quota_rule'] = f'*:storage={uq}M'
  105. env_extra.append('userdb_quota_rule')
  106. ugroups = lookup_result[4]
  107. if len(ugroups):
  108. os.environ['userdb_acl_groups'] = ','.join(ugroups)
  109. env_extra.append('userdb_acl_groups')
  110. os.environ['EXTRA'] = ' '.join(env_extra)
  111. os.system(chpw_repl_bin)
  112. sys.exit(ex_code)
  113. else:
  114. if int(os.environ.get('AUTHORIZED', 0)) == 1 and int(os.environ.get('CREDENTIALS_LOOKUP', 0)) == 1:
  115. sys.exit(ERR_NOUSER)
  116. else:
  117. sys.exit(ERR_PERMFAIL)