Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions XSConsole.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,15 @@ def main():
if '--shelltimeout' in sys.argv:
# Print a shell timeout value, suitable for TMOUT=`xsconsole --shelltimeout`
if Config.Inst().AllShellsTimeout():
print State.Inst().AuthTimeoutSeconds()
print(State.Inst().AuthTimeoutSeconds())
else:
print
print()
else:
app = App.Inst()
app.Build( ['plugins-base', 'plugins-oem', 'plugins-extras'] )
try:
app.Enter()
except Exception, e:
except Exception as e:
# it may be that the screen size has changed
app.AssertScreenSize()
# if we get here then it was some other problem
Expand All @@ -44,7 +44,7 @@ def main():
if __name__ == "__main__":
try:
main()
except Exception, e:
except Exception as e:
# Add backtrace to log
try:
trace = traceback.format_tb(sys.exc_info()[2])
Expand Down
38 changes: 8 additions & 30 deletions XSConsoleAuth.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@

import os, spwd, re, sys, time, socket

try:
import PAM # From PyPAM RPM
except ImportError:
import pam as PAM # From pip install python-pam
import pam

from XSConsoleBases import *
from XSConsoleLang import *
Expand Down Expand Up @@ -109,26 +106,7 @@ def TCPAuthenticate(self, inUsername, inPassword):
session.close()

def PAMAuthenticate(self, inUsername, inPassword):

def PAMConv(inAuth, inQueryList, *theRest):
# *theRest consumes the userData argument from later versions of PyPAM
retVal = []
for query in inQueryList:
if query[1] == PAM.PAM_PROMPT_ECHO_ON or query[1] == PAM.PAM_PROMPT_ECHO_OFF:
# Return inPassword from the scope that encloses this function
retVal.append((inPassword, 0)) # Append a tuple with two values (so double brackets)
return retVal

auth = PAM.pam()
auth.start('passwd')
auth.set_item(PAM.PAM_USER, inUsername)
auth.set_item(PAM.PAM_CONV, PAMConv)

try:
auth.authenticate()
auth.acct_mgmt()
# No exception implies a successful login
except Exception, e:
if not pam.authenticate(inUsername, inPassword):
Copy link
Collaborator

@bernhardkaindl bernhardkaindl Oct 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes PAM Authentication (for sure on XS8, please test for XS9 too):

Suggested change
if not pam.authenticate(inUsername, inPassword):
if not pam.authenticate(inUsername, inPassword, service="passwd"):

It was really easy to find using Googling '"pam.authentication" False python':

The default argument is: pam.authenticate(user, pass, service="login")

service="login" also checks if the user's tty is in /etc/securetty

On XS8, and (among serial lines, etc) there is only pts/0 in /etc/securetty.

But if you are logged in over SSH, you might get the pty pts/2, for example.

When pam.authenticate(user, pass, service="login") does not find your SSH pty in /etc/securetty, it rejects the login.

Thus, it may happen if you open multiple SSH sessions and also test it on one of the sessions which don't have pts/0, it could fail on XS9 too:

Example command line:

python3 -c 'import pam;print(pam.authenticate("root","root", service="login"))'
False

Adding using service="passwd" is the correct thing to do be in line to what the existing python2 PAM auth does on XenServer so far.

It is possible that the same happens on xs9 too, but you might not have run into it if you only tested xsconsole on the default terminals and never called it on a second or third SSH pty.

Reference: FirefighterBlu3/python-pam#13

# Display a generic message for all failures
raise Exception(Lang("The system could not log you in. Please check your access credentials and try again."))

Expand Down Expand Up @@ -188,7 +166,7 @@ def OpenSession(self):
session = None
self.masterConnectionBroken = True
self.error = 'The master connection has timed out.'
except Exception, e:
except Exception as e:
session = None
self.error = e

Expand All @@ -198,15 +176,15 @@ def OpenSession(self):
try:
session.login_with_password('root', self.defaultPassword,'','XSConsole')

