aboutsummaryrefslogtreecommitdiff
path: root/en_US.ISO8859-1/articles/ldap-auth/article.xml
blob: 7f055fa72ad05942144e091e62170bfc59a5776b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook XML V4.5-Based Extension//EN"
	"../../../share/xml/freebsd45.dtd">

<article lang="en">
  <articleinfo>
    <title>LDAP Authentication</title>

    <authorgroup>
      <author>
	<firstname>Toby</firstname>
	<surname>Burress</surname>
	<affiliation>
	  <address><email>kurin@causa-sui.net</email></address>
	</affiliation>
      </author>
    </authorgroup>

    <copyright>
      <year>2007</year>
      <year>2008</year>
      <holder>The FreeBSD Documentation Project</holder>
    </copyright>

    <legalnotice id="trademarks" role="trademarks">
      &tm-attrib.freebsd;
      &tm-attrib.general;
    </legalnotice>

    <pubdate>$FreeBSD$</pubdate>

    <releaseinfo>$FreeBSD$</releaseinfo>

    <abstract>
      <para>This document is intended as a guide for the configuration
	of an LDAP server (principally an <application>OpenLDAP</application>
	server) for authentication on &os;.  This is useful for situations
	where many servers need the same user accounts, for example as a
	replacement for <application>NIS</application>.</para>
    </abstract>
  </articleinfo>

  <sect1 id="preface">
    <title>Preface</title>

    <para>This document is intended to give the reader enough of an
      understanding of LDAP to configure an LDAP server.  This document will
      attempt to provide an
      explanation of <filename role="package">net/nss_ldap</filename>
      and <filename role="package">security/pam_ldap</filename> for use with
      client machines services for use with the LDAP server.</para>

    <para>When finished, the reader should be able to configure and
      deploy a &os; server that can host an LDAP directory, and to
      configure and deploy a &os; server which can authenticate against
      an LDAP directory.</para>

    <para>This article is not intended to be an exhaustive account of
      the security, robustness, or best practice considerations for
      configuring LDAP or the other services discussed herein.  While the author
      takes care to do everything correctly, he does not
      address security issues beyond a general scope.  This article should be
      considered to lay the theoretical groundwork only, and any actual
      implementation should be accompanied by careful requirement
      analysis.</para>
  </sect1>

  <sect1 id="ldap">
    <title>Configuring LDAP</title>
    <para>LDAP stands for <quote>Lightweight Directory Access
      Protocol</quote> and is a subset of the X.500 Directory Access
      Protocol.  Its most recent specifications are in <ulink
	url="http://www.ietf.org/rfc/rfc4510.txt">RFC4510</ulink> and
      friends.  Essentially it is a database that expects to be read from
      more often than it is written to.</para>

    <para>The LDAP server <ulink
	url="http://www.openldap.org/">OpenLDAP</ulink> will be used in the
      examples in this document; while the principles here should be
      generally applicable to many different servers, most of the
      concrete administration is
      <application>OpenLDAP</application>-specific.  There are several
      server versions in ports, for example <filename
	role="package">net/openldap24-server</filename>.  Client servers
      will need the corresponding <filename
	role="package">net/openldap24-client</filename> libraries.</para>

    <para>There are (basically) two areas of the LDAP service which need
      configuration.  The first is setting up a server to receive
      connections properly, and the second is adding entries to the
      server's directory so that &os; tools know how to interact with it.</para>

    <sect2 id="ldap-connect">
      <title>Setting Up the Server for Connections</title>

      <note>
	<para>This section is specific to
	  <application>OpenLDAP</application>.  If you are using another
	  server, you will need to consult that server's
	  documentation.</para>
      </note>

      <sect3 id="ldap-connect-install">
	<title>Installing <application>OpenLDAP</application></title>

	<para>First, install <application>OpenLDAP</application>:</para>

	<example id="oldap-install">
	  <title>Installing <application>OpenLDAP</application></title>

	  <screen>&prompt.root; <userinput>cd /usr/ports/net/openldap24-server</userinput>
