docs/132839: Fix example script in ldap-auth
Toby Burress
kurin at delete.org
Fri Mar 20 05:30:02 UTC 2009
>Number: 132839
>Category: docs
>Synopsis: Fix example script in ldap-auth
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: freebsd-doc
>State: open
>Quarter:
>Keywords:
>Date-Required:
>Class: maintainer-update
>Submitter-Id: current-users
>Arrival-Date: Fri Mar 20 05:30:01 UTC 2009
>Closed-Date:
>Last-Modified:
>Originator: Toby Burress
>Release: FreeBSD 6.3-RELEASE-p2 amd64
>Organization:
>Environment:
System: FreeBSD lithium.delete.org 6.3-RELEASE-p2 FreeBSD 6.3-RELEASE-p2 #2: Sun May 4 03:12:43 EDT 2008 root at lithium.delete.org:/usr/obj/usr/src/sys/LITHIUMv3 amd64
>Description:
This article (it turns out) has a few errors, two of which
are fixed in this patch.
(a) some object classes given in the examples are implicit
as they are the parents of other object classes explicitly
named, and it is apparently gouche to include them.
Specifically "top", maybe others.
(b) the Ruby script that was here before used an LDAP Modify
operation to change a user's password. This is not the
Done Thing for a number of reasons which interested parties
can read about in RFC 3062. It turns out the entire ruby-ldap
library is not very well maintained and does not support
the proper operation. The example has been replaced with
a Python script that implements the proper procedure.
see http://lists.freebsd.org/pipermail/freebsd-doc/2008-November/015026.html for more info
>How-To-Repeat:
>Fix:
patch:
--- patch begins here ---
--- article.sgml.old 2009-03-20 00:57:22.000000000 -0400
+++ article.sgml 2009-03-20 01:03:08.000000000 -0400
@@ -307,7 +307,6 @@
organizational unit will look like:</para>
<programlisting>dn: ou=people,dc=example,dc=org
-objectClass: top
objectClass: organizationalUnit
ou: people</programlisting>
@@ -336,7 +335,6 @@
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
-objectClass: top
uidNumber: 10000
gidNumber: 10000
homeDirectory: /home/tuser
@@ -352,13 +350,11 @@
user entries, but we will use the defaults below:</para>
<programlisting>dn: ou=groups,dc=example,dc=org
-objectClass: top
objectClass: organizationalUnit
ou: groups
dn: cn=tuser,ou=groups,dc=example,dc=org
objectClass: posixGroup
-objectClass: top
gidNumber: 10000
cn: tuser</programlisting>
@@ -604,51 +600,74 @@
<screen>&prompt.root; <userinput>sysctl security.bsd.see_other_uids=0</userinput>.</screen>
</caution>
- <para>A more flexible (and probably more secure) approach can be
- used by writing a custom program, or even a web interface. The
- following is part of a <application>Ruby</application> library
- that can change LDAP passwords. It sees use both on the command
- line, and on the web.</para>
+ <para>A more flexible (and probably more secure) approach can be
+ used by writing a custom program, or even a web interface.
+ The following is modeled on a <application>Python</application>
+ library that can change LDAP passwords. It sees use both
+ on the command line, and on the web.</para>
- <example id="chpw-ruby">
- <title>Ruby script for changing passwords</title>
+ <example id="chpw-python">
+ <title>Python script for changing passwords</title>
- <programlisting><![CDATA[require 'ldap'
-require 'base64'
-require 'digest'
-require 'password' # ruby-password
-
-ldap_server = "ldap.example.org"
-luser = "uid=#{ENV['USER']},ou=people,dc=example,dc=org"
-
-# get the new password, check it, and create a salted hash from it
-def get_password
- pwd1 = Password.get("New Password: ")
- pwd2 = Password.get("Retype New Password: ")
-
- raise if pwd1 != pwd2
- pwd1.check # check password strength
-
- salt = rand.to_s.gsub(/0\./, '')
- pass = pwd1.to_s
- hash = "{SSHA}"+Base64.encode64(Digest::SHA1.digest("#{pass}#{salt}")+salt).chomp!
- return hash
-end
-
-oldp = Password.get("Old Password: ")
-newp = get_password
-
-# We'll just replace it. That we can bind proves that we either know
-# the old password or are an admin.
-
-replace = LDAP::Mod.new(LDAP::LDAP_MOD_REPLACE | LDAP::LDAP_MOD_BVALUES,
- "userPassword",
- [newp])
-
-conn = LDAP::SSLConn.new(ldap_server, 389, true)
-conn.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
-conn.bind(luser, oldp)
-conn.modify(luser, [replace])]]></programlisting>
+ <programlisting><![CDATA[import ldap # python-ldap
+import os, sys
+from getpass import getpass
+
+uri = "ldap://ldap1.dimins.com"
+searchbase = "ou=people,dc=dimins,dc=com"
+filter = "(&(objectClass=posixAccount)(uid=%s))"
+
+# get the username; if none is given, use the current user
+user = os.environ['USER']
+if len(sys.argv) > 1:
+ user = sys.argv[1]
+
+ldapobj = ldap.initialize(uri)
+ldapobj.start_tls_s() # this is pretty important
+
+# Get the users DN, and then bind as that.
+# The way to do this is first bind anonymously (if you don't allow anon
+# binds, there's probably some standard account you use for this.
+ldapobj.simple_bind_s()
+
+# Search for a user with the uid we gave. We search everything under
+# the "base" we configure above (as there may be other users with the same
+# UID elsewhere in the tree; we don't want to return those.
+result = ldapobj.search_s(searchbase, ldap.SCOPE_SUBTREE, filter%user)
+
+if len(result) > 1:
+ # This is kind of suspicious; we only want one user.
+ print "I found several users that match that user id."
+ print "Talk to your sysadmin."
+ sys.exit(1)
+
+# The results are an array of (dn, attrlist) tuples.
+dn = result[0][0]
+
+# Now we get the user's old password, and bind to the server with it
+# and his DN. If it succeeds, he (and we) have the proper credentials to
+# change his password.
+passwd = getpass("current password: ")
+try:
+ ldapobj.simple_bind_s(dn, passwd)
+except ldap.INVALID_CREDENTIALS:
+ print "Bad password."
+ sys.exit(1)
+
+# Get and confirm new password.
+npass1 = 'a'
+npass2 = 'b'
+while npass1 != npass2:
+ npass1 = getpass("new password: ")
+ npass2 = getpass("new password (again): ")
+
+# This is the key. This uses the LDAP Password Modify Extended Operation.
+# It is important to use this when you can, although not all libraries
+# (e.g. ruby-ldap) support it. See rfc3062.
+ldapobj.passwd_s(dn, passwd, npass1)
+
+# And we're done.
+ldapobj.unbind()]]></programlisting>
</example>
<para>Although not guaranteed to be free of security holes (the
@@ -759,7 +778,6 @@
<title>Creating a management group</title>
<programlisting>dn: cn=homemanagement,dc=example,dc=org
-objectClass: top
objectClass: posixGroup
cn: homemanagement
gidNumber: 121 # required for posixGroup
--- patch ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:
More information about the freebsd-doc
mailing list