Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
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.

cred_unix.py 5.8KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. # -*- test-case-name: twisted.test.test_strcred -*-
  2. #
  3. # Copyright (c) Twisted Matrix Laboratories.
  4. # See LICENSE for details.
  5. """
  6. Cred plugin for UNIX user accounts.
  7. """
  8. from zope.interface import implementer
  9. from twisted import plugin
  10. from twisted.cred.checkers import ICredentialsChecker
  11. from twisted.cred.credentials import IUsernamePassword
  12. from twisted.cred.error import UnauthorizedLogin
  13. from twisted.cred.strcred import ICheckerFactory
  14. from twisted.internet import defer
  15. def verifyCryptedPassword(crypted, pw):
  16. """
  17. Use L{crypt.crypt} to Verify that an unencrypted
  18. password matches the encrypted password.
  19. @param crypted: The encrypted password, obtained from
  20. the Unix password database or Unix shadow
  21. password database.
  22. @param pw: The unencrypted password.
  23. @return: L{True} if there is successful match, else L{False}.
  24. @rtype: L{bool}
  25. """
  26. try:
  27. import crypt
  28. except ImportError:
  29. crypt = None
  30. if crypt is None:
  31. raise NotImplementedError("cred_unix not supported on this platform")
  32. if isinstance(pw, bytes):
  33. pw = pw.decode("utf-8")
  34. if isinstance(crypted, bytes):
  35. crypted = crypted.decode("utf-8")
  36. try:
  37. crypted_check = crypt.crypt(pw, crypted)
  38. if isinstance(crypted_check, bytes):
  39. crypted_check = crypted_check.decode("utf-8")
  40. return crypted_check == crypted
  41. except OSError:
  42. return False
  43. @implementer(ICredentialsChecker)
  44. class UNIXChecker:
  45. """
  46. A credentials checker for a UNIX server. This will check that
  47. an authenticating username/password is a valid user on the system.
  48. Does not work on Windows.
  49. Right now this supports Python's pwd and spwd modules, if they are
  50. installed. It does not support PAM.
  51. """
  52. credentialInterfaces = (IUsernamePassword,)
  53. def checkPwd(self, pwd, username, password):
  54. """
  55. Obtain the encrypted password for C{username} from the Unix password
  56. database using L{pwd.getpwnam}, and see if it it matches it matches
  57. C{password}.
  58. @param pwd: Module which provides functions which
  59. access to the Unix password database.
  60. @type pwd: C{module}
  61. @param username: The user to look up in the Unix password database.
  62. @type username: L{unicode}/L{str} or L{bytes}
  63. @param password: The password to compare.
  64. @type username: L{unicode}/L{str} or L{bytes}
  65. """
  66. try:
  67. if isinstance(username, bytes):
  68. username = username.decode("utf-8")
  69. cryptedPass = pwd.getpwnam(username).pw_passwd
  70. except KeyError:
  71. return defer.fail(UnauthorizedLogin())
  72. else:
  73. if cryptedPass in ("*", "x"):
  74. # Allow checkSpwd to take over
  75. return None
  76. elif verifyCryptedPassword(cryptedPass, password):
  77. return defer.succeed(username)
  78. def checkSpwd(self, spwd, username, password):
  79. """
  80. Obtain the encrypted password for C{username} from the
  81. Unix shadow password database using L{spwd.getspnam},
  82. and see if it it matches it matches C{password}.
  83. @param spwd: Module which provides functions which
  84. access to the Unix shadow password database.
  85. @type spwd: C{module}
  86. @param username: The user to look up in the Unix password database.
  87. @type username: L{unicode}/L{str} or L{bytes}
  88. @param password: The password to compare.
  89. @type username: L{unicode}/L{str} or L{bytes}
  90. """
  91. try:
  92. if isinstance(username, bytes):
  93. username = username.decode("utf-8")
  94. if getattr(spwd.struct_spwd, "sp_pwdp", None):
  95. # Python 3
  96. cryptedPass = spwd.getspnam(username).sp_pwdp
  97. else:
  98. # Python 2
  99. cryptedPass = spwd.getspnam(username).sp_pwd
  100. except KeyError:
  101. return defer.fail(UnauthorizedLogin())
  102. else:
  103. if verifyCryptedPassword(cryptedPass, password):
  104. return defer.succeed(username)
  105. def requestAvatarId(self, credentials):
  106. username, password = credentials.username, credentials.password
  107. try:
  108. import pwd
  109. except ImportError:
  110. pwd = None
  111. if pwd is not None:
  112. checked = self.checkPwd(pwd, username, password)
  113. if checked is not None:
  114. return checked
  115. try:
  116. import spwd
  117. except ImportError:
  118. spwd = None
  119. if spwd is not None:
  120. checked = self.checkSpwd(spwd, username, password)
  121. if checked is not None:
  122. return checked
  123. # TODO: check_pam?
  124. # TODO: check_shadow?
  125. return defer.fail(UnauthorizedLogin())
  126. unixCheckerFactoryHelp = """
  127. This checker will attempt to use every resource available to
  128. authenticate against the list of users on the local UNIX system.
  129. (This does not support Windows servers for very obvious reasons.)
  130. Right now, this includes support for:
  131. * Python's pwd module (which checks /etc/passwd)
  132. * Python's spwd module (which checks /etc/shadow)
  133. Future versions may include support for PAM authentication.
  134. """
  135. @implementer(ICheckerFactory, plugin.IPlugin)
  136. class UNIXCheckerFactory:
  137. """
  138. A factory for L{UNIXChecker}.
  139. """
  140. authType = "unix"
  141. authHelp = unixCheckerFactoryHelp
  142. argStringFormat = "No argstring required."
  143. credentialInterfaces = UNIXChecker.credentialInterfaces
  144. def generateChecker(self, argstring):
  145. """
  146. This checker factory ignores the argument string. Everything
  147. needed to generate a user database is pulled out of the local
  148. UNIX environment.
  149. """
  150. return UNIXChecker()
  151. theUnixCheckerFactory = UNIXCheckerFactory()