&prompt.root; make install clean</screen>
	</example>

	<para>This installs the <command>slapd</command> and
	  <command>slurpd</command> binaries, along with the required
	  <application>OpenLDAP</application> libraries.</para>
      </sect3>

      <sect3 id="ldap-connect-config">
	<title>Configuring <application>OpenLDAP</application></title>

	<para>Next we must configure
	  <application>OpenLDAP</application>.</para>

	<para>You will want to require encryption in your
	  connections to the LDAP server; otherwise your users' passwords
	  will be transferred in plain text, which is considered
	  insecure.  The tools we will be using support two very similar kinds
	  of encryption, SSL and TLS.</para>

	<para>TLS stands for <quote>Transportation Layer Security</quote>.
	  Services that employ TLS tend to connect on the
	  <emphasis>same</emphasis> ports as the same services without
	  TLS; thus an SMTP server which supports TLS will listen for
	  connections on port 25, and an LDAP server will listen on 389.</para>

	<para>SSL stands for <quote>Secure Sockets Layer</quote>, and
	  services that implement SSL do <emphasis>not</emphasis> listen on
	  the same ports as their non-SSL counterparts.  Thus SMTPS listens
	  on port 465 (not 45), HTTPS listens on 443, and LDAPS on
	  636.</para>

	<para>The reason SSL uses a different port than TLS is because a
	  TLS connection begins as plain text, and switches to encrypted
	  traffic after the <literal>STARTTLS</literal> directive.  SSL
	  connections are encrypted from the beginning.  Other than that
	  there are no substantial differences between the two.</para>

	<note>
	  <para>We will adjust
	    <application>OpenLDAP</application> to use TLS, as SSL is
	    considered deprecated.</para>
	</note>

	<para>Once <application>OpenLDAP</application> is installed via
	  ports, the following configuration parameters in
	  <filename>/usr/local/etc/openldap/slapd.conf</filename> will
	  enable TLS:</para>

	<programlisting>security ssf=128

TLSCertificateFile /path/to/your/cert.crt
TLSCertificateKeyFile /path/to/your/cert.key
TLSCACertificateFile /path/to/your/cacert.crt</programlisting>


	<para>Here, <literal>ssf=128</literal> tells
	  <application>OpenLDAP</application> to require 128-bit
	  encryption for all connections, both search and update.  This
	  parameter may be configured based on the security needs of your
	  site, but rarely you need to weaken it, as most LDAP client
	  libraries support strong encryption.</para>

	<para>The <filename>cert.crt</filename>,
	  <filename>cert.key</filename>, and
	  <filename>cacert.crt</filename> files are necessary for clients
	  to authenticate <emphasis>you</emphasis> as the valid LDAP
	  server.  If you simply want a server that runs, you can create a
	  self-signed certificate with OpenSSL:</para>

	<example id="genrsa">
	  <title>Generating an RSA key</title>

	  <screen>&prompt.user; <userinput>openssl genrsa -out cert.key 1024</userinput>
Generating RSA private key, 1024 bit long modulus
....................++++++
...++++++
e is 65537 (0x10001)
&prompt.user; <userinput>openssl req -new -key cert.key -out cert.csr</userinput></screen>
	</example>

	<para>At this point you should be prompted for some values.  You
	  may enter whatever values you like; however, it is important the
	  <quote>Common Name</quote> value be the fully qualified domain
	  name of the <application>OpenLDAP</application> server.
	  In our case, and the examples here, the server is
	  <replaceable>server.example.org</replaceable>.
	  Incorrectly setting this value will cause clients to fail when
	  making connections.  This can the
	  cause of great frustration, so ensure that you follow these
	  steps closely.</para>

	<para>Finally, the certificate signing request needs to be
	  signed:</para>

	<example id="self-sign">
	  <title>Self-signing the certificate</title>

	  <screen>&prompt.user; <userinput>openssl x509 -req -in cert.csr -days 365 -signkey cert.key -out cert.crt</userinput>
