LoginSignup
1
0

More than 3 years have passed since last update.

OpenLDAP が抱えるパスワードを C# & .NET を使って変更してみる。

Last updated at Posted at 2021-02-14

目的

OpenLDAP が抱えるパスワードを変更するときは ldappasswd を使うわけだが、「自作プログラムからそれをやるときに、OpenLDAP for Windows に同梱されているコマンドをどうしても叩きたくない!」という偏屈者のための記事。

「簡単にできるやろ」と高を括ってたが、半日くらいかかってしまったんで記事にする。

とりあえずコード全体

using System;
using System.DirectoryServices.Protocols;
using System.Net;
using System.Text.RegularExpressions;

namespace LdapPasswdLib
{
    public static class LdapPasswordChanger
    {
        public const int LDAPS_DEFAULT_PORT = 636;

        public const string LDAP_EXOP_MODIFY_PASSWD = "1.3.6.1.4.1.4203.1.11.1";
        public const int LDAP_TAG_EXOP_MODIFY_PASSWD_ID = 0x80;
        public const int LDAP_TAG_EXOP_MODIFY_PASSWD_OLD = 0x81;
        public const int LDAP_TAG_EXOP_MODIFY_PASSWD_NEW = 0x82;

        /// <summary>
        /// LDAP でパスワードを変更する。
        /// </summary>
        /// <param name="accountDN">アカウントの DN</param>
        /// <param name="oldPasswd">現在のパスワード</param>
        /// <param name="newPasswd">変更後のパスワード</param>
        /// <param name="ldapServer">LDAP サーバーのホスト名または IP アドレス</param>
        /// <param name="isTls">LDAPS にする場合 true。LDAP のままにする場合 false。</param>
        public static void ChangePassword(string accountDN, string oldPasswd, string newPasswd, string ldapServer, bool isTls)
        {
            if (accountDN == null || oldPasswd == null || newPasswd == null || ldapServer == null)
                    throw new ArgumentNullException();

            if (accountDN.Length < 1 || oldPasswd.Length < 1 || newPasswd.Length < 1 || ldapServer.Length < 1)
                    throw new ArgumentException();

            ldapServer = SpecifyServerPort(ldapServer, isTls);

            LdapConnection ldapConnection = new LdapConnection(ldapServer)
            {
                Credential = new NetworkCredential(accountDN, oldPasswd),
                AuthType = AuthType.Basic,
                Timeout = new TimeSpan(0, 0, 10)
            };
            ldapConnection.SessionOptions.ProtocolVersion = 3;
            ldapConnection.SessionOptions.SecureSocketLayer = isTls;

            // https://tools.ietf.org/html/rfc3062
            var ber = BerConverter.Encode("{tststs}",
                    LDAP_TAG_EXOP_MODIFY_PASSWD_ID, accountDN,
                    LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, oldPasswd,
                    LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, newPasswd);
            var modifyPasswdRequest = new ExtendedRequest(LDAP_EXOP_MODIFY_PASSWD, ber);
            try
            {
                // パスワードを変更するユーザーでバインドする
                ldapConnection.Bind();

                // パスワード変更要求を送信
                ldapConnection.SendRequest(modifyPasswdRequest);

                // 応答が「成功」か確認
                if (modifyPasswdResponse.ResultCode != ResultCode.Success)
                        throw new Exception("Could not change password. (" + modifyPasswdResponse.ResultCode + ")");
            }
            finally
            {
                if (ldapConnection != null)
                {
                    try
                    {
                        ldapConnection.Dispose();
                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine(e);
                    }
                }
            }
        }

        /// <summary>
        /// TCP ポート指定なし、かつ、LDAPS のときはポート指定を追加する。
        /// </summary>
        /// <param name="ldapServer">LDAP サーバー指定</param>
        private static string SpecifyServerPort(string ldapServer, bool isTls)
        {
            if (isTls && !Regex.IsMatch(ldapServer, ":[1-9][0-9]*$")) return ldapServer + ":" + LDAPS_DEFAULT_PORT;
            else return ldapServer;
        }
    }
}

肝はここ

苦労したのはどこかというと、ここ。

var ber = BerConverter.Encode("{tststs}",
        LDAP_TAG_EXOP_MODIFY_PASSWD_ID, accountDN,
        LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, oldPasswd,
        LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, newPasswd);
var modifyPasswdRequest = new ExtendedRequest(LDAP_EXOP_MODIFY_PASSWD, ber);

BerConverterExtendedRequest の公式ドキュメントには、当然使用例の記載がなく (一見さんお断り感)。一番参考になったのは、xUnit で書かれた BerConverter のテスト。ExtendedRequest の使い方は勘w (一応、stackoverflow に情報はあった)

あと、ここで使ってる定数 LDAP_TAG_~ は、openldap のソース コードのヘッダーから頂いてきた。

識者が読めば判るのかもだが、俺は RFC 3062 を読んだだけではこのコードが書ける気はしない…。結局、openldap の ldappasswd のコードを移植しただけ。

結論

皆は ldappasswd コマンドを使おう。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0