甲骨文.使用 LDAP 进行身份验证总是返回 -16 [英] ORACLE. Authenticating with LDAP always returns -16

查看:32
本文介绍了甲骨文.使用 LDAP 进行身份验证总是返回 -16的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们正在使用来自 Oracle 数据库的 LDAP 进行用户身份验证.但是,身份验证结果始终为 -16,在我见过的某些示例中,它被转换为无效凭据.我已经使用 DBMS_LDAP.search_s 搜索了 Active Directory,并确保我输入的 DN 是正确的.有趣的是,DBMS_LDAP.SIMPLE_BIND_S,密码不正确绑定失败,绑定好的密码.但是,当尝试使用 DBMS_LDAP_UTL.AUTHENTICATE_USER 时,如果密码不正确,DN 不正确,它总是返回 -16.这是身份验证示例:

We're doing user authentication with LDAP from Oracle database. However authentication result is always -16 which in some examples i've seen is translated to Invalid credentials. I've searched Active directory with DBMS_LDAP.search_s and made sure that the DN i've entered is correct. Interesting thing is that the DBMS_LDAP.SIMPLE_BIND_S, with incorrect password fails to bind, with good one it binds. However when trying the same with DBMS_LDAP_UTL.AUTHENTICATE_USER, with incorrect password, incorrect DN, it always returns -16. Here's authentication example:

DECLARE
 ldap_host      VARCHAR2(256);
 ldap_port      PLS_INTEGER;
 ldap_user      VARCHAR2(256);
 ldap_passwd    VARCHAR2(256);
 ldap_base      VARCHAR2(256);

 retval              PLS_INTEGER;
 my_session          DBMS_LDAP.session;

 subscriber_handle   DBMS_LDAP_UTL.HANDLE;
 sub_type            PLS_INTEGER;
 subscriber_id       VARCHAR2(2000);

 my_pset_coll        DBMS_LDAP_UTL.PROPERTY_SET_COLLECTION;
 my_property_names   DBMS_LDAP.STRING_COLLECTION;
 my_property_values  DBMS_LDAP.STRING_COLLECTION;

 user_handle         DBMS_LDAP_UTL.HANDLE;
 user_id             VARCHAR2(2000);
 user_type           PLS_INTEGER;
 user_password       VARCHAR2(2000);

 my_mod_pset         DBMS_LDAP_UTL.MOD_PROPERTY_SET;


 my_attrs            DBMS_LDAP.STRING_COLLECTION;

 locate raw(4000);