Signature ok
subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
Getting Private key</screen>
	</example>

	<para>This will create a self-signed certificate that can be
	  used for the directives in <filename>slapd.conf</filename>,
	  where <filename>cert.crt</filename> and
	  <filename>cacert.crt</filename> are the same file.  If you are
	  going to use many <application>OpenLDAP</application> servers
	  (for replication via <literal>slurpd</literal>) you will want to
	  see <xref linkend="ssl-ca"/> to generate a CA key and use it to
	  sign individual server certificates.</para>

	<para>Once this is done, put the following in
	  <filename>/etc/rc.conf</filename>:</para>

	<programlisting>slapd_enable="YES"</programlisting>

	<para>Then run <userinput>/usr/local/etc/rc.d/slapd
	  start</userinput>.  This should start
	  <application>OpenLDAP</application>.  Confirm that it is
	  listening on 389 with</para>

	<screen>&prompt.user; <userinput>sockstat -4 -p 389</userinput>
ldap     slapd      3261  7  tcp4   *:389                 *:*</screen>
      </sect3>

      <sect3 id="ldap-connect-client">
	<title>Configuring the Client</title>

	<para>Install the <filename
	    role="package">net/openldap24-client</filename> port for the
	  <application>OpenLDAP</application> libraries.  The client
	  machines will always have <application>OpenLDAP</application>
	  libraries since that is all <filename
	    role="package">security/pam_ldap</filename> and <filename
	    role="package">net/nss_ldap</filename> support, at least for the
	  moment.</para>

	<para>The configuration file for the
	  <application>OpenLDAP</application> libraries is
	  <filename>/usr/local/etc/openldap/ldap.conf</filename>.  Edit
	  this file to contain the following values:</para>

	<programlisting>base dc=example,dc=org
uri ldap://server.example.org/
ssl start_tls
tls_cacert /path/to/your/cacert.crt</programlisting>

	<note>
	  <para>It is important that your clients have access to
	    <filename>cacert.crt</filename>, otherwise they will not be
	    able to connect.</para>
	</note>

	<note>
	  <para>There are two files called
	    <filename>ldap.conf</filename>.  The first is this file, which
	    is for the <application>OpenLDAP</application> libraries and
	    defines how to talk to the server.  The second is
	    <filename>/usr/local/etc/ldap.conf</filename>, and is for
	    <application>pam_ldap</application>.</para>
	</note>

	<para>At this point you should be able to run
	  <userinput>ldapsearch -Z</userinput> on the client machine;
	  <option>-Z</option> means <quote>use TLS</quote>.  If you
	  encounter an error, then something is configured wrong; most
	  likely it is your certificates.  Use &man.openssl.1;'s
	  <command>s_client</command> and <command>s_server</command> to
	  ensure you have them configured and signed properly.</para>
      </sect3>
    </sect2>

    <sect2 id="ldap-database">
      <title>Entries in the Database</title>

      <para>Authentication against an LDAP directory is generally
	accomplished by attempting to bind to the directory as the connecting user.
	This is done by establishing a <quote>simple</quote>
	bind on the directory with the user name supplied.  If there is an
	entry with the <literal>uid</literal> equal to the user name and
	that entry's <literal>userPassword</literal> attribute matches the
	password supplied, then the bind is successful.</para>

      <para>The first thing we have to do is figure out is where in the
	directory our users will live.</para>

      <para>The base entry for our database is
	<literal>dc=example,dc=org</literal>.  The default location for
	users that most clients seem to expect is something like
	<literal>ou=people,<replaceable>base</replaceable></literal>, so
	that is what will be used here.  However keep in mind that this is
	configurable.</para>

      <para>So the ldif entry for the <literal>people</literal>
	organizational unit will look like:</para>

      <programlisting>dn: ou=people,dc=example,dc=org
