Bug 132839 - [patch] Fix example script in ldap-auth article
Summary: [patch] Fix example script in ldap-auth article
Status: Open
Alias: None
Product: Documentation
Classification: Unclassified
Component: Books & Articles (show other bugs)
Version: Latest
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-doc (Nobody)
URL:
Keywords: patch
Depends on:
Blocks:
 
Reported: 2009-03-20 05:30 UTC by Toby Burress
Modified: 2022-10-17 12:38 UTC (History)
0 users

See Also:


Attachments
patch (5.08 KB, patch)
2009-03-20 05:30 UTC, Toby Burress
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Toby Burress 2009-03-20 05:30:01 UTC
	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

Fix: patch:
Comment 1 Toby Burress 2009-03-20 05:42:27 UTC
Oops.  I just realized I had some typos, and I didn't use the
standard example.org domain.  Revised patch follows.

--- 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.example.org"
+searchbase = "ou=people,dc=example,dc=org"
+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 ---
Comment 2 Eitan Adler freebsd_committer freebsd_triage 2017-12-31 08:00:36 UTC
For bugs matching the following criteria:

Status: In Progress Changed: (is less than) 2014-06-01

Reset to default assignee and clear in-progress tags.

Mail being skipped
Comment 3 Graham Perrin freebsd_committer freebsd_triage 2022-10-17 12:38:48 UTC
Keyword: 

    patch
or  patch-ready

– in lieu of summary line prefix: 

    [patch]

* bulk change for the keyword
* summary lines may be edited manually (not in bulk). 

Keyword descriptions and search interface: 

    <https://bugs.freebsd.org/bugzilla/describekeywords.cgi>