BEGIN


 -- Please customize the following variables as needed

 ldap_host  := 'host' ;
 ldap_port     :=   389;
 ldap_user  := 'CN=UserName1,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
 ldap_passwd   :=   'SamePassword';

 sub_type      :=   DBMS_LDAP_UTL.TYPE_DN;
 subscriber_id :=   'OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
 user_type     :=   DBMS_LDAP_UTL.TYPE_DN;
 user_id       :=   'CN=UserName2,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';;
 user_password :=   'SamePassword';
 -- Choosing exceptions to be raised by DBMS_LDAP library.
 DBMS_LDAP.USE_EXCEPTION := TRUE;

 -----------------------------------------------
 -- Connect to the LDAP server 
 -- and obtain and ld session.
 -----------------------------------------------

 my_session := DBMS_LDAP.init(ldap_host,ldap_port);

 -----------------------------------------------
 -- Bind to the directory
 -- 
 -----------------------------------------------

 retval := DBMS_LDAP.simple_bind_s(my_session,
                                ldap_user, 
                                ldap_passwd);

 ---------------------------------------------------------------------
 -- Create Subscriber Handle
 -- 
 ---------------------------------------------------------------------

 retval := DBMS_LDAP_UTL.create_subscriber_handle(subscriber_handle,
                                            sub_type,
                                            subscriber_id);

 IF retval != DBMS_LDAP_UTL.SUCCESS  THEN
    -- Handle Errors
    DBMS_OUTPUT.PUT_LINE('create_subscriber_handle returns : ' || to_char(retval));
 END IF;

 ---------------------------------------------------------------------
 -- Create User Handle
 -- 
 ---------------------------------------------------------------------

 retval := DBMS_LDAP_UTL.create_user_handle(user_handle,user_type,user_id);

 IF retval != DBMS_LDAP_UTL.SUCCESS  THEN
    -- Handle Errors
    DBMS_OUTPUT.PUT_LINE('create_user_handle returns : ' || to_char(retval));
 END IF;

  ---------------------------------------------------------------------

 dbms_output.put_line(dbms_ldap_utl.locate_subscriber_for_user(
   ld                => my_session,
   user_handle       => user_handle,
   subscriber_handle => locate));

   dbms_output.put_line(DBMS_LDAP_UTL.SUCCESS);


 ---------------------------------------------------------------------
 -- Set user handle properties
 -- (link subscriber to user )
 ---------------------------------------------------------------------

 retval := DBMS_LDAP_UTL.set_user_handle_properties(user_handle,
                                          DBMS_LDAP_UTL.SUBSCRIBER_HANDLE,
                                          subscriber_handle);

 IF retval != DBMS_LDAP_UTL.SUCCESS  THEN
    -- Handle Errors
    DBMS_OUTPUT.PUT_LINE('set_user_handle_properties returns : ' || to_char(retval));
 END IF;

 ---------------------------------------------------------------------
 -- Authenticate User
 ---------------------------------------------------------------------

 retval := DBMS_LDAP_UTL.authenticate_user(my_session,
                                           user_handle,
                                           DBMS_LDAP_UTL.AUTH_SIMPLE,
                                           user_password,
                                           NULL);

 IF retval != DBMS_LDAP_UTL.SUCCESS  THEN
    -- Handle Errors
    DBMS_OUTPUT.PUT_LINE('authenticate_user returns : ' || to_char(retval));
 END IF;

 ---------------------------------------------------------------------
 -- Free handles
 ---------------------------------------------------------------------

 DBMS_LDAP_UTL.free_handle(subscriber_handle);
 DBMS_LDAP_UTL.free_handle(user_handle);


  -- unbind from the directory  
 retval := DBMS_LDAP.unbind_s(my_session);

 IF retval != DBMS_LDAP_UTL.SUCCESS  THEN
    -- Handle Errors
    DBMS_OUTPUT.PUT_LINE('unbind_s returns : ' || to_char(retval));
 END IF;


-- Handle Exceptions
 EXCEPTION
  WHEN OTHERS THEN
   DBMS_OUTPUT.PUT_LINE(' Error code    : ' || TO_CHAR(SQLCODE));
   DBMS_OUTPUT.PUT_LINE(' Error Message : ' || SQLERRM);
   DBMS_OUTPUT.PUT_LINE(' Exception encountered .. exiting');

  END;
/

推荐答案

如果您只是想根据 LDAP 对用户进行身份验证,这很简单:

If you just like to authenticate a user against LDAP it is rather simple:

DECLARE

    ret NUMBER;     
    ld DBMS_LDAP.SESSION;
    SUCCESS INTEGER;

    ldap_host VARCHAR2(100) := 'host';
    ldap_port INTEGER := 389;

    userDn VARCHAR2(100) := 'OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
    userPassword VARCHAR2(100) := 'secret';
    
BEGIN
    DBMS_LDAP.USE_EXCEPTION := TRUE;
    ld := DBMS_LDAP.INIT(ldap_host, ldap_port);
    DBMS_LDAP.USE_EXCEPTION := FALSE;

    SUCCESS := DBMS_LDAP.SIMPLE_BIND_S(ld, userDn, userPassword);
    IF SUCCESS = DBMS_LDAP.SUCCESS AND userPassword IS NOT NULL THEN
        DBMS_OUTPUT.PUT_LINE('Password is valid');
    ELSE
        DBMS_OUTPUT.PUT_LINE('Wrong password');
    END IF;

    ret := DBMS_LDAP.UNBIND_S(ld);

END;

如果你想查询给定用户的属性,你可以看看这个例子:

If you like to query attributes of given user you can have a look at this example:

DECLARE

    SUBTYPE T_USER_ACCOUNT_CONTROL IS INTEGER;
    -- see https://support.microsoft.com/en-us/help/305144/how-to-use-the-useraccountcontrol-flags-to-manipulate-user-account-pro
    SKRIPT                                  CONSTANT T_USER_ACCOUNT_CONTROL := 1;
    ACCOUNTDISABLE                          CONSTANT T_USER_ACCOUNT_CONTROL := 2;
    HOMEDIR_REQUIRED                        CONSTANT T_USER_ACCOUNT_CONTROL := 8;
    LOCKOUT                                     CONSTANT T_USER_ACCOUNT_CONTROL := 16;
    PASSWD_NOTREQD                          CONSTANT T_USER_ACCOUNT_CONTROL := 32;
    PASSWD_CANT_CHANGE                  CONSTANT T_USER_ACCOUNT_CONTROL := 64;
    ENCRYPTED_TEXT_PWD_ALLOWED          CONSTANT T_USER_ACCOUNT_CONTROL := 128;
    TEMP_DUPLICATE_ACCOUNT              CONSTANT T_USER_ACCOUNT_CONTROL := 256;
    NORMAL_ACCOUNT                          CONSTANT T_USER_ACCOUNT_CONTROL := 512;
    INTERDOMAIN_TRUST_ACCOUNT           CONSTANT T_USER_ACCOUNT_CONTROL := 2048;
    WORKSTATION_TRUST_ACCOUNT           CONSTANT T_USER_ACCOUNT_CONTROL := 4096;
    SERVER_TRUST_ACCOUNT                    CONSTANT T_USER_ACCOUNT_CONTROL := 8192;
    DONT_EXPIRE_PASSWORD                    CONSTANT T_USER_ACCOUNT_CONTROL := 65536;
    MNS_LOGON_ACCOUNT                   CONSTANT T_USER_ACCOUNT_CONTROL := 131072;
    SMARTCARD_REQUIRED                  CONSTANT T_USER_ACCOUNT_CONTROL := 262144;
    TRUSTED_FOR_DELEGATION              CONSTANT T_USER_ACCOUNT_CONTROL := 524288;
    NOT_DELEGATED                           CONSTANT T_USER_ACCOUNT_CONTROL := 1048576;
    USE_DES_KEY_ONLY                        CONSTANT T_USER_ACCOUNT_CONTROL := 2097152;
    DONT_REQ_PREAUTH                        CONSTANT T_USER_ACCOUNT_CONTROL := 4194304;
    PASSWORD_EXPIRED                        CONSTANT T_USER_ACCOUNT_CONTROL := 8388608;
    TRUSTED_TO_AUTH_FOR_DELEGATION  CONSTANT T_USER_ACCOUNT_CONTROL := 16777216;
    PARTIAL_SECRETS_ACCOUNT             CONSTANT T_USER_ACCOUNT_CONTROL := 67108864;
    
    LDAP_USER CONSTANT VARCHAR2(255) := 'CN=UserName1,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';
    LDAP_PASSWORD CONSTANT VARCHAR2(30) := 'secret';
    LDAP_SERVER CONSTANT VARCHAR2(30) := 'host';
    LDAP_PORT INTEGER := 389;

    userDn VARCHAR2(100) := 'CN=UserName2,OU=SERVICE_ACCOUNTS,OU=Users,OU=LT,OU=PB,DC=pan,DC=int';


    ld DBMS_LDAP.SESSION;
    ret NUMBER; 
    ldapEntry DBMS_LDAP.MESSAGE;    
    attrs DBMS_LDAP.STRING_COLLECTION;  
    ldapMessage DBMS_LDAP.MESSAGE;
    attribName VARCHAR2(256);
    berEelement DBMS_LDAP.BER_ELEMENT;
    info DBMS_LDAP.STRING_COLLECTION;
    
    pwdLastSet TIMESTAMP;
    expireDate TIMESTAMP
    