objectClass: top
objectClass: organizationalUnit
ou: people</programlisting>

      <para>All users will be created as subentries of this
	organizational unit.</para>

      <para>Some thought might be given to the object class your users
	will belong to.  Most tools by default will use
	<literal>people</literal>, which is fine if you simply want to
	provide entries against which to authenticate.  However, if you
	are going to store user information in the LDAP database as well,
	you will probably want to use <literal>inetOrgPerson</literal>,
	which has many useful attributes.  In either case, the relevant
	schemas need to be loaded in
	<filename>slapd.conf</filename>.</para>

      <para>For this example we will use the <literal>person</literal>
	object class.  If you are using <literal>inetOrgPerson</literal>,
	the steps are basically identical, except that the
	<literal>sn</literal> attribute is required.</para>

      <para>To add a user <literal>testuser</literal>, the ldif would
	be:</para>

      <programlisting>dn: uid=tuser,ou=people,dc=example,dc=org
objectClass: person
objectClass: posixAccount
objectClass: shadowAccount
objectClass: top
uidNumber: 10000
gidNumber: 10000
homeDirectory: /home/tuser
loginShell: /bin/csh
uid: tuser
cn: tuser</programlisting>

      <para>I start my LDAP users' UIDs at 10000 to avoid collisions with
	system accounts; you can configure whatever number you wish here,
	as long as it's less than 65536.</para>

      <para>We also need group entries.  They are as configurable as
	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>

      <para>To enter these into your database, you can use
	<command>slapadd</command> or <command>ldapadd</command>
	on a file containing these entries.  Alternatively, you can use
	<filename role="package">sysutils/ldapvi</filename>.</para>

      <para>The <command>ldapsearch</command> utility on the client machine
	should now return these entries.  If it does, your database is
	properly configured to be used as an LDAP authentication server.</para>
    </sect2>
  </sect1>

  <sect1 id="client">
    <title>Client Configuration</title>

    <para>The client should already have
      <application>OpenLDAP</application> libraries from <xref
	linkend="ldap-connect-client"/>, but if you are installing several
      client machines you will need to install <filename
	role="package">net/openldap24-client</filename> on each of
      them.</para>

    <para>&os; requires two ports to be installed to authenticate
      against an LDAP server, <filename
	role="package">security/pam_ldap</filename> and <filename
	role="package">net/nss_ldap</filename>.</para>

    <sect2 id="client-auth">
      <title>Authentication</title>

      <para><filename role="package">security/pam_ldap</filename> is
	configured via <filename>/usr/local/etc/ldap.conf</filename>.</para>

      <note>
	<para>This is a <emphasis>different file</emphasis> than the
	  <application>OpenLDAP</application> library functions'
	  configuration file,
	  <filename>/usr/local/etc/openldap/ldap.conf</filename>; however,
	  it takes many of the same options; in fact it is a superset of
	  that file.  For the rest of this section, references to
	  <filename>ldap.conf</filename> will mean
	  <filename>/usr/local/etc/ldap.conf</filename>.</para>
      </note>

      <para>Thus, we will want to copy all of our original
	configuration parameters from
	<filename>openldap/ldap.conf</filename> to the new
	<filename>ldap.conf</filename>.  Once this is done, we want to
	tell <filename role="package">security/pam_ldap</filename> what to
	look for on the directory server.</para>

      <para>We are identifying our users with the <literal>uid</literal>
	attribute.  To configure this (though it is the default), set the
	<literal>pam_login_attribute</literal> directive in
	<filename>ldap.conf</filename>:</para>

      <example id="set-pam-login-attr">
	<title>Setting <literal>pam_login_attribute</literal></title>

	<programlisting>pam_login_attribute uid</programlisting>
      </example>

      <para>With this set, <filename
	  role="package">security/pam_ldap</filename> will search the entire
	LDAP directory under <literal>base</literal> for the value
	<literal>uid=<replaceable>username</replaceable></literal>.  If it
	finds one and only one entry, it will attempt to bind as that user
	with the password it was given.  If it binds correctly, then it
	will allow access.  Otherwise it will fail.</para>

      <sect3 id="client-auth-pam">
	<title>PAM</title>

	<para>PAM, which stands for <quote>Pluggable Authentication
	  Modules</quote>, is the method by which &os; authenticates most
	  of its sessions.  To tell &os; we wish to use an LDAP server, we
	  will have to add a line to the appropriate PAM file.</para>

	<para>Most of the time the appropriate PAM file is
	  <filename>/etc/pam.d/sshd</filename>, if you want to use
	  <application>SSH</application> (remember to set the relevant
	  options in <filename>/etc/ssh/sshd_config</filename>, otherwise
	  <application>SSH</application> will not use PAM).</para>

	<para>To use PAM for authentication, add the line</para>

	<programlisting>auth  sufficient  /usr/local/lib/pam_ldap.so  no_warn</programlisting>

	<para>Exactly where this line shows up in the file and which
	  options appear in the fourth column determine the exact behavior
	  of the authentication mechanism; see &man.pam.d.5;</para>

	<para>With this configuration you should be able to authenticate
	  a user against an LDAP directory.
	  <application>PAM</application> will perform a bind with your
	  credentials, and if successful will tell
	  <application>SSH</application> to allow access.</para>

	<para>However it is not a good idea to allow
	  <emphasis>every</emphasis> user in the directory into
	  <emphasis>every</emphasis> client machine.  With the
	  current configuration, all that a user needs to log into a
	  machine is an LDAP entry.  Fortunately there are a few ways to
	  restrict user access.</para>

	<para><filename>ldap.conf</filename> supports a
	  <literal>pam_groupdn</literal> directive; every account that
	  connects to this machine needs to be a member of the group
	  specified here.  For example, if you have</para>

	<programlisting>pam_groupdn cn=servername,ou=accessgroups,dc=example,dc=org</programlisting>

	<para>in <filename>ldap.conf</filename>, then only members of
	  that group will be able to log in.  There are a few things to
	  bear in mind, however.</para>

	<para>Members of this group are specified in one or more
	  <literal>memberUid</literal> attributes, and each attribute must
	  have the full distinguished name of the member.  So
	  <literal>memberUid: someuser</literal> will not work; it must
	  be:</para>

	<programlisting>memberUid: uid=someuser,ou=people,dc=example,dc=org</programlisting>

	<para>Additionally, this directive is not checked in PAM during
	  authentication, it is checked during account management, so you
	  will need a second line in your PAM files under
	  <literal>account</literal>.  This will require, in turn,
	  <emphasis>every</emphasis> user to be listed in the group, which
	  is not necessarily what we want.  To avoid blocking users that
	  are not in LDAP, you should enable the
	  <literal>ignore_unknown_user</literal> attribute.  Finally, you
	  should set the <literal>ignore_authinfo_unavail</literal> option
	  so that you are not locked out of every computer when the LDAP
	  server is unavailable.</para>

	<para>Your <filename>pam.d/sshd</filename> might then end up
	  looking like this:</para>

	<example id="pam">
	  <title>Sample <filename>pam.d/sshd</filename></title>

	  <programlisting>auth            required        pam_nologin.so          no_warn
