Exemple #1
0
    def login_from_args(self, args, print_status=True):
        result = None

        if not args.anonymous and not args.user:
            last = UserDataFile('client/lastuser')

            if last.exists():
                args.user = last.read_full()

        if args.anonymous or not args.user:
            self._LOG.info("Attempting anonymous login")
            return self.anonymous_login()

        if args.user:
            self._LOG.info("Attempting login as: %s" % args.user)
            self.username = args.user

            userkey = UserDataFile('client/%s.key' % self.username)
            if userkey.exists():
                self.login_key = userkey.read_full()
                result = self.relogin()

                if result == EResult.InvalidPassword:
                    self._LOG.info("Remembered login has expired")
                    userkey.remove()

            if not self.logged_on:
                result = self.cli_login(self.user)

        return result
Exemple #2
0
def cmd_webapi_set(args):
    keyfile = UserDataFile('apikey.txt', 'w')

    if args.key:
        with keyfile as fp:
            fp.write(args.key)

    if keyfile.exists():
        print("Current key:", keyfile.read_full())
    else:
        print("Current key: NOT SET")
Exemple #3
0
def cmd_authenticator_status(args):
    account = args.account.lower().strip()
    secrets_file = UserDataFile('authenticator/{}.json'.format(account))
    sa = None

    wa = BetterMWA(account)

    if secrets_file.exists():
        sa = SteamAuthenticator(secrets_file.read_json(), backend=wa)

    try:
        wa.bcli_login(sa_instance=sa)
    except (KeyboardInterrupt, EOFError):
        print("Login interrupted")
        return 1  # error

    if sa is None:
        sa = SteamAuthenticator(backend=wa)

    status = sa.status()

    print("----- Status ------------")
    mode = status['steamguard_scheme']

    if mode == 0:
        print("Steam Guard mode: disabled/insecure")
    elif mode == 1:
        print("Steam Guard mode: enabled (email)")
    elif mode == 2:
        print("Steam Guard mode: enabled (authenticator)")
    else:
        print("Steam Guard mode: unknown ({})".format(mode))

    print("Authenticator enabled:", "Yes" if status['state'] == 1 else "No")
    print("Authenticator allowed:", "Yes" if status['state'] else "No")
    print("Email verified:", "Yes" if status['email_validated'] else "No")
    print("External allowed:",
          "Yes" if status['allow_external_authenticator'] else "No")

    if status['state'] == 1:
        print("----- Token details -----")
        print("Token GID:", status['token_gid'])
        print("Created at:", fmt_datetime(status['time_created']))
        print("Device identifier:", status['device_identifier'])
        print("Classified agent:", status['classified_agent'])
        print("Revocation attempts remaining:",
              status['revocation_attempts_remaining'])
Exemple #4
0
def cmd_authenticator_code(args):
    account = args.account.lower().strip()
    secrets = UserDataFile('authenticator/{}.json'.format(account)).read_json()

    if not secrets:
        print("No authenticator for %r" % account)
        return 1  # error

    print(SteamAuthenticator(secrets).get_code())
Exemple #5
0
def cmd_authenticator_code(args):
    account = args.account.lower().strip()
    secrets = UserDataFile('authenticator/{}.json'.format(account)).read_json()

    if not secrets:
        print("No authenticator for %r" % account)
        return 1  # error

    print(
        generate_twofactor_code_for_time(b64decode(secrets['shared_secret']),
                                         time()))
Exemple #6
0
    def save_cache(self):
        cached_depot_keys = self.get_cached_depot_keys()

        if cached_depot_keys == self.depot_keys:
            return

        self.depot_keys.update(cached_depot_keys)
        out = {str(depot_id): key.hex()
               for depot_id, key in self.depot_keys.items()
               }

        UserDataFile('depot_keys.json').write_json(out)
