QA@IT
この質問・回答は、@ITの旧掲示板からインポートされたものです。

c#からOpenLdapへの認証

いつも参考にさせてもらってます。しゅうといいます。C#でWebアプリを作ってます。
検索してみたのですが、思うような情報が見つからなかったので
ここで質問させていただきます。
Insider.NETとLinux Squareどちらに投稿するか迷いましたが、
C#なのでこちらにしました。

C#で作成したログイン画面に入力されたユーザーIDとパスワードを
LDAPサーバー(OpenLdap 2.2.23)に認証をかけ、ログインの可否を
判断したいのですが、うまくいきません。
C#のコードは以下のようになってます。

 String adPath = "LDAP://LDAPサーバーのIPアドレス/ou=Users,dc=example,dc=com";
 try
 {
  string sLogin = txtUsername.Text.Trim();
  string sPass = txtPassword.Text.Trim();

  DirectoryEntry de = new DirectoryEntry(adPath);
  DirectorySearcher ds = new DirectorySearcher(de);

  ds.Filter=("(cn= " + sLogin + ")");
  //ds.Filter="(&(cn=" + sLogin + ")(userPassword="+ sPass + "))";

  SearchResult searchResult = ds.FindOne();

  if ( searchResult != null )
  {
   DirectoryEntry directoryEntry = searchResult.GetDirectoryEntry();
   Label4.Text = directoryEntry.Name;
  }
  else
  {
   Label4.Text= "無効なログイン" ;
  }
 }
 catch(Exception exc)
 {
  Label4.Text="Exception : " + exc.Message;
 }

slapd.confのACLの設定は以下のようになってます。

 access to attrs=usersPassword
 by dn="cn=Manager,dc=example,dc=com" wirte
 by self write
 by anonymous auth
 by * note

 access to *
 by self write
 by dn="cn=Manager,dc=example,dc=com" wirte
 by * read

ds.FilterにユーザーIDのみを指定した場合はうまくいくのですが、
パスワードまで指定すると(コメントの部分にすると)searchResultに値が入りません。
パスワードはFilterに指定するものじゃないんでしょうか?
LdapBrowserでSearchするときにFilterとして
(&(cn=ユーザーID)(userPassword=パスワード))
とするとうまく抽出されるので、コードもこれでいいと思ったのですが。
このような場合、PAMが必要なんでしょうか?

LDAPサーバーは顧客の環境には既に稼動していて、今回の対応のため社内に
LDAPサーバーを立てて開発しています。LDAPは今回初めて触ったので、
理解が不足しているのは自分でも承知していますが、周りにOpenLdap + .net
の経験者がいないものでよろしくご教授ください。よろしくお願いします。

質問者:しゅう

回答

以前同じようにはまったので、その時の解決方法です。
LDAPの設定はあまり詳しくないので、多少環境が違うかもしれませんが。。

DirectoryEntryの初期化のところを以下のようにしました。

try
{
//LdapにログインするためのDN
string userDN = "cn=" + sLogin + ",ou=Users,dc=example,dc=com";
entry = new DirectoryEntry( adPath , userDN, パスワード, AuthenticationTypes.None );

Object obj = entry.NativeObject; //ここでLdap認証が入る(Exceptionが発生しなければ成功)

}
catch
{
//認証失敗
}

このあとは同じようにユーザーIDのみでフィルターをかけてユーザーの情報を取得したらできました。

最初はしゅうさんと同じようにフィルターにパスワードを入れてみたのですが、
パスワード属性は、ルートでログインしたときしか取得できませんでした。
しかも、取得できた文字列は、元のパスワードをMD5でハッシュ化しさらにBASE64エンコード
されたもの(たぶんLDAPの設定が関係する)だったので、LDAPの内部設定を知っていないと扱えないかかと思います。

投稿者:いもぽてと

編集 履歴 (0)

南部です。

うまくいきそうですけどねぇ、、

!?
ds.Filter="(&(cn=" + sLogin + ")(userPassword="+ sPass + "))";
access to attrs=usersPassword
!!
名前ちがうけど、、、

投稿者:nanbu

編集 履歴 (0)

返信が遅れてすみません。別件で出張などがあったもんで。

string sDC = "ou=USER,dc=example,dc=com";
string sLogin = ログインID;
string sPass = パスワード;

string adPath = "LDAP://" + LDAPサーバーのIP + "/" + sDC;
string sDN = "uid=" + sLogin + "," + sDC;
//注:顧客の環境に合わせたため、以前の投稿内容とは違ってます

DirectoryEntry de = new DirectoryEntry(adPath,sDN,sPass,AuthenticationTypes.FastBind);

という具合にやったらできました。いもぽてとさんの記述どおり、
DirectoryEntryのインスタンス生成時にusernameをDNで指定するようです。
filterを使おうとしてたのがそもそもの間違いでした。
認証できたときはホッとしました。

いもぽてとさん、nanbuさんありがとうございました

投稿者:しゅう

編集 履歴 (0)
ウォッチ

この質問への回答やコメントをメールでお知らせします。