auth            sufficient      pam_opie.so             no_warn no_fake_prompts
auth            requisite       pam_opieaccess.so       no_warn allow_local
auth            sufficient      /usr/local/lib/pam_ldap.so      no_warn
auth            required        pam_unix.so             no_warn try_first_pass

account         required        pam_login_access.so
account         required        /usr/local/lib/pam_ldap.so      no_warn ignore_authinfo_unavail ignore_unknown_user</programlisting>
	</example>

	<note>
	  <para>Since we are adding these lines specifically to
	  <filename>pam.d/sshd</filename>, this will only have an effect
	  on <application>SSH</application> sessions.  LDAP users will
	  be unable to log in at the console.  To change this behavior,
	  examine the other files in <filename>/etc/pam.d</filename> and
	  modify them accordingly.</para>
	</note>
      </sect3>
    </sect2>

    <sect2 id="client-nss">
      <title>Name Service Switch</title>

      <para><application>NSS</application> is the service that maps
	attributes to names.  So, for example, if a file is owned by user
	<literal>1001</literal>, an application will query
	<application>NSS</application> for the name of
	<literal>1001</literal>, and it might get <literal>bob</literal>
	or <literal>ted</literal> or whatever the user's name is.</para>

      <para>Now that our user information is kept in LDAP, we need to
	tell <application>NSS</application> to look there when
	queried.</para>

      <para>The <filename role="package">net/nss_ldap</filename> port
	does this.  It uses the same configuration file as <filename
	  role="package">security/pam_ldap</filename>, and should not need
	any extra parameters once it is installed.  Instead, what is left
	is simply to edit <filename>/etc/nsswitch.conf</filename> to take
	advantage of the directory.  Simply replace the following
	lines:</para>

      <programlisting>group: compat