Exemple #7
0
    def fetch_content_servers(self, *args, **kwargs):
        cached_cs = UserDataFile('cs_servers.json')

        data = cached_cs.read_json()

        # load from cache, only keep for 5 minutes
        if data and (data['timestamp'] + 300) > time():
            for server in data['servers']:
                entry = ContentServer()
                entry.__dict__.update(server)
                self.servers.append(entry)
            return

        # fetch cs servers
        CDNClient.fetch_content_servers(self, *args, **kwargs)

        # cache cs servers
        data = {
            "timestamp": int(time()),
            "cell_id": self.cell_id,
            "servers": list(map(lambda x: x.__dict__, self.servers)),
        }

        cached_cs.write_json(data)
Exemple #8
0
def cmd_authenticator_qrcode(args):
    account = args.account.lower().strip()
    secrets = UserDataFile('authenticator/{}.json'.format(account)).read_json()

    if not secrets:
        print("No authenticator for %r" % account)
        return 1  # error

    import pyqrcode
    from base64 import b64decode, b32encode

    if args.invert:
        FG, BG = '0', '1'
    else:
        FG, BG = '1', '0'

    charmap = {
        (BG, BG): '█',
        (FG, FG): ' ',
        (BG, FG): '▀',
        (FG, BG): '▄',
    }

    if args.compat:
        uri = 'otpauth://totp/steamctl:{user}?secret={secret}&issuer=Steam&digits=5'
    else:
        uri = 'otpauth://steam/steamctl:{user}?secret={secret}&issuer=Steam'

    uri = uri.format(
        user=secrets['account_name'],
        secret=b32encode(b64decode(secrets['shared_secret'])).decode('ascii'),
    )

    qrlines = pyqrcode.create(uri, error='M').text(1).split('\n')[:-1]

    print("Suggested 2FA App: Aegis, andOTP")
    print("Scan the QR code below:")

    for y in range(0, len(qrlines), 2):
        for x in range(0, len(qrlines[y])):
            print(charmap[(qrlines[y][x],
                           FG if y + 1 >= len(qrlines) else qrlines[y +
                                                                    1][x])],
                  end='')
        print()
Exemple #9
0
def cmd_authenticator_remove(args):
    account = args.account.lower().strip()
    secrets_file = UserDataFile('authenticator/{}.json'.format(account))
    secrets = secrets_file.read_json()

    if not secrets:
        print("No authenticator found for %r" % account)
        return 1  #error

    if args.force:
        secrets_file.remove()
        print("Forceful removal of %r successful" % account)
        return

    print("To remove an authenticator, first we need to login to Steam")
    print("Account name:", account)

    wa = BetterMWA(account)
    sa = SteamAuthenticator(secrets, backend=wa)

    try:
        wa.bcli_login(sa_instance=sa)
    except (KeyboardInterrupt, EOFError):
        print("Login interrupted")
        return 1  # error

    print("Login successful.")
    print("Steam Guard will be set to email, after removal.")

    while True:
        if not pmt_confirmation("Proceed with removing Steam Authenticator?"):
            break
        else:
            try:
                sa.remove()
            except SteamAuthenticatorError as exp:
                print("Removal error: %s" % exp)
                continue
            except (EOFError, KeyboardInterrupt):
                break
            else:
                secrets_file.remove()
                print("Removal successful!")
                return

    print("Removal cancelled.")
Exemple #10
0
    def _handle_login_key(self, message):
        SteamClient._handle_login_key(self, message)

        with UserDataFile('client/%s.key' % self.username).open('w') as fp:
            fp.write(self.login_key)
Exemple #11
0
 def get_cached_depot_keys(self):
     return {
         int(depot_id): bytes.fromhex(key)
         for depot_id, key in (
             UserDataFile('depot_keys.json').read_json() or {}).items()
     }
