Password

哪些命令會更改 Open Directory 密碼?

  • July 31, 2012

我將 Open Directory 理解為 OpenLDAP + SASL(密碼伺服器)+ Kerberos。OpenLDAP 似乎遵循 SASL 進行身份驗證;我不知道 Kerberos。

我想從腳本更改使用者密碼,最好是遠端更改,並且我希望正確更改密碼。(即,在任何情況下,我都不希望使用者擁有不同的密碼,具體取決於他們對進入 Open Directory 的三個服務中的哪一個進行身份驗證。)

我可以在dsimport未綁定到目錄的機器上通過網路進行操作,但是,當您嘗試導入密碼時(儘管將 AuthType 設置為 dsAuthMethodStandard:dsAuthClearText),它僅在密碼尚未被輸入的情況下才有效之前設定的。(我相信可以設置 Crypt 密碼,但我擔心這意味著只有 OD 的 LDAP 部分會知道目前密碼。)

除了啟動與伺服器的 ssh 會話並在那裡更改密碼之外,我還能做些什麼嗎?如果我這樣做,是否有任何命令可以讓我在一行上指定多個使用者及其新密碼?

哪些命令可以更改所有打開的目錄密碼,有沒有更喜歡的?

apropos password給了我這些有趣的結果:

  • kpasswd(1) - 更改使用者的 Kerberos 密碼
  • ldappasswd(1) - 更改 LDAP 條目的密碼
  • lppasswd(1) - 添加、更改或刪除摘要密碼
  • passwd(1) - 修改使用者密碼
  • pwpolicy(8) - 獲取和設置密碼策略
  • saslpasswd2(8) - 設置使用者的 sasl 密碼
  • slappasswd(8) - OpenLDAP 密碼實用程序

我會看一些手冊頁,我覺得這pwpolicy是最好的選擇,但我很想知道使用這些手冊是否有任何微妙之處(例如,不要更改 Kerberos 密碼無需更改 LDAP 和 SASL 密碼),並且如果它們中的任何一個在沒有 ssh 會話的情況下遠端工作。

我遇到的最方便的答案是將 passwd 命令與 dscl 結合使用。這是互動式會話的輸出(密碼由星號替換):

$ dscl -u diradmin -p ces 
Password: 
> cd /LDAPv3/127.0.0.1/
/LDAPv3/127.0.0.1 > auth diradmin *****
/LDAPv3/127.0.0.1 > passwd Users/Atwo807 *****
/LDAPv3/127.0.0.1 > passwd Users/Atwo249 *****
/LDAPv3/127.0.0.1 > passwd Users/doesnotexist foobar
passwd: Invalid Path
<dscl_cmd> DS Error: -14009 (eDSUnknownNodeName)
/LDAPv3/127.0.0.1 > exit
Goodbye

這是一個用於進行更改的python腳本。您將需要 pexpect 模組(sudo easy_install pexpect應該為您獲取它;我認為您不需要安裝開發工具)。

#!/usr/bin/env python

import pexpect

def ChangePasswords(host, path, diradmin, diradmin_password, user_passwords, record_type='Users'):
   """Changes passwords in a Open Directory or similar directory service.

   host = the dns name or IP of the computer hosting the directory
   path = the pathname to the directory (ex. '/LDAPv3/127.0.0.1')
   diradmin = the directory administrator's shortname (ex. 'diradmin')
   diradmin_password = the directory administrator's password
   user_passwords = a dictionary mapping record names (typically, user's short
                    names) onto their new password
   record_type = the sort of records you are updating.  Typically 'Users'

   Returns a tuple.  The first entry is a list of all records (users) who
       failed to update.  The second entry is a list of all records (users)
       who successfully updated.
   """

   failed_list = []
   succeeded_list = []
   prompt = " > "

   child = pexpect.spawn("dscl -u %s -p %s" % (diradmin, host))

   if not (ReplyOnGoodResult(child, "Password:", diradmin_password) and
      ReplyOnGoodResult(child, prompt, "cd %s" % path) and
      ReplyOnGoodResult(child, prompt,
                       "auth %s %s"  % (diradmin, diradmin_password)) and
      ReplyOnGoodResult(child, prompt, None)):
       print "Failed to log in and authenticate"
       failed_list = user_passwords.keys()
       return (failed_list, succeeded_list)

   # We are now logged in, and have a prompt waiting for us
   expected_list = [ pexpect.EOF, pexpect.TIMEOUT,
                    '(?i)error', 'Invalid Path', prompt ]
   desired_index = len(expected_list) - 1
   for record_name in user_passwords:        
       #print "Updating password for %s" % record_name,

       child.sendline("passwd %s/%s %s" % (record_type, record_name,
                                           user_passwords[record_name]))
       if child.expect(expected_list) == desired_index:
           #print ": Succeeded"
           succeeded_list.append(record_name)
       else:
           #print ": Failed"
           failed_list.append(record_name)
           child.expect(prompt)

   child.sendline("exit")
   child.expect(pexpect.EOF)

   return (failed_list, succeeded_list)


def ReplyOnGoodResult(child, desired, reply):
   """Helps analyze the results as we try to set passwords.

   child = a pexpect child process
   desired = The value we hope to see 
   reply = text to send if we get the desired result (or None for no reply)
   If we do get the desired result, we send the reply and return true.
   If not, we return false."""

   expectations = [ pexpect.EOF, pexpect.TIMEOUT, '(?i)error', desired ]
   desired_index = len(expectations) - 1

   index = child.expect(expectations)
   if index == desired_index:
       if reply:
           child.sendline(reply)
       return True
   else:
       return False

您可以按如下方式使用它:

# This example assumes that you have named the script given above 'pwchange.py'
# and that it is in the current working directory
import pwchange 

(failed, succeeded) = pwchange.ChangePasswords("ces", "/LDAPv3/127.0.0.1", 
    "diradmin", "******", 
    { 'Atwo807' : '*****', 'Atwo249' : '*****', 
      'Nonexist' : 'foobar', 'Bad' : 'bad' })

print failed, succeeded
['Bad', 'Nonexist'] ['Atwo249', 'Atwo807']

引用自:https://serverfault.com/questions/56830