def login(ra_name, user, secret): """Attempt to Authenitcate user using LDAP :param ra_name: name of registration authority :param user: Username :param secret: Secret/Passphrase :returns: AuthDetails -- Class used for authentication information """ conf = jsonloader.authentication_for_registration_authority(ra_name) ldap_port = int(conf.get('port', 389)) use_ssl = conf.get('ssl', ldap_port == 636) lds = ldap3.Server(conf['host'], port=ldap_port, get_info=ldap3.ALL, use_ssl=use_ssl) try: ldap_user = "******" % (user, conf['domain']) ldc = ldap3.Connection(lds, auto_bind=True, client_strategy=ldap3.SYNC, user=ldap_user, password=secret, authentication=ldap3.SIMPLE, check_names=True) filter_str = ('(sAMAccountName=%s)' % ldap3.utils.conv.escape_bytes(user)) ldc.search(conf['base'], filter_str, ldap3.SUBTREE, attributes=['memberOf']) if ldc.result['result'] != 0: return None user_attrs = ldc.response[0]['attributes'] user_groups = user_get_groups(user_attrs) return results.AuthDetails(username=user, groups=user_groups) except ldap3.LDAPSocketOpenError: logger.error("cannot connect to LDAP host '%s' (authority '%s')", conf['host'], ra_name) return None except ldap3.LDAPBindError: logger.info("failed ldap auth for user %s", user) return None
def login(ra_name, user, secret): """Validates a user supplied user/password against an expected value. The expected value is pulled from the pecan config. Note that this value is currently stored in the clear inside that config, so we are assuming that the config is protected using file perms, etc. This function provides some resistance to timing attacks, but information on the expected user/password lengths can still be leaked. It may also be possible to use a timing attack to see which input failed validation. See comments below for details. :param ra_name: name of the registration authority :param user: The user supplied username (unicode or string) :param secret: The user supplied password (unicode or string) :return: None on failure or an AuthDetails object on success """ auth_conf = jsonloader.authentication_for_registration_authority(ra_name) # convert input to strings user = str(user) secret = str(secret) # expected values try: expected_user = str(auth_conf['user']) expected_secret = str(auth_conf['secret']) except (KeyError, TypeError): logger.warning("auth conf missing static user or secret") return None # This technique is used to provide a constant time string compare # between the user input and the expected values. valid_user = util.constant_time_compare(user, expected_user) valid_secret = util.constant_time_compare(secret, expected_secret) # This if statement results in a potential timing attack where the # statement could return more quickly if valid_secret=False. We # do not see an obvious solution to this problem, but also believe # that leaking which input was valid isn't as big of a concern. if valid_user and valid_secret: return results.AuthDetails(username=expected_user, groups=[]) logger.info("failed static auth for user {}".format(user))
def login(ra_name, user, secret): """Validates a user supplied user/password against an expected value. The expected value is pulled from the pecan config. Note that this value is currently stored in the clear inside that config, so we are assuming that the config is protected using file perms, etc. This function provides some resistance to timing attacks, but information on the expected user/password lengths can still be leaked. It may also be possible to use a timing attack to see which input failed validation. See comments below for details. :param ra_name: name of the registration authority :param user: The user supplied username (unicode or string) :param secret: The user supplied password (unicode or string) :return: None on failure or an AuthDetails object on success """ auth_conf = jsonloader.authentication_for_registration_authority(ra_name) # convert input to strings user = str(user) secret = str(secret) # expected values try: expected_user = str(auth_conf['user']) expected_secret = str(auth_conf['secret']) except (KeyError, TypeError): logger.warning("auth conf missing static user or secret") return None # This technique is used to provide a constant time string compare # between the user input and the expected values. valid_user = util.constant_time_compare(user, expected_user) valid_secret = util.constant_time_compare(secret, expected_secret) # This if statement results in a potential timing attack where the # statement could return more quickly if valid_secret=False. We # do not see an obvious solution to this problem, but also believe # that leaking which input was valid isn't as big of a concern. if valid_user and valid_secret: return results.AuthDetails(username=expected_user, groups=[]) logger.info("failed static auth for user {}".format(user))
def validate(ra_name, user, secret): """Top-level authN entry point. This will return an AuthDetails object or abort. This will only check that a single auth method. That method will either succeed or fail. :param ra_name: name of the registration authority :param user: user provided user name :param secret: user provided secret (password or token) :return: AuthDetails if authenticated or aborts """ auth_conf = jsonloader.authentication_for_registration_authority(ra_name) backend_name = auth_conf['backend'] backend = jsonloader.conf.get_authentication(backend_name) res = backend(ra_name, user, secret) if res: return res # we should only get here if a module failed to abort pecan.abort(401, "authentication failure")
def login(ra_name, user, secret): """Attempt to Authenitcate user using LDAP :param ra_name: name of registration authority :param user: Username :param secret: Secret/Passphrase :returns: AuthDetails -- Class used for authentication information """ conf = jsonloader.authentication_for_registration_authority(ra_name) ldap_port = int(conf.get('port', 389)) use_ssl = conf.get('ssl', ldap_port == 636) lds = ldap3.Server(conf['host'], port=ldap_port, get_info=ldap3.ALL, use_ssl=use_ssl) try: ldap_user = "******" % (user, conf['domain']) ldc = ldap3.Connection(lds, auto_bind=True, client_strategy=ldap3.SYNC, user=ldap_user, password=secret, authentication=ldap3.SIMPLE, check_names=True) filter_str = ('(sAMAccountName=%s)' % ldap3.utils.conv.escape_bytes(user)) ldc.search(conf['base'], filter_str, ldap3.SUBTREE, attributes=['memberOf']) if ldc.result['result'] != 0: return None user_attrs = ldc.response[0]['attributes'] user_groups = user_get_groups(user_attrs) return results.AuthDetails(username=user, groups=user_groups) except ldap3.LDAPSocketOpenError: logger.error("cannot connect to LDAP host '%s' (authority '%s')", conf['host'], ra_name) return None except ldap3.LDAPBindError: logger.info("failed ldap auth for user %s", user) return None