Exemple #12
0
def cmd_authenticator_add(args):
    account = args.account.lower().strip()
    secrets_file = UserDataFile('authenticator/{}.json'.format(account))
    sa = None

    if secrets_file.exists():
        if not args.force:
            print(
                "There is already an authenticator for that account. Use --force to overwrite"
            )
            return 1  # error
        sa = SteamAuthenticator(secrets_file.read_json())

    print("To add an authenticator, first we need to login to Steam")
    print("Account name:", account)

    wa = BetterMWA(account)
    try:
        wa.bcli_login(sa_instance=sa)
    except (KeyboardInterrupt, EOFError):
        print("Login interrupted")
        return 1  # error

    print("Login successful. Checking pre-conditions...")

    sa = SteamAuthenticator(backend=wa)

    status = sa.status()
    _LOG.debug("Authenticator status: %s", status)

    if not status['email_validated']:
        print("Account needs a verified email address")
        return 1  # error

    if status['state'] == 1:
        print("This account already has an authenticator.")
        print("You need to remove it first, before proceeding")
        return 1  # error

    if not status['authenticator_allowed']:
        print("This account is now allowed to have authenticator")
        return 1  # error

    # check phone number, and add one if its missing
    if not sa.has_phone_number():
        print("No phone number on this account. This is required.")

        if pmt_confirmation("Do you want to add a phone number?",
                            default_yes=True):
            print("Phone number need to include country code and no spaces.")

            while True:
                phnum = pmt_input("Enter phone number:",
                                  regex=r'^(\+|00)[0-9]+$')

                resp = sa.validate_phone_number(phnum)
                _LOG.debug("Phone number validation for %r: %s", phnum, resp)

                if not resp.get('is_valid', False):
                    print("That number is not valid for Steam.")
                    continue

                if not sa.add_phone_number(phnum):
                    print("Failed to add phone number!")
                    continue

                print("Phone number added. Confirmation SMS sent.")

                while not sa.confirm_phone_number(
                        pmt_input("Enter SMS code:", regex='^[0-9]+$')):
                    print("Code was incorrect. Try again.")

                break
        else:
            # user declined adding a phone number, we cant proceed
            return 1  # error

    # being adding authenticator setup
    sa.add()

    _LOG.debug("Authenticator secrets obtained. Saving to disk")

    secrets_file.write_json(sa.secrets)

    # Setup Steam app in conjuction
    if pmt_confirmation("Do you want to use Steam app too?",
                        default_yes=False):
        print("Great! Go and setup Steam Guard in your app.")
        print("Once completed, generate a code and enter it below.")

        showed_fail_info = False
        fail_counter = 0

        while True:
            code = pmt_input(
                "Steam Guard code:",
                regex='^[23456789BCDFGHJKMNPQRTVWXYbcdfghjkmnpqrtvwxy]{5}$')

            # code match
            if sa.get_code() == code.upper():
                break  # success
            # code do not match
            else:
                fail_counter += 1

                if fail_counter >= 3 and not showed_fail_info:
                    showed_fail_info = True
                    print("The codes do not match. This can be caused by:")
                    print("* The code was not entered correctly")
                    print("* Your system time is not synchronized")
                    print("* Steam has made changes to their backend")

                if not pmt_confirmation("Code mismatch. Try again?",
                                        default_yes=True):
                    _LOG.debug("Removing secrets file")
                    secrets_file.remove()
                    return 1  # failed, exit

    # only setup steamctl 2fa
    else:
        print(
            "Authenticator secrets obtained. SMS code for finalization sent.")

        while True:
            code = pmt_input("Enter SMS code:", regex='^[0-9]+$')
            try:
                sa.finalize(code)
            except SteamAuthenticatorError as exp:
                print("Finalization error: %s", exp)
                continue
            else:
                break

    # finish line
    print("Authenticator added successfully!")
    print("Get a code: {} authenticator code {}".format(__appname__, account))
    print("Or QR code: {} authenticator qrcode {}".format(
        __appname__, account))