passwd: compat</programlisting>

      <para>with</para>

      <programlisting>group: files ldap
passwd: files ldap</programlisting>

      <para>This will allow you to map usernames to UIDs and UIDs to
	usernames.</para>

      <para>Congratulations!  You should now have working LDAP
	authentication.</para>
    </sect2>

    <sect2 id="caveats">
      <title>Caveats</title>

      <para>Unfortunately, as of the time this was written &os; did not
	support changing user passwords with &man.passwd.1;.  Because of
	this, most administrators are left to implement a solution
	themselves.  I provide some examples here.  Note that if you write
	your own password change script, there are some security issues
	you should be made aware of; see <xref
	  linkend="security-passwd"/></para>

      <example id="chpw-shell">
	<title>Shell script for changing passwords</title>

	<programlisting><![CDATA[#!/bin/sh

stty -echo
read -p "Old Password: " oldp; echo
read -p "New Password: " np1; echo
read -p "Retype New Password: " np2; echo
stty echo

if [ "$np1" != "$np2" ]; then
  echo "Passwords do not match."
  exit 1
fi

ldappasswd -D uid="$USER",ou=people,dc=example,dc=org \
  -w "$oldp" \
  -a "$oldp" \
  -s "$np1"]]></programlisting>
      </example>

      <caution>
	<para>This script does hardly any error checking, but more
	  important it is very cavalier about how it stores your
	  passwords.  If you do anything like this, at least adjust
	  the <literal>security.bsd.see_other_uids</literal>
	  sysctl value:</para>

	<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>

      <example id="chpw-ruby">
	<title>Ruby 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>
      </example>

      <para>Although not guaranteed to be free of security holes (the
	password is kept in memory, for example) this is cleaner and more
	flexible than a simple <command>sh</command> script.</para>
    </sect2>
  </sect1>

  <sect1 id="secure">
    <title>Security Considerations</title>

    <para>Now that your machines (and possibly other services) are
      authenticating against your LDAP server, this server needs to be
      protected at least as well as
      <filename>/etc/master.passwd</filename> would be on a regular
      server, and possibly even more so since a broken or cracked LDAP
      server would break every client service.</para>

    <para>Remember, this section is not exhaustive.  You should
      continually review your configuration and procedures for
      improvements.</para>

    <sect2 id="secure-readonly">
      <title>Setting attributes read-only</title>

      <para>Several attributes in LDAP should be read-only.  If left
	writable by the user, for example, a user could change his
	<literal>uidNumber</literal> attribute to <literal>0</literal> and
	get <username>root</username> access!</para>

      <para>To begin with, the <literal>userPassword</literal> attribute
	should not be world-readable.  By default, anyone who can connect
	to the LDAP server can read this attribute.  To disable this, put
	the following in <filename>slapd.conf</filename>:</para>

      <example id="hide-userpass">
	<title>Hide passwords</title>

	<programlisting>access to dn.subtree="ou=people,dc=example,dc=org"
  attrs=userPassword
  by self write
  by anonymous auth
  by * none