BEGIN
    
    attrs(1) := 'displayName';
    attrs(2) := 'userAccountControl';
    attrs(3) := 'pwdLastSet';

    ld := DBMS_LDAP.INIT(LDAP_SERVER, LDAP_PORT);
    ret := DBMS_LDAP.SIMPLE_BIND_S(ld, LDAP_USER, LDAP_PASSWORD);
    DBMS_LDAP.USE_EXCEPTION := FALSE;
    
    ret := DBMS_LDAP.SEARCH_S(
        ld => ld, 
        base => 'DC=pan,DC=int',
        SCOPE => DBMS_LDAP.SCOPE_SUBTREE,
        FILTER => '&(objectCategory=user)(distinguishedName='||userDn||')', 
        attrs => attrs,
        attronly => 0,
        res => ldapMessage);
    ldapEntry := DBMS_LDAP.FIRST_ENTRY(ld, ldapMessage);
    IF ldapEntry IS NULL THEN
        DBMS_OUTPUT.PUT_LINE('User "'||userDn||'" does not exist');
    ELSE
        WHILE ldapEntry IS NOT NULL LOOP        
            attribName := DBMS_LDAP.FIRST_ATTRIBUTE(ld, ldapEntry, berEelement);
            WHILE attribName IS NOT NULL LOOP     
                info := DBMS_LDAP.GET_VALUES(ld, ldapEntry, attribName);
                CASE attribName
                WHEN 'displayName' THEN 
                    DBMS_OUTPUT.PUT_LINE('Display Name = ' || info(info.FIRST));
                WHEN 'userAccountControl' THEN
                    IF SIGN(BITAND(ACCOUNTDISABLE, info(info.FIRST))) = 1 THEN
                        DBMS_OUTPUT.PUT_LINE('Account is disabled');
                    END IF;
                    
                    IF SIGN(BITAND(PASSWORD_EXPIRED, info(info.FIRST))) = 1 THEN
                        DBMS_OUTPUT.PUT_LINE('Password is expired');
                    END IF;                 
                WHEN 'pwdLastSet' THEN
                    pwdLastSet := (TIMESTAMP '1601-01-01 00:00:00 UTC' + info(info.FIRST)/1000/1000/10/60/60/24 * INTERVAL '1' DAY) AT LOCAL;
                    DBMS_OUTPUT.PUT_LINE('Password was changed at ' || TO_CHAR(pwdLastSet, 'yyyy-mm-dd hh24:mi:ss'));

                    -- Password life time is not available in LDAP. Check your company policy and calculate expire date accordingly, for example:
                    expireDate := pwdLastSet + INTERVAL '6' MONTH;
                    DBMS_OUTPUT.PUT_LINE('Your password will expire at ' || TO_CHAR(expireDate, 'yyyy-mm-dd hh24:mi:ss'));                  
                END CASE;       
                attribName := DBMS_LDAP.NEXT_ATTRIBUTE(ld, ldapEntry, berEelement);
            END LOOP;
            ldapEntry := DBMS_LDAP.NEXT_ENTRY(ld, ldapEntry);
        END LOOP;
        DBMS_LDAP.BER_FREE(berEelement, freebuf => 0);  
    END IF;
    ret := DBMS_LDAP.MSGFREE(ldapMessage);
    ret := DBMS_LDAP.UNBIND_S(ld);

end;

更新

当我说LDAP 中的密码生命周期不可用"时,我犯了一个错误.你可以这样查询:

I did a mistake when I say "Password life time is not available in LDAP". You can query it like this:

BEGIN

   dn := 'DC=pan,DC=int';
   attrs(1) := 'maxPwdAge';

    ld := DBMS_LDAP.INIT(LDAP_SERVER, LDAP_PORT);
    ret := DBMS_LDAP.SIMPLE_BIND_S(ld, LDAP_USER, LDAP_PASSWORD);
    DBMS_LDAP.USE_EXCEPTION := FALSE;

    ret := DBMS_LDAP.SEARCH_S(
        ld => ld, 
        base => 'DC=pan,DC=int',
        SCOPE => DBMS_LDAP.SCOPE_SUBTREE,
        FILTER => '&(objectCategory=domain)(distinguishedName='||dn||')', 
        attrs => attrs,
        attronly => 0,
        res => ldapMessage);
    -- You do not need a loop because you have only one single entry and one single attribute. Otherwise a loop would be required, see above.
    ldapEntry := DBMS_LDAP.FIRST_ENTRY(ld, ldapMessage);
    attribName := DBMS_LDAP.FIRST_ATTRIBUTE(ld, ldapEntry, berEelement);
    info := DBMS_LDAP.GET_VALUES(ld, ldapEntry, attribName);    

    DBMS_OUTPUT.PUT_LINE('Password life time = ' || -info(info.FIRST)/1000/1000/10/60/60/24 || ' days');

    DBMS_LDAP.BER_FREE(berEelement, freebuf => 0);  
    ret := DBMS_LDAP.MSGFREE(ldapMessage);
    ret := DBMS_LDAP.UNBIND_S(ld);

END;

这篇关于甲骨文.使用 LDAP 进行身份验证总是返回 -16的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