Exemple #13
0
def cmd_authenticator_add(args):
    account = args.account.lower().strip()
    secrets_file = UserDataFile('authenticator/{}.json'.format(account))

    if secrets_file.exists():
        print("There is already an authenticator for that account")
        return 1  # error

    print("To add an authenticator, first we need to login to Steam")
    print("Account name:", account)

    wa = BetterMWA(account)
    try:
        wa.bcli_login()
    except (KeyboardInterrupt, EOFError):
        print("Login interrupted")
        return 1  # error

    print("Login successful. Checking pre-conditions...")

    sa = SteamAuthenticator(backend=wa)

    # check phone number, and add one if its missing
    if not sa.has_phone_number():
        print("No phone number on this account. This is required.")

        if pmt_confirmation("Do you want to add a phone number?", default_yes=True):
            print("Phone number need to include country code and no spaces.")

            while True:
                phnum = pmt_input("Enter phone number:", regex=r'^(\+|00)[0-9]+$')

                resp = sa.validate_phone_number(phnum)
                _LOG.debug("Phone number validation for %r: %s", phnum, resp)

                if not resp.get('is_valid', False):
                    print("That number is not valid for Steam.")
                    continue

                if not sa.add_phone_number(phnum):
                    print("Failed to add phone number!")
                    continue

                print("Phone number added. Confirmation SMS sent.")

                while not sa.confirm_phone_number(pmt_input("Enter SMS code:", regex='^[0-9]+$')):
                    print("Code was incorrect. Try again.")

                break
        else:
            # user declined adding a phone number, we cant proceed
            return 1  # error

    # being adding authenticator setup
    sa.add()

    _LOG.debug("Authenticator secrets obtained. Saving to disk")

    secrets_file.write_json(sa.secrets)

    print("Authenticator secrets obtained. SMS code for finalization sent.")

    while True:
        code = pmt_input("Enter SMS code:", regex='^[0-9]+$')
        try:
            sa.finalize(code)
        except SteamAuthenticatorError as exp:
            print("Finalization error: %s", exp)
            continue
        else:
            break

    # finish line
    print("Authenticator added successfully!")
    print("To get a code run: {} authenticator code {}".format(__appname__, account))
Exemple #14
0
def get_webapi_key():
    return UserDataFile('apikey.txt').read_full()
Exemple #15
0
    def login_from_args(self, args, print_status=True):
        result = None

        # anonymous login
        if args.anonymous and not args.user:
            self._LOG.info("Attempting anonymous login")
            return self.anonymous_login()

        # user login
        user = args.user
        lastFile = UserDataFile('client/lastuser')

        # check for previously used user
        if not user and lastFile.exists():
            user = lastFile.read_text()

            if user:
                self._LOG.info("Reusing previous username: %s", user)
                self._LOG.info(
                    "Hint: use 'steamctl --user <username> ...' to change")
            else:
                self._LOG.debug("lastuser file is empty?")
                lastFile.remove()

        if user:
            # attempt login
            self.username = user

            if not lastFile.exists() or lastFile.read_text() != self.username:
                lastFile.write_text(self.username)

            # check for userkey and login without a prompt
            userkey = UserDataFile('client/%s.key' % self.username)
            if userkey.exists():
                self._LOG.info("Attempting login with remembered credentials")
                self.login_key = userkey.read_text()
                result = self.relogin()

                self._LOG.debug("Re-login result is: %s",
                                repr(EResult(result)))

                if result == EResult.InvalidPassword:
                    self._LOG.info("Remembered credentials have expired")
                    userkey.remove()
                else:
                    return result

            self.sleep(0.1)

        # attempt manual cli login
        if not self.logged_on:
            if self.username:
                self._LOG.info("Enter credentials for: %s", self.username)
            else:
                self._LOG.info("Enter Steam login")

            result = self.cli_login(self.username)

        if not lastFile.exists() or lastFile.read_text() != self.username:
            lastFile.write_text(self.username)

        self._LOG.debug("Login result is: %s", repr(EResult(result)))

        if not self.relogin_available:
            self.wait_event(self.EVENT_NEW_LOGIN_KEY, timeout=10)

        return result
Exemple #16
0
def cmd_webapi_clear(args):
    UserDataFile('apikey.txt').remove()