except XenAPI.Failure, e:
except XenAPI.Failure as e:
if e.details[0] != 'HOST_IS_SLAVE': # Ignore slave errors when testing
session = None
self.error = e
except socket.timeout:
session = None
self.masterConnectionBroken = True
self.error = 'The master connection has timed out.'
except Exception, e:
except Exception as e:
session = None
self.error = e
return session
Expand All @@ -218,7 +196,7 @@ def CloseSession(self, inSession):
if inSession._session is not None:
try:
inSession.logout()
except XenAPI.Failure, e:
except XenAPI.Failure as e:
XSLog('XAPI Failed to logout exception was ', e)
return None

Expand All @@ -241,7 +219,7 @@ def ChangePassword(self, inOldPassword, inNewPassword):
if self.IsPasswordSet():
try:
self.PAMAuthenticate('root', inOldPassword)
except Exception, e:
except Exception as e:
raise Exception(Lang('Old password not accepted. Please check your access credentials and try again.'))
self.AssertAuthenticated()

Expand All @@ -252,7 +230,7 @@ def ChangePassword(self, inOldPassword, inNewPassword):
session.xenapi.session.change_password(inOldPassword, inNewPassword)
finally:
self.CloseSession(session)
except Exception, e:
except Exception as e:
ShellPipe("/usr/bin/passwd", "--stdin", "root").Call(inNewPassword)
raise Exception(Lang("The underlying Xen API xapi could not be used. Password changed successfully on this host only."))

Expand Down
4 changes: 2 additions & 2 deletions XSConsoleBases.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
def ParamsToAttr():
d = inspect.currentframe().f_back.f_locals
obj = d.pop("self")
for name, value in d.iteritems():
for name, value in d.items():
setattr(obj, name,value)

def FirstValue(*inArgs):
Expand All @@ -33,7 +33,7 @@ def FirstValue(*inArgs):

class Struct:
def __init__(self, *inArgs, **inKeywords):
for k, v in inKeywords.items():
for k, v in list(inKeywords.items()):
setattr(self, k, v)

def __repr__(self):
Expand Down
13 changes: 7 additions & 6 deletions XSConsoleCurses.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import curses, sys, commands
import curses, sys, subprocess

from XSConsoleBases import *
from XSConsoleConfig import *
from XSConsoleLang import *
from XSConsoleState import *


class CursesPalette:
pairIndex = 1
colours = {}
Expand Down Expand Up @@ -93,7 +94,7 @@ def DefineColours(cls):
'MODAL_SELECTED', 'MODAL_FLASH', 'HELP_BASE', 'HELP_BRIGHT',
'HELP_FLASH', 'TOPLINE_BASE']:
cls.colours[name] = curses.color_pair(0)
for key, value in cls.colours.items():
for key, value in list(cls.colours.items()):
if key.endswith('_SELECTED'):
cls.colours[key] |= curses.A_REVERSE
elif key.endswith('_FLASH'):
Expand Down Expand Up @@ -174,13 +175,13 @@ def ClippedAddStr(self, inString, inX, inY, inColour): # Internal use
if len(clippedStr) > 0:
try:
encodedStr = clippedStr
if isinstance(clippedStr, unicode):
encodedStr = clippedStr.encode('utf-8')
if isinstance(clippedStr, bytes):
encodedStr = clippedStr.decode('utf-8')
# Clear field here since addstr will clear len(encodedStr)-len(clippedStr) too few spaces
self.win.addstr(inY, xPos, len(clippedStr)*' ', CursesPalette.ColourAttr(FirstValue(inColour, self.defaultColour)))
self.win.refresh()
self.win.addstr(inY, xPos, encodedStr, CursesPalette.ColourAttr(FirstValue(inColour, self.defaultColour)))
except Exception, e:
except Exception as e:
if xPos + len(inString) == self.xSize and inY + 1 == self.ySize:
# Curses incorrectly raises an exception when writing the bottom right
# character in a window, but still completes the write, so ignore it
Expand Down Expand Up @@ -212,7 +213,7 @@ def AddWrappedText(self, inString, inX, inY, inColour = None):
yPos += 1

def AddHCentredText(self, inString, inY, inColour = None):
xStart = self.xSize / 2 - len(inString) / 2
xStart = self.xSize // 2 - len(inString) // 2
self.ClippedAddStr(inString, xStart, inY, inColour)

def Decorate(self):
Expand Down
Loading