access to *
  by self write
  by * read</programlisting>
      </example>

      <para>This will disallow reading of the
	<literal>userPassword</literal> attribute, while still allowing
	users to change their own passwords.</para>

      <para>Additionally, you'll want to keep users from changing some
	of their own attributes.  By default, users can change any
	attribute (except for those which the LDAP schemas themselves deny
	changes), such as <literal>uidNumber</literal>.  To close this
	hole, modify the above to</para>

      <example id="attrib-readonly">
	<title>Read-only attributes</title>

	<programlisting>access to dn.subtree="ou=people,dc=example,dc=org"
  attrs=userPassword
  by self write
  by anonymous auth
  by * none

access to attrs=homeDirectory,uidNumber,gidNumber
  by * read

access to *
  by self write
  by * read</programlisting>
      </example>

      <para>This will stop users from being able to masquerade as other
	users.</para>
    </sect2>

    <sect2 id="secure-root">
      <title><username>Root</username> account definition</title>

      <para>Often the <username>root</username> or manager account for
	the LDAP service will be defined in the configuration file.
	<application>OpenLDAP</application> supports this, for example,
	and it works, but it can lead to trouble if
	<filename>slapd.conf</filename> is compromised.  It may be better
	to use this only to bootstrap yourself into LDAP, and then define
	a <username>root</username> account there.</para>

      <para>Even better is to define accounts that have limited
	permissions, and omit a <username>root</username> account entirely.
	For example, users to can add or remove user accounts are added to
	one group, but they cannot themselves change the membership of
	this group.  Such a security policy would help mitigate the effects
	of a leaked password.</para>

      <sect3 id="manager-acct">
	<title>Creating a management group</title>

	<para>Say you want your IT department to be able to change home
	  directories for users, but you don't want all of them to be able
	  to add or remove users.  The way to do this is to add a group
	  for these admins:</para>

	<example id="manager-acct-dn">
	  <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
memberUid: uid=tuser,ou=people,dc=example,dc=org
memberUid: uid=user2,ou=people,dc=example,dc=org</programlisting>
	</example>

	<para>And then change the permissions attributes in
	  <filename>slapd.conf</filename>:</para>

	<example id="management-acct-acl">
	  <title>ACLs for a home directory management group</title>

	<programlisting>access to dn.subtree="ou=people,dc=example,dc=org"
  attr=homeDirectory
  by dn="cn=homemanagement,dc=example,dc=org"
  dnattr=memberUid write</programlisting>
	</example>

	<para>Now <username>tuser</username> and <username>user2</username>
        can change other users' home directories.</para>

	<para>In this example we've given a subset of administrative
	  power to certain users without giving them power in other
	  domains.  The idea is that soon no single user account has the
	  power of a <username>root</username> account, but every power
	  root had is had by at least one user.  The <username>root</username>
	  account then becomes unnecessary and can be removed.</para>
      </sect3>
    </sect2>

    <sect2 id="security-passwd">
      <title>Password storage</title>

      <para>By default <application>OpenLDAP</application> will store
	the value of the <literal>userPassword</literal> attribute as it
	stores any other data: in the clear.  Most of the time it is base
	64 encoded, which provides enough protection to keep an honest
	administrator from knowing your password, but little else.</para>

      <para>It is a good idea, then, to store passwords in a more secure
	format, such as SSHA (salted SHA).  This is done by whatever
	program you use to change users' passwords.</para>
    </sect2>
  </sect1>

  <appendix id="useful">
    <title>Useful Aids</title>

    <para>There are a few other programs that might be useful,
      particularly if you have many users and do not want to configure
      everything manually.</para>

    <para><filename role="package">security/pam_mkhomedir</filename> is
      a PAM module that always succeeds; its purpose is to create home
      directories for users which do not have them.  If you have dozens of
      client servers and hundreds of users, it is much easier to use this
      and set up skeleton directories than to prepare every home
      directory.</para>

    <para><filename role="package">sysutils/cpu</filename> is a
      &man.pw.8;-like utility that can be used to manage users in the LDAP
      directory.  You can call it directly, or wrap scripts around it.  It
      can handle both TLS (with the <option>-x</option> flag) and
      SSL (directly).</para>

    <para><filename role="package">sysutils/ldapvi</filename> is a great
      utility for editing LDAP values in an LDIF-like syntax.  The
      directory (or subsection of the directory) is presented in the
      editor chosen by the <envar>EDITOR</envar> environment variable.
      This makes it easy to enable large-scale changes in the directory
      without having to write a custom tool.</para>

    <para><filename role="package">security/openssh-portable</filename>
      has the ability to contact an LDAP server to verify
      <application>SSH</application> keys.  This is extremely nice if you
      have many servers and do not want to copy your public keys across
      all of them.</para>
  </appendix>

  <appendix id="ssl-ca">
    <title><application>OpenSSL</application> Certificates For LDAP</title>

    <para>If you are hosting two or more LDAP servers, you will probably
      not want to use self-signed certificates, since each client will
      have to be configured to work with each certificate.  While this is
      possible, it is not nearly as simple as creating your own
      certificate authority, and signing your servers' certificates with
      that.</para>

    <para>The steps here are presented as they are with very little
      attempt at explaining what is going on&mdash;further explanation
      can be found in &man.openssl.1; and its friends.</para>

    <para>To create a certificate authority, we simply need a
      self-signed certificate and key.  The steps for this again
      are</para>

    <example id="make-cert">
      <title>Creating a certificate</title>

      <screen>&prompt.user; <userinput>openssl genrsa -out root.key 1024</userinput>
&prompt.user; <userinput>openssl req -new -key root.key -out root.csr</userinput>
&prompt.user; <userinput>openssl x509 -req -days 1024 -in root.csr -signkey root.key -out root.crt</userinput></screen>
    </example>

    <para>These will be your root CA key and certificate.  You will
      probably want to encrypt the key and store it in a cool, dry place;
      anyone with access to it can masquerade as one of your LDAP
      servers.</para>

    <para>Next, using the first two steps above create a key
      <filename>ldap-server-one.key</filename> and certificate signing
      request <filename>ldap-server-one.csr</filename>.  Once you sign the
      signing request with <filename>root.key</filename>, you will be able
      to use <filename>ldap-server-one.*</filename> on your LDAP
      servers.</para>

    <note>
      <para>Do not forget to use the fully qualified domain name for the
	<quote>common name</quote> attribute when generating the
	certificate signing request; otherwise clients will reject a
	connection with you, and it can be very tricky to diagnose.</para>
    </note>

    <para>To sign the key, use <option>-CA</option> and
      <option>-CAkey</option> instead of
      <option>-signkey</option>:</para>

    <example id="ca-sign">
      <title>Signing as a certificate authority</title>

      <screen>&prompt.user; <userinput>openssl x509 -req -days 1024 \
-in ldap-server-one.csr -CA root.crt -CAkey root.key \
-out ldap-server-one.crt</userinput></screen>
    </example>

    <para>The resulting file will be the certificate that you can use on
      your LDAP servers.</para>

    <para>Finally, for clients to trust all your servers, distribute
      <filename>root.crt</filename> (the <emphasis>certificate</emphasis>,
      not the key!) to each client, and specify it in the
      <literal>TLSCACertificateFile</literal> directive in
      <filename>ldap.conf</filename>.</para>
  </appendix>
</article>