1#!/usr/local/bin/python3.8 2# -*- coding: utf-8 -*- 3# 4# (c) Copyright @ 2015 HP Development Company, L.P. 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19# 20# Author: Amarnath Chitumalla 21# 22import os 23import getpass 24import time 25import string 26 27from . import utils, tui 28from .g import * 29from .sixext import BytesIO, StringIO 30from .sixext.moves import input 31from . import pexpect 32 33PASSWORD_RETRY_COUNT = 3 34 35AUTH_TYPES = {'mepis': 'su', 36 'debian': 'su', 37 'suse': 'su', 38 'mandriva': 'su', 39 'fedora': 'su', 40 'fedora28': 'sudo', 41 'redhat': 'su', 42 'rhel': 'su', 43 'slackware': 'su', 44 'gentoo': 'su', 45 'redflag': 'su', 46 'ubuntu': 'sudo', 47 'xandros': 'su', 48 'freebsd': 'su', 49 'linspire': 'su', 50 'ark': 'su', 51 'pclinuxos': 'su', 52 'centos': 'su', 53 'igos': 'su', 54 'linuxmint': 'sudo', 55 'linpus': 'sudo', 56 'gos': 'sudo', 57 'boss': 'su', 58 'lfs': 'su', 59 'manjarolinux': 'sudo', 60 } 61 62 63# This function promts for the username and password and returns 64# (username,password) 65def showPasswordPrompt(prompt): 66 import getpass 67 print ("") 68 print ("") 69 print (log.bold(prompt)) 70 username = input("Username: ") 71 password = getpass.getpass("Password: ") 72 73 return (username, password) 74 75 76# TBD this function shoud be removed once distro class implemented 77def get_distro_name(): 78 os_name = None 79 try: 80 import platform 81 except ImportError: 82 os_name = None 83 84 try: 85 os_name = platform.dist()[0] 86 except AttributeError: 87 import distro 88 os_name = distro.linux_distribution()[0] 89 90 if not os_name: 91 name = os.popen('lsb_release -i | cut -f 2') 92 os_name = name.read().strip() 93 name.close() 94 95 if not os_name: 96 name = os.popen("cat /etc/issue | awk '{print $1}' | head -n 1") 97 os_name = name.read().strip() 98 name.close() 99 100 os_name = os_name.lower() 101 if "redhatenterprise" in os_name: 102 os_name = 'rhel' 103 elif "suse" in os_name: 104 os_name = 'suse' 105 elif "arch" in os_name: 106 os_name = 'manjarolinux' 107 108 return os_name 109 110 111class Password(object): 112 113 def __init__(self, Mode=INTERACTIVE_MODE): 114 self.__password = "" 115 self.__password_prompt_str = "" 116 self.__passwordValidated = False 117 self.__mode = Mode 118 self.__readAuthType() # self.__authType 119 self.__expectList = [] 120 121 if not utils.to_bool(sys_conf.get('configure', 'qt5', '0')) and not not utils.to_bool(sys_conf.get('configure', 'qt4', '0')) and utils.to_bool(sys_conf.get('configure', 'qt3', '0')): 122 self.__ui_toolkit = 'qt3' 123 elif not utils.to_bool(sys_conf.get('configure', 'qt5', '0')) and not utils.to_bool(sys_conf.get('configure', 'qt3', '0')) and utils.to_bool(sys_conf.get('configure', 'qt4', '0')): 124 self.__ui_toolkit = 'qt4' 125 elif not utils.to_bool(sys_conf.get('configure', 'qt3', '0')) and not utils.to_bool(sys_conf.get('configure', 'qt4', '0')) and utils.to_bool(sys_conf.get('configure', 'qt5', '0')): 126 self.__ui_toolkit = 'qt5' 127 128 for s in utils.EXPECT_WORD_LIST: 129 try: 130 p = re.compile(s, re.I) 131 except TypeError: 132 self.__expectList.append(s) 133 else: 134 self.__expectList.append(p) 135 136 ##################### Private functions ###################### 137 138 def __readAuthType(self): 139 # TBD: Getting distro name should get distro class 140 # added replace() to remove the spaces in distro_name 141 distro_name = get_distro_name().lower().replace(" ","") 142 143 self.__authType = user_conf.get('authentication', 'su_sudo', '') 144 if self.__authType != "su" and self.__authType != "sudo": 145 try: 146 self.__authType = AUTH_TYPES[distro_name] 147 if distro_name == 'fedora': 148 import platform 149 try: 150 ver = int(platform.dist()[1]) 151 except AttributeError: 152 import distro 153 ver = int(distro.linux_distribution()[1]) 154 if ver >= 28: 155 self.__authType = AUTH_TYPES['fedora28'] 156 except KeyError: 157 log.warn("%s distro is not found in AUTH_TYPES" % distro_name) 158 self.__authType = 'su' 159 160 def __getPasswordDisplayString(self): 161 if self.__authType == "su": 162 return "Please enter the root/superuser password: " 163 else: 164 return "Please enter the sudoer (%s)'s password: " % os.getenv('USER') 165 166 def __changeAuthType(self): 167 if self.__authType == "sudo": 168 self.__authType = "su" 169 else: 170 self.__authType = "sudo" 171 user_conf.set('authentication', 'su_sudo', self.__authType) 172 173 def __get_password(self, pswd_msg=''): 174 if pswd_msg == '': 175 if self.__authType == "su": 176 pswd_msg = "Please enter the root/superuser password: " 177 else: 178 pswd_msg = "Please enter the sudoer (%s)'s password: " % os.getenv( 179 'USER') 180 return getpass.getpass(log.bold(pswd_msg)) 181 182 def __get_password_ui(self, pswd_msg='', user="root"): 183 if pswd_msg == '': 184 pswd_msg = "Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue" 185 186 if self.__ui_toolkit == "qt3": 187 from ui.setupform import showPasswordUI 188 username, password = showPasswordUI(pswd_msg, user, False) 189 elif self.__ui_toolkit == "qt5": 190 from ui5.setupdialog import showPasswordUI 191 username, password = showPasswordUI(pswd_msg, user, False) 192 else: # self.__ui_toolkit == "qt4" --> default qt4 193 from ui4.setupdialog import showPasswordUI 194 username, password = showPasswordUI(pswd_msg, user, False) 195 196 if username == "" and password == "": 197 raise Exception("User Cancel") 198 199 return password 200 201 def __password_check(self, cmd, timeout=10): 202 import io 203 output = io.StringIO() 204 ok, ret = False, '' 205 206 try: 207 child = pexpect.spawnu(cmd, timeout=timeout) 208 except pexpect.ExceptionPexpect: 209 return 1, '' 210 211 try: 212 try: 213 start = time.time() 214 215 while True: 216 update_spinner() 217 218 i = child.expect(self.__expectList) 219 220 cb = child.before 221 if cb: 222 start = time.time() 223 output.write(cb) 224 225 if i == 0: # EOF 226 ok, ret = True, output.getvalue() 227 break 228 229 elif i == 1: # TIMEOUT 230 if('true' in cmd and self.__password_prompt_str == ""): # sudo true or su -c "true" 231 cb = cb.replace("[", "\[") 232 cb = cb.replace("]", "\]") 233 234 self.__password_prompt_str = cb 235 try: 236 p = re.compile(cb, re.I) 237 except TypeError: 238 self.__expectList.append(cb) 239 else: 240 self.__expectList.append(p) 241 log.debug( 242 "Adding missing password prompt string [%s]" % self.__password_prompt_str) 243 continue 244 245 else: # password 246 if(self.__password_prompt_str == ""): 247 self.__password_prompt_str = utils.EXPECT_WORD_LIST[ 248 i] 249 log.debug( 250 "Updating password prompt string [%s]" % self.__password_prompt_str) 251 252 child.sendline(self.__password) 253 254 except (Exception, pexpect.ExceptionPexpect) as e: 255 log.exception() 256 257 finally: 258 cleanup_spinner() 259 260 try: 261 child.close() 262 except OSError: 263 pass 264 265 if ok: 266 return child.exitstatus, ret 267 else: 268 269 return 1, '' 270 271 def __validatePassword(self, pswd_msg): 272 x = 1 273 while True: 274 if self.__mode == INTERACTIVE_MODE: 275 self.__password = self.__get_password(pswd_msg) 276 else: 277 try: 278 if self.getAuthType() == 'su': 279 self.__password = self.__get_password_ui( 280 pswd_msg, "root") 281 else: 282 self.__password = self.__get_password_ui( 283 pswd_msg, os.getenv("USER")) 284 except Exception as ex: 285 log.debug(ex) 286 break 287 288 cmd = self.getAuthCmd() % "true" 289 log.debug(cmd) 290 291 status, output = self.__password_check(cmd) 292 log.debug("status = %s output=%s " % (status, output)) 293 294 if self.__mode == GUI_MODE: 295 if self.__ui_toolkit == "qt4": 296 from ui4.setupdialog import FailureMessageUI 297 elif self.__ui_toolkit == "qt5": 298 from ui5.setupdialog import FailureMessageUI 299 elif self.__ui_toolkit == "qt3": 300 from ui.setupform import FailureMessageUI 301 302 if status == 0: 303 self.__passwordValidated = True 304 break 305 elif "not in the sudoers file" in output: 306 # TBD.. IF user doesn't have sudo permissions, needs to change 307 # to "su" type and query for password 308 self.__changeAuthType() 309 msg = "User doesn't have sudo permissions.\nChanging Authentication Type. Try again." 310 if self.__mode == GUI_MODE: 311 FailureMessageUI(msg) 312 else: 313 log.error(msg) 314 raise Exception("User is not in the sudoers file.") 315 316 else: 317 self.__password = "" 318 x += 1 319 if self.__mode == GUI_MODE: 320 if x > PASSWORD_RETRY_COUNT: 321 FailureMessageUI("Password incorrect. ") 322 return 323 else: 324 FailureMessageUI("Password incorrect. %d attempt(s) left." % ( 325 PASSWORD_RETRY_COUNT + 1 - x)) 326 else: 327 if x > PASSWORD_RETRY_COUNT: 328 log.error("Password incorrect. ") 329 return 330 else: 331 log.error("Password incorrect. %d attempt(s) left." % ( 332 PASSWORD_RETRY_COUNT + 1 - x)) 333 334 def __get_password_utils(self): 335 if self.__authType == "su": 336 AuthType, AuthCmd = 'su', 'su -c "%s"' 337 else: 338 AuthType, AuthCmd = 'sudo', 'sudo %s' 339 340 return AuthType, AuthCmd 341 342 def __get_password_utils_ui(self): 343 distro_name = get_distro_name().lower() 344 if self.__authType == "sudo": 345 AuthType, AuthCmd = 'sudo', 'sudo %s' 346 else: 347 AuthType, AuthCmd = 'su', 'su -c "%s"' 348 ''' 349 if utils.which('kdesu'): 350 AuthType, AuthCmd = 'kdesu', 'kdesu -- %s' 351 elif utils.which('kdesudo'): 352 AuthType, AuthCmd = 'kdesudo', 'kdesudo -- %s' 353 elif utils.which('gnomesu'): 354 AuthType, AuthCmd = 'gnomesu', 'gnomesu -c "%s"' 355 elif utils.which('gksu'): 356 AuthType, AuthCmd = 'gksu' , 'gksu "%s"' 357 ''' 358 359 return AuthType, AuthCmd 360 361 ##################### Public functions ###################### 362 363 def clearPassword(self): 364 log.debug("Clearing password...") 365 self.__password = "" 366 self.__passwordValidated = False 367 if self.__authType == 'sudo': 368 utils.run("sudo -K") 369 370 def getAuthType(self): 371 if self.__mode == INTERACTIVE_MODE: 372 retValue = self.__authType 373 else: 374 retValue, AuthCmd = self.__get_password_utils_ui() 375 376 return retValue 377 378 def getAuthCmd(self): 379 if self.__mode == INTERACTIVE_MODE: 380 AuthType, AuthCmd = self.__get_password_utils() 381 else: 382 AuthType, AuthCmd = self.__get_password_utils_ui() 383 384 return AuthCmd 385 386 def getPassword(self, pswd_msg='', psswd_queried_cnt=0): 387 if self.__passwordValidated: 388 return self.__password 389 390 if psswd_queried_cnt: 391 return self.__password 392 393 self.__validatePassword(pswd_msg) 394 return self.__password 395 396 def getPasswordPromptString(self): 397 return self.__password_prompt_str 398