Date: Fri, 20 Mar 2009 01:10:02 -0400 (EDT) From: Toby Burress <kurin@delete.org> To: FreeBSD-gnats-submit@FreeBSD.org Subject: docs/132839: Fix example script in ldap-auth Message-ID: <20090320051002.C1F197E83F@lithium.delete.org> Resent-Message-ID: <200903200530.n2K5U1Xp021468@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>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@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:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20090320051002.C1F197E83F>