Annotation of wikisrc/guide/pam.mdwn, revision 1.3
1.1 jdf 1: # Pluggable Authentication Modules (PAM)
2:
3: ## About
4:
1.3 ! jdf 5: This article describes the underlying principles and mechanisms of the
! 6: *Pluggable Authentication Modules (PAM)* library, and explains how to configure
1.1 jdf 7: PAM, how to integrate PAM into applications, and how to write PAM modules.
8:
1.2 jdf 9: See on the bottom of the page for the license of this text.
1.1 jdf 10:
11: ## Introduction
12:
1.3 ! jdf 13: The Pluggable Authentication Modules (PAM) library is a generalized API for
! 14: authentication-related services which allows a system administrator to add new
! 15: authentication methods simply by installing new PAM modules, and to modify
1.1 jdf 16: authentication policies by editing configuration files.
17:
1.3 ! jdf 18: PAM was defined and developed in 1995 by Vipin Samar and Charlie Lai of Sun
! 19: Microsystems, and has not changed much since. In 1997, the Open Group published
! 20: the X/Open Single Sign-on (XSSO) preliminary specification, which standardized
! 21: the PAM API and added extensions for single (or rather integrated) sign-on. At
! 22: the time of this writing, this specification has not yet been adopted as a
1.1 jdf 23: standard.
24:
1.3 ! jdf 25: Although this article focuses primarily on FreeBSD 5.x and NetBSD 3.x, which
! 26: both use OpenPAM, it should be equally applicable to FreeBSD 4.x, which uses
1.1 jdf 27: Linux-PAM, and other operating systems such as Linux and Solaris.
28:
29: ## Terms and conventions
30:
31: ### Definitions
32:
1.3 ! jdf 33: The terminology surrounding PAM is rather confused. Neither Samar and Lai's
! 34: original paper nor the XSSO specification made any attempt at formally defining
! 35: terms for the various actors and entities involved in PAM, and the terms that
! 36: they do use (but do not define) are sometimes misleading and ambiguous. The
! 37: first attempt at establishing a consistent and unambiguous terminology was a
! 38: whitepaper written by Andrew G. Morgan (author of Linux-PAM) in 1999. While
! 39: Morgan's choice of terminology was a huge leap forward, it is in this author's
! 40: opinion by no means perfect. What follows is an attempt, heavily inspired by
! 41: Morgan, to define precise and unambiguous terms for all actors and entities
1.1 jdf 42: involved in PAM.
43:
1.3 ! jdf 44: * *account* -- The set of credentials the applicant is requesting from the
1.1 jdf 45: arbitrator.
46:
47: * *applicant* -- The user or entity requesting authentication.
48:
1.3 ! jdf 49: * *arbitrator* -- The user or entity who has the privileges necessary to verify
1.1 jdf 50: the applicant's credentials and the authority to grant or deny the request.
51:
1.3 ! jdf 52: * *chain* -- A sequence of modules that will be invoked in response to a PAM
! 53: request. The chain includes information about the order in which to invoke
! 54: the modules, what arguments to pass to them, and how to interpret the
1.1 jdf 55: results.
56:
1.3 ! jdf 57: * *client* -- The application responsible for initiating an authentication
! 58: request on behalf of the applicant and for obtaining the necessary
1.1 jdf 59: authentication information from him.
60:
1.3 ! jdf 61: * *facility* -- One of the four basic groups of functionality provided by PAM:
! 62: authentication, account management, session management and authentication
1.1 jdf 63: token update.
64:
1.3 ! jdf 65: * *module* -- A collection of one or more related functions implementing a
! 66: particular authentication facility, gathered into a single (normally
1.1 jdf 67: dynamically loadable) binary file and identified by a single name.
68:
1.3 ! jdf 69: * *policy* -- The complete set of configuration statements describing how to
! 70: handle PAM requests for a particular service. A policy normally consists of
! 71: four chains, one for each facility, though some services do not use all four
1.1 jdf 72: facilities.
73:
1.3 ! jdf 74: * *server* -- The application acting on behalf of the arbitrator to converse
! 75: with the client, retrieve authentication information, verify the applicant's
1.1 jdf 76: credentials and grant or deny requests.
77:
1.3 ! jdf 78: * *service* -- A class of servers providing similar or related functionality
! 79: and requiring similar authentication. PAM policies are defined on a
! 80: per-service basis, so all servers that claim the same service name will be
1.1 jdf 81: subject to the same policy.
82:
1.3 ! jdf 83: * *session* -- The context within which service is rendered to the applicant by
! 84: the server. One of PAM's four facilities, session management, is concerned
1.1 jdf 85: exclusively with setting up and tearing down this context.
86:
1.3 ! jdf 87: * *token* -- A chunk of information associated with the account, such as a
! 88: password or passphrase, which the applicant must provide to prove his
1.1 jdf 89: identity.
90:
1.3 ! jdf 91: * *transaction* -- A sequence of requests from the same applicant to the same
! 92: instance of the same server, beginning with authentication and session set-up
1.1 jdf 93: and ending with session tear-down.
94:
95: ### Usage examples
96:
1.3 ! jdf 97: This section aims to illustrate the meanings of some of the terms defined above
! 98: by way of a handful of simple examples.
1.1 jdf 99:
100: #### Client and server are one
101:
1.3 ! jdf 102: This simple example shows `alice`
! 103: [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386)'ing to
! 104: `root`:
1.1 jdf 105:
106: $ whoami
107: alice
108: $ ls -l `which su`
109: -r-sr-xr-x 1 root wheel 10744 Dec 6 19:06 /usr/bin/su
110: $ su -
111: Password: xi3kiune
112: # whoami
113: root
114:
115: * The applicant is `alice`.
116: * The account is `root`.
1.3 ! jdf 117: * The [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386)
! 118: process is both client and server.
1.1 jdf 119: * The authentication token is `xi3kiune`.
1.3 ! jdf 120: * The arbitrator is `root`, which is why
! 121: [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386) is
! 122: setuid `root`.
1.1 jdf 123:
124: #### Client and server are separate
125:
1.3 ! jdf 126: The example below shows `eve` try to initiate an
! 127: [ssh(1)](http://netbsd.gw.com/cgi-bin/man-cgi?ssh+1+NetBSD-5.0.1+i386)
! 128: connection to `login.example.com`, ask to log in as `bob`, and succeed. Bob
1.1 jdf 129: should have chosen a better password!
130:
131: $ whoami
132: eve
133: $ ssh bob@login.example.com
134: bob@login.example.com's password: god
135: Last login: Thu Oct 11 09:52:57 2001 from 192.168.0.1
136: NetBSD 3.0 (LOGIN) #1: Thu Mar 10 18:22:36 WET 2005
137:
138: Welcome to NetBSD!
139: $
140:
141: * The applicant is `eve`.
1.3 ! jdf 142: * The client is Eve's
! 143: [ssh(1)](http://netbsd.gw.com/cgi-bin/man-cgi?ssh+1+NetBSD-5.0.1+i386)
! 144: process.
! 145: * The server is the
! 146: [sshd(8)](http://netbsd.gw.com/cgi-bin/man-cgi?sshd+8+NetBSD-5.0.1+i386)
! 147: process on `login.example.com`
1.1 jdf 148: * The account is `bob`.
149: * The authentication token is `god`.
150: * Although this is not shown in this example, the arbitrator is `root`.
151:
152: #### Sample policy
153:
154: The following is FreeBSD's default policy for `sshd`:
155:
156: sshd auth required pam_nologin.so no_warn
157: sshd auth required pam_unix.so no_warn try_first_pass
158: sshd account required pam_login_access.so
159: sshd account required pam_unix.so
160: sshd session required pam_lastlog.so no_fail
161: sshd password required pam_permit.so
162:
1.3 ! jdf 163: * This policy applies to the `sshd` service (which is not necessarily
! 164: restricted to the
! 165: [sshd(8)](http://netbsd.gw.com/cgi-bin/man-cgi?sshd+8+NetBSD-5.0.1+i386)
1.1 jdf 166: server.)
167:
168: * `auth`, `account`, `session` and `password` are facilities.
169:
1.3 ! jdf 170: * `pam_nologin.so`, `pam_unix.so`, `pam_login_access.so`, `pam_lastlog.so` and
! 171: `pam_permit.so` are modules. It is clear from this example that `pam_unix.so`
1.1 jdf 172: provides at least two facilities (authentication and account management.)
173:
174: There are some differences between FreeBSD and NetBSD PAM policies:
175:
176: * By default, every configuration is done under `/etc/pam.d`.
177:
1.3 ! jdf 178: * If configuration is non-existent, you will not have access to the system, in
1.1 jdf 179: contrast with FreeBSD that has a default policy of allowing authentication.
180:
1.3 ! jdf 181: * For authentication, NetBSD forces at least one `required`, `requisite` or
1.1 jdf 182: `binding` module to be present.
183:
184: ## PAM Essentials
185:
186: ### Facilities and primitives
187:
1.3 ! jdf 188: The PAM API offers six different authentication primitives grouped in four
1.1 jdf 189: facilities, which are described below.
190:
1.3 ! jdf 191: * `auth` -- *Authentication.* This facility concerns itself with authenticating
! 192: the applicant and establishing the account credentials. It provides two
1.1 jdf 193: primitives:
194:
1.3 ! jdf 195: * [pam\_authenticate(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_authenticate+3+NetBSD-5.0.1+i386)
! 196: authenticates the applicant, usually by requesting an authentication token
! 197: and comparing it with a value stored in a database or obtained from an
1.1 jdf 198: authentication server.
199:
1.3 ! jdf 200: * [pam\_setcred(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_setcred+3+NetBSD-5.0.1+i386)
! 201: establishes account credentials such as user ID, group membership and
1.1 jdf 202: resource limits.
203:
1.3 ! jdf 204: * `account` -- *Account management.* This facility handles
! 205: non-authentication-related issues of account availability, such as access
! 206: restrictions based on the time of day or the server's work load. It provides
1.1 jdf 207: a single primitive:
208:
1.3 ! jdf 209: * [pam\_acct\_mgmt(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_acct_mgmt+3+NetBSD-5.0.1+i386)
1.1 jdf 210: verifies that the requested account is available.
211:
1.3 ! jdf 212: * `session` -- *Session management.* This facility handles tasks associated
! 213: with session set-up and tear-down, such as login accounting. It provides two
1.1 jdf 214: primitives:
215:
1.3 ! jdf 216: * [pam\_open\_session(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_open_session+3+NetBSD-5.0.1+i386)
! 217: performs tasks associated with session set-up: add an entry in the `utmp`
1.1 jdf 218: and `wtmp` databases, start an SSH agent, etc.
219:
1.3 ! jdf 220: * [pam\_close\_session(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_close_session+3+NetBSD-5.0.1+i386)
! 221: performs tasks associated with session tear-down: add an entry in the
1.1 jdf 222: `utmp` and `wtmp` databases, stop the SSH agent, etc.
223:
1.3 ! jdf 224: * `password` -- *Password management.* This facility is used to change the
! 225: authentication token associated with an account, either because it has
! 226: expired or because the user wishes to change it. It provides a single
1.1 jdf 227: primitive:
228:
1.3 ! jdf 229: * [pam\_chauthtok(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_chauthtok+3+NetBSD-5.0.1+i386)
! 230: changes the authentication token, optionally verifying that it is
1.1 jdf 231: sufficiently hard to guess, has not been used previously, etc.
232:
233: ### Modules
234:
1.3 ! jdf 235: Modules are a very central concept in PAM; after all, they are the *M* in *PAM*.
! 236: A PAM module is a self-contained piece of program code that implements the
! 237: primitives in one or more facilities for one particular mechanism; possible
! 238: mechanisms for the authentication facility, for instance, include the UNIX®
1.1 jdf 239: password database, NIS, LDAP and Radius.
240:
241: #### Module Naming
242:
1.3 ! jdf 243: FreeBSD and NetBSD implement each mechanism in a single module, named
! 244: `pam_mechanism`.so (for instance, `pam_unix.so` for the UNIX mechanism.) Other
! 245: implementations sometimes have separate modules for separate facilities, and
! 246: include the facility name as well as the mechanism name in the module name. To
! 247: name one example, Solaris has a `pam_dial_auth.so.1` module which is commonly
! 248: used to authenticate dialup users. Also, almost every module has a man page with
! 249: the same name, i.e.:
! 250: [pam\_unix(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_unix+8+NetBSD-5.0.1+i386)
1.1 jdf 251: explains how the `pam_unix.so` module works.
252:
253: #### Module Versioning
254:
1.3 ! jdf 255: FreeBSD's original PAM implementation, based on Linux-PAM, did not use version
! 256: numbers for PAM modules. This would commonly cause problems with legacy
! 257: applications, which might be linked against older versions of the system
! 258: libraries, as there was no way to load a matching version of the required
1.1 jdf 259: modules.
260:
1.3 ! jdf 261: OpenPAM, on the other hand, looks for modules that have the same version number
! 262: as the PAM library (currently 2 in FreeBSD and 0 in NetBSD), and only falls back
! 263: to an unversioned module if no versioned module could be loaded. Thus legacy
! 264: modules can be provided for legacy applications, while allowing new (or newly
1.1 jdf 265: built) applications to take advantage of the most recent modules.
266:
1.3 ! jdf 267: Although Solaris PAM modules commonly have a version number, they're not truly
! 268: versioned, because the number is a part of the module name and must be included
1.1 jdf 269: in the configuration.
270:
271: #### Module Path
272:
1.3 ! jdf 273: There isn't a common directory for storing PAM modules. Under FreeBSD, they are
! 274: located at `/usr/lib` and, under NetBSD, you can find them in
1.1 jdf 275: `/usr/lib/security`.
276:
277: ### Chains and policies
278:
1.3 ! jdf 279: When a server initiates a PAM transaction, the PAM library tries to load a
! 280: policy for the service specified in the
! 281: [pam\_start(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_start+3+NetBSD-5.0.1+i386)
! 282: call. The policy specifies how authentication requests should be processed, and
! 283: is defined in a configuration file. This is the other central concept in PAM:
! 284: the possibility for the admin to tune the system security policy (in the wider
1.1 jdf 285: sense of the word) simply by editing a text file.
286:
1.3 ! jdf 287: A policy consists of four chains, one for each of the four PAM facilities. Each
! 288: chain is a sequence of configuration statements, each specifying a module to
! 289: invoke, some (optional) parameters to pass to the module, and a control flag
1.1 jdf 290: that describes how to interpret the return code from the module.
291:
1.3 ! jdf 292: Understanding the control flags is essential to understanding PAM configuration
1.1 jdf 293: files. There are a number of different control flags:
294:
1.3 ! jdf 295: * `binding` -- If the module succeeds and no earlier module in the chain has
! 296: failed, the chain is immediately terminated and the request is granted. If
! 297: the module fails, the rest of the chain is executed, but the request is
1.1 jdf 298: ultimately denied.
299:
1.3 ! jdf 300: This control flag was introduced by Sun in Solaris 9 (SunOS 5.9), and is also
! 301: supported by OpenPAM.
1.1 jdf 302:
1.3 ! jdf 303: * `required` -- If the module succeeds, the rest of the chain is executed, and
! 304: the request is granted unless some other module fails. If the module fails,
1.1 jdf 305: the rest of the chain is also executed, but the request is ultimately denied.
306:
1.3 ! jdf 307: * `requisite` -- If the module succeeds, the rest of the chain is executed, and
! 308: the request is granted unless some other module fails. If the module fails,
1.1 jdf 309: the chain is immediately terminated and the request is denied.
310:
1.3 ! jdf 311: * `sufficient` -- If the module succeeds and no earlier module in the chain has
! 312: failed, the chain is immediately terminated and the request is granted. If
! 313: the module fails, the module is ignored and the rest of the chain is
1.1 jdf 314: executed.
315:
1.3 ! jdf 316: As the semantics of this flag may be somewhat confusing, especially when it
! 317: is used for the last module in a chain, it is recommended that the `binding`
! 318: control flag be used instead if the implementation supports it.
1.1 jdf 319:
1.3 ! jdf 320: * `optional` -- The module is executed, but its result is ignored. If all
! 321: modules in a chain are marked `optional`, all requests will always be
1.1 jdf 322: granted.
323:
1.3 ! jdf 324: When a server invokes one of the six PAM primitives, PAM retrieves the chain for
! 325: the facility the primitive belongs to, and invokes each of the modules listed in
! 326: the chain, in the order they are listed, until it reaches the end, or determines
! 327: that no further processing is necessary (either because a `binding` or
! 328: `sufficient` module succeeded, or because a `requisite` module failed.) The
! 329: request is granted if and only if at least one module was invoked, and all
1.1 jdf 330: non-optional modules succeeded.
331:
1.3 ! jdf 332: Note that it is possible, though not very common, to have the same module listed
! 333: several times in the same chain. For instance, a module that looks up user names
! 334: and passwords in a directory server could be invoked multiple times with
! 335: different parameters specifying different directory servers to contact. PAM
! 336: treat different occurrences of the same module in the same chain as different,
1.1 jdf 337: unrelated modules.
338:
339: ### Transactions
340:
1.3 ! jdf 341: The lifecycle of a typical PAM transaction is described below. Note that if any
! 342: of these steps fails, the server should report a suitable error message to the
1.1 jdf 343: client and abort the transaction.
344:
1.3 ! jdf 345: 1. If necessary, the server obtains arbitrator credentials through a mechanism
! 346: independent of PAM -- most commonly by virtue of having been started by `root`,
1.1 jdf 347: or of being setuid `root`.
348:
1.3 ! jdf 349: 2. The server calls
! 350: [pam\_start(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_start+3+NetBSD-5.0.1+i386)
! 351: to initialize the PAM library and specify its service name and the target
1.1 jdf 352: account, and register a suitable conversation function.
353:
1.3 ! jdf 354: 3. The server obtains various information relating to the transaction (such as
! 355: the applicant's user name and the name of the host the client runs on) and
! 356: submits it to PAM using
1.1 jdf 357: [pam\_set\_item(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_set_item+3+NetBSD-5.0.1+i386).
358:
1.3 ! jdf 359: 4. The server calls
! 360: [pam\_authenticate(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_authenticate+3+NetBSD-5.0.1+i386)
1.1 jdf 361: to authenticate the applicant.
362:
1.3 ! jdf 363: 5. The server calls
! 364: [pam\_acct\_mgmt(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_acct_mgmt+3+NetBSD-5.0.1+i386)
! 365: to verify that the requested account is available and valid. If the password is
! 366: correct but has expired,
! 367: [pam\_acct\_mgmt(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_acct_mgmt+3+NetBSD-5.0.1+i386)
1.1 jdf 368: will return `PAM_NEW_AUTHTOK_REQD` instead of `PAM_SUCCESS`.
369:
1.3 ! jdf 370: 6. If the previous step returned `PAM_NEW_AUTHTOK_REQD`, the server now calls
! 371: [pam\_chauthtok(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_chauthtok+3+NetBSD-5.0.1+i386)
! 372: to force the client to change the authentication token for the requested
1.1 jdf 373: account.
374:
1.3 ! jdf 375: 7. Now that the applicant has been properly authenticated, the server calls
! 376: [pam\_setcred(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_setcred+3+NetBSD-5.0.1+i386)
! 377: to establish the credentials of the requested account. It is able to do this
! 378: because it acts on behalf of the arbitrator, and holds the arbitrator's
1.1 jdf 379: credentials.
380:
1.3 ! jdf 381: 8. Once the correct credentials have been established, the server calls
! 382: [pam\_open\_session(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_open_session+3+NetBSD-5.0.1+i386)
1.1 jdf 383: to set up the session.
384:
1.3 ! jdf 385: 9. The server now performs whatever service the client requested -- for
1.1 jdf 386: instance, provide the applicant with a shell.
387:
1.3 ! jdf 388: 10. Once the server is done serving the client, it calls
! 389: [pam\_close\_session(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_close_session+3+NetBSD-5.0.1+i386)
1.1 jdf 390: to tear down the session.
391:
1.3 ! jdf 392: 11. Finally, the server calls
! 393: [pam\_end(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_end+3+NetBSD-5.0.1+i386)
! 394: to notify the PAM library that it is done and that it can release whatever
1.1 jdf 395: resources it has allocated in the course of the transaction.
396:
397: ## PAM Configuration
398:
399: ### PAM policy files
400:
401: #### The `/etc/pam.conf` file
402:
1.3 ! jdf 403: The traditional PAM policy file is `/etc/pam.conf`. This file contains all the
! 404: PAM policies for your system. Each line of the file describes one step in a
1.1 jdf 405: chain, as shown below:
406:
407: login auth required pam_nologin.so no_warn
408:
1.3 ! jdf 409: The fields are, in order: service name, facility name, control flag, module
! 410: name, and module arguments. Any additional fields are interpreted as additional
1.1 jdf 411: module arguments.
412:
1.3 ! jdf 413: A separate chain is constructed for each service / facility pair, so while the
! 414: order in which lines for the same service and facility appear is significant,
! 415: the order in which the individual services and facilities are listed is not. The
! 416: examples in the original PAM paper grouped configuration lines by facility, and
! 417: the Solaris stock `pam.conf` still does that, but FreeBSD's stock configuration
! 418: groups configuration lines by service. Either way is fine; either way makes
1.1 jdf 419: equal sense.
420:
421: #### The `/etc/pam.d` directory
422:
1.3 ! jdf 423: OpenPAM and Linux-PAM support an alternate configuration mechanism, which is the
! 424: preferred mechanism in FreeBSD and NetBSD. In this scheme, each policy is
! 425: contained in a separate file bearing the name of the service it applies to.
1.1 jdf 426: These files are stored in `/etc/pam.d/`.
427:
1.3 ! jdf 428: These per-service policy files have only four fields instead of `pam.conf`'s
! 429: five: the service name field is omitted. Thus, instead of the sample `pam.conf`
! 430: line from the previous section, one would have the following line in
1.1 jdf 431: `/etc/pam.d/login`:
432:
433: auth required pam_nologin.so no_warn
434:
1.3 ! jdf 435: As a consequence of this simplified syntax, it is possible to use the same
! 436: policy for multiple services by linking each service name to a same policy file.
! 437: For instance, to use the same policy for the `su` and `sudo` services, one could
1.1 jdf 438: do as follows:
439:
440: # cd /etc/pam.d
441: # ln -s su sudo
442:
1.3 ! jdf 443: This works because the service name is determined from the file name rather than
! 444: specified in the policy file, so the same file can be used for multiple
1.1 jdf 445: differently-named services.
446:
1.3 ! jdf 447: Since each service's policy is stored in a separate file, the `pam.d` mechanism
! 448: also makes it very easy to install additional policies for third-party software
1.1 jdf 449: packages.
450:
451: #### The policy search order
452:
1.3 ! jdf 453: As we have seen above, PAM policies can be found in a number of places. If no
! 454: configuration file is found for a particular service, the `/etc/pam.d/other` is
! 455: used instead. If that file does not exist, `/etc/pam.conf` is searched for
1.1 jdf 456: entries matching he specified service or, failing that, the "other" service.
457:
1.3 ! jdf 458: It is essential to understand that PAM's configuration system is centered on
1.1 jdf 459: chains.
460:
461: ### Breakdown of a configuration line
462:
1.3 ! jdf 463: As explained in the [PAM policy files](chap-pam.html#pam-config-file "18.5.1.
! 464: PAM policy files") section, each line in `/etc/pam.conf` consists of four or
! 465: more fields: the service name, the facility name, the control flag, the module
1.1 jdf 466: name, and zero or more module arguments.
467:
1.3 ! jdf 468: The service name is generally (though not always) the name of the application
! 469: the statement applies to. If you are unsure, refer to the individual
1.1 jdf 470: application's documentation to determine what service name it uses.
471:
1.3 ! jdf 472: Note that if you use `/etc/pam.d/` instead of `/etc/pam.conf`, the service name
! 473: is specified by the name of the policy file, and omitted from the actual
1.1 jdf 474: configuration lines, which then start with the facility name.
475:
476: The facility is one of the four facility keywords described in the
477: [[Facilities and primitives|guide/pam#facilities-primitives]]] section.
478:
1.3 ! jdf 479: Likewise, the control flag is one of the four keywords described in the [[Chains
! 480: and policies|guide/pam#chains-policies]] section, describing how to interpret
! 481: the return code from the module. Linux-PAM supports an alternate syntax that
! 482: lets you specify the action to associate with each possible return code, but
! 483: this should be avoided as it is non-standard and closely tied in with the way
! 484: Linux-PAM dispatches service calls (which differs greatly from the way Solaris
1.1 jdf 485: and OpenPAM do it.) Unsurprisingly, OpenPAM does not support this syntax.
486:
487: ### Policies
488:
1.3 ! jdf 489: To configure PAM correctly, it is essential to understand how policies are
1.1 jdf 490: interpreted.
491:
1.3 ! jdf 492: When an application calls
! 493: [pam\_start(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_start+3+NetBSD-5.0.1+i386),
! 494: the PAM library loads the policy for the specified service and constructs four
! 495: module chains (one for each facility.) If one or more of these chains are empty,
! 496: the corresponding chains from the policy for the `other` service are
1.1 jdf 497: substituted.
498:
1.3 ! jdf 499: When the application later calls one of the six PAM primitives, the PAM library
! 500: retrieves the chain for the corresponding facility and calls the appropriate
! 501: service function in each module listed in the chain, in the order in which they
! 502: were listed in the configuration. After each call to a service function, the
! 503: module type and the error code returned by the service function are used to
! 504: determine what happens next. With a few exceptions, which we discuss below, the
1.1 jdf 505: following table applies:
506:
507: [[!table data="""
508: | `PAM_SUCCESS` | `PAM_IGNORE` | `other`
509: binding | if (!fail) break; | - | fail = true;
510: required | - | - | fail = true;
511: requisite | - | - | fail = true; break;
512: sufficient | if (!fail) break; | - | -
513: optional | - | - | -
514: """]]
515:
1.3 ! jdf 516: If `fail` is true at the end of a chain, or when a `break` is reached, the
! 517: dispatcher returns the error code returned by the first module that failed.
1.1 jdf 518: Otherwise, it returns `PAM_SUCCESS`.
519:
1.3 ! jdf 520: The first exception of note is that the error code `PAM_NEW_AUTHTOK_REQD` is
! 521: treated like a success, except that if no module failed, and at least one module
! 522: returned `PAM_NEW_AUTHTOK_REQD`, the dispatcher will return
1.1 jdf 523: `PAM_NEW_AUTHTOK_REQD`.
524:
1.3 ! jdf 525: The second exception is that
! 526: [pam\_setcred(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_setcred+3+NetBSD-5.0.1+i386)
1.1 jdf 527: treats `binding` and `sufficient` modules as if they were `required`.
528:
1.3 ! jdf 529: The third and final exception is that
! 530: [pam\_chauthtok(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_chauthtok+3+NetBSD-5.0.1+i386)
! 531: runs the entire chain twice (once for preliminary checks and once to actually
! 532: set the password), and in the preliminary phase it treats `binding` and
1.1 jdf 533: `sufficient` modules as if they were `required`.
534:
535: ## PAM modules
536:
537: ### Common Modules
538:
539: #### pam\_deny(8)
540:
1.3 ! jdf 541: The
! 542: [pam\_deny(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_deny+8+NetBSD-5.0.1+i386)
! 543: module is one of the simplest modules available; it responds to any request with
! 544: `PAM_AUTH_ERR`. It is useful for quickly disabling a service (add it to the top
1.1 jdf 545: of every chain), or for terminating chains of `sufficient` modules.
546:
547: #### pam\_echo(8)
548:
1.3 ! jdf 549: The
! 550: [pam\_echo(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_echo+8+NetBSD-5.0.1+i386)
! 551: module simply passes its arguments to the conversation function as a
! 552: `PAM_TEXT_INFO` message. It is mostly useful for debugging, but can also serve
! 553: to display messages such as `Unauthorized access will be prosecuted` before
1.1 jdf 554: starting the authentication procedure.
555:
556: #### pam\_exec(8)
557:
1.3 ! jdf 558: The
! 559: [pam\_exec(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_exec+8+NetBSD-5.0.1+i386)
! 560: module takes its first argument to be the name of a program to execute, and the
! 561: remaining arguments are passed to that program as command-line arguments. One
! 562: possible application is to use it to run a program at login time which mounts
1.1 jdf 563: the user's home directory.
564:
565: #### pam\_ftpusers(8)
566:
1.3 ! jdf 567: The
! 568: [pam\_ftpusers(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_ftpusers+8+NetBSD-5.0.1+i386)
! 569: module successes if and only if the user is listed in `/etc/ftpusers`.
! 570: Currently, in NetBSD, this module doesn't understand the extended syntax of
! 571: [ftpd(8)](http://netbsd.gw.com/cgi-bin/man-cgi?ftpd+8+NetBSD-5.0.1+i386), but
1.1 jdf 572: this will be fixed in the future.
573:
574: #### pam\_group(8)
575:
1.3 ! jdf 576: The
! 577: [pam\_group(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_group+8+NetBSD-5.0.1+i386)
! 578: module accepts or rejects applicants on the basis of their membership in a
! 579: particular file group (normally `wheel` for
! 580: [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386)). It is
! 581: primarily intended for maintaining the traditional behaviour of BSD
! 582: [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386), but has
! 583: many other uses, such as excluding certain groups of users from a particular
1.1 jdf 584: service.
585:
1.3 ! jdf 586: In NetBSD, there is an argument called `authenticate` in which the user is asked
1.1 jdf 587: to authenticate using his own password.
588:
589: #### pam\_guest(8)
590:
1.3 ! jdf 591: The
! 592: [pam\_guest(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_guest+8+NetBSD-5.0.1+i386)
! 593: module allows guest logins using fixed login names. Various requirements can be
! 594: placed on the password, but the default behaviour is to allow any password as
! 595: long as the login name is that of a guest account. The
! 596: [pam\_guest(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_guest+8+NetBSD-5.0.1+i386)
1.1 jdf 597: module can easily be used to implement anonymous FTP logins.
598:
599: #### pam\_krb5(8)
600:
1.3 ! jdf 601: The
! 602: [pam\_krb5(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_krb5+8+NetBSD-5.0.1+i386)
! 603: module provides functions to verify the identity of a user and to set user
! 604: specific credentials using Kerberos 5. It prompts the user for a password and
! 605: obtains a new Kerberos TGT for the principal. The TGT is verified by obtaining a
! 606: service ticket for the local host. The newly acquired credentials are stored in
! 607: a credential cache and the environment variable KRB5CCNAME is set appropriately.
! 608: The credentials cache should be destroyed by the user at logout with
1.1 jdf 609: [kdestroy(1)](http://netbsd.gw.com/cgi-bin/man-cgi?kdestroy+1+NetBSD-5.0.1+i386).
610:
611: #### pam\_ksu(8)
612:
1.3 ! jdf 613: The
! 614: [pam\_ksu(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_ksu+8+NetBSD-5.0.1+i386)
! 615: module provides only authentication services for Kerberos 5 to determine whether
! 616: or not the applicant is authorized to obtain the privileges of the target
1.1 jdf 617: account.
618:
619: #### pam\_lastlog(8)
620:
1.3 ! jdf 621: The
! 622: [pam\_lastlog(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_lastlog+8+NetBSD-5.0.1+i386)
! 623: module provides only session management services. It records the session in
! 624: [utmp(5)](http://netbsd.gw.com/cgi-bin/man-cgi?utmp+5+NetBSD-5.0.1+i386),
! 625: [utmpx(5)](http://netbsd.gw.com/cgi-bin/man-cgi?utmpx+5+NetBSD-5.0.1+i386),
! 626: [wtmp(5)](http://netbsd.gw.com/cgi-bin/man-cgi?wtmp+5+NetBSD-5.0.1+i386),
! 627: [wtmpx(5)](http://netbsd.gw.com/cgi-bin/man-cgi?wtmpx+5+NetBSD-5.0.1+i386),
! 628: [lastlog(5)](http://netbsd.gw.com/cgi-bin/man-cgi?lastlog+5+NetBSD-5.0.1+i386)
! 629: and
! 630: [lastlogx(5)](http://netbsd.gw.com/cgi-bin/man-cgi?lastlogx+5+NetBSD-5.0.1+i386)
1.1 jdf 631: databases.
632:
633: #### pam\_login\_access(8)
634:
1.3 ! jdf 635: The
! 636: [pam\_login\_access(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_login_access+8+NetBSD-5.0.1+i386)
! 637: module provides an implementation of the account management primitive which
! 638: enforces the login restrictions specified in the
! 639: [login.access(5)](http://netbsd.gw.com/cgi-bin/man-cgi?login.access+5+NetBSD-5.0.1+i386)
1.1 jdf 640: table.
641:
642: #### pam\_nologin(8)
643:
1.3 ! jdf 644: The
! 645: [pam\_nologin(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_nologin+8+NetBSD-5.0.1+i386)
! 646: module refuses non-root logins when `/var/run/nologin` exists. This file is
! 647: normally created by
! 648: [shutdown(8)](http://netbsd.gw.com/cgi-bin/man-cgi?shutdown+8+NetBSD-5.0.1+i386)
1.1 jdf 649: when less than five minutes remain until the scheduled shutdown time.
650:
651: #### pam\_permit(8)
652:
1.3 ! jdf 653: The
! 654: [pam\_permit(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_permit+8+NetBSD-5.0.1+i386)
! 655: module is one of the simplest modules available; it responds to any request with
! 656: `PAM_SUCCESS`. It is useful as a placeholder for services where one or more
1.1 jdf 657: chains would otherwise be empty.
658:
659: #### pam\_radius(8)
660:
1.3 ! jdf 661: The
! 662: [pam\_radius(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_radius+8+NetBSD-5.0.1+i386)
! 663: module provides authentication services based upon the RADIUS (Remote
1.1 jdf 664: Authentication Dial In User Service) protocol.
665:
666: #### pam\_rhosts(8)
667:
1.3 ! jdf 668: The
! 669: [pam\_rhosts(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_rhosts+8+NetBSD-5.0.1+i386)
! 670: module provides only authentication services. It reports success if and only if
! 671: the target user's ID is not 0 and the remote host and user are listed in
1.1 jdf 672: `/etc/hosts.equiv` or in the target user's `~/.rhosts`.
673:
674: #### pam\_rootok(8)
675:
1.3 ! jdf 676: The
! 677: [pam\_rootok(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_rootok+8+NetBSD-5.0.1+i386)
! 678: module reports success if and only if the real user id of the process calling it
! 679: (which is assumed to be run by the applicant) is 0. This is useful for
! 680: non-networked services such as
! 681: [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386) or
! 682: [passwd(1)](http://netbsd.gw.com/cgi-bin/man-cgi?passwd+1+NetBSD-5.0.1+i386), to
1.1 jdf 683: which the `root` should have automatic access.
684:
685: #### pam\_securetty(8)
686:
1.3 ! jdf 687: The
! 688: [pam\_securetty(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_securetty+8+NetBSD-5.0.1+i386)
! 689: module provides only account services. It is used when the applicant is
! 690: attempting to authenticate as superuser, and the process is attached to an
1.1 jdf 691: insecure TTY.
692:
693: #### pam\_self(8)
694:
1.3 ! jdf 695: The
! 696: [pam\_self(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_self+8+NetBSD-5.0.1+i386)
! 697: module reports success if and only if the names of the applicant matches that of
! 698: the target account. It is most useful for non-networked services such as
! 699: [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386), where the
1.1 jdf 700: identity of the applicant can be easily verified.
701:
702: #### pam\_ssh(8)
703:
1.3 ! jdf 704: The
! 705: [pam\_ssh(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_ssh+8+NetBSD-5.0.1+i386)
! 706: module provides both authentication and session services. The authentication
! 707: service allows users who have passphrase-protected SSH secret keys in their
! 708: `~/.ssh` directory to authenticate themselves by typing their passphrase. The
! 709: session service starts
! 710: [ssh-agent(1)](http://netbsd.gw.com/cgi-bin/man-cgi?ssh-agent+1+NetBSD-5.0.1+i386)
! 711: and preloads it with the keys that were decrypted in the authentication phase.
! 712: This feature is particularly useful for local logins, whether in X (using
! 713: [xdm(1)](http://netbsd.gw.com/cgi-bin/man-cgi?xdm+1+NetBSD-5.0.1+i386) or
1.1 jdf 714: another PAM-aware X login manager) or at the console.
715:
1.3 ! jdf 716: This module implements what is fundamentally a password authentication scheme.
! 717: Care should be taken to only use this module over a secure session (secure TTY,
! 718: encrypted session, etc.), otherwise the user's SSH passphrase could be
1.1 jdf 719: compromised.
720:
1.3 ! jdf 721: Additional consideration should be given to the use of
! 722: [pam\_ssh(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_ssh+8+NetBSD-5.0.1+i386).
! 723: Users often assume that file permissions are sufficient to protect their SSH
! 724: keys, and thus use weak or no passphrases. Since the system administrator has no
! 725: effective means of enforcing SSH passphrase quality, this has the potential to
1.1 jdf 726: expose the system to security risks.
727:
728: #### pam\_unix(8)
729:
1.3 ! jdf 730: The
! 731: [pam\_unix(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_unix+8+NetBSD-5.0.1+i386)
! 732: module implements traditional UNIX® password authentication, using
! 733: [getpwnam(3)](http://netbsd.gw.com/cgi-bin/man-cgi?getpwnam+3+NetBSD-5.0.1+i386)
! 734: under FreeBSD or
! 735: [getpwnam\_r(3)](http://netbsd.gw.com/cgi-bin/man-cgi?getpwnam_r+3+NetBSD-5.0.1+i386)
! 736: under NetBSD to obtain the target account's password and compare it with the one
! 737: provided by the applicant. It also provides account management services
! 738: (enforcing account and password expiration times) and password-changing
! 739: services. This is probably the single most useful module, as the great majority
1.1 jdf 740: of admins will want to maintain historical behaviour for at least some services.
741:
742: ### NetBSD-specific PAM Modules
743:
744: #### pam\_skey(8)
745:
1.3 ! jdf 746: The
! 747: [pam\_skey(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_skey+8+NetBSD-5.0.1+i386)
! 748: module implements S/Key One Time Password (OTP) authentication methods, using
1.1 jdf 749: the `/etc/skeykeys` database.
750:
751: ## PAM Application Programming
752:
753: This section has not yet been written.
754:
755: ## PAM Module Programming
756:
757: This section has not yet been written.
758:
759: ## Sample PAM Application
760:
1.3 ! jdf 761: The following is a minimal implementation of
! 762: [su(1)](http://netbsd.gw.com/cgi-bin/man-cgi?su+1+NetBSD-5.0.1+i386) using PAM.
! 763: Note that it uses the OpenPAM-specific
! 764: [openpam\_ttyconv(3)](http://netbsd.gw.com/cgi-bin/man-cgi?openpam_ttyconv+3+NetBSD-5.0.1+i386)
! 765: conversation function, which is prototyped in `security/openpam.h`. If you wish
! 766: build this application on a system with a different PAM library, you will have
! 767: to provide your own conversation function. A robust conversation function is
! 768: surprisingly difficult to implement; the one presented in the [Sample PAM
! 769: Conversation Function](chap-pam.html#pam-sample-conv "18.11. Sample PAM
! 770: Conversation Function") sub-chapter is a good starting point, but should not be
1.1 jdf 771: used in real-world applications.
772:
773: #include <sys/param.h>
774: #include <sys/wait.h>
775:
776: #include <err.h>
777: #include <pwd.h>
778: #include <stdio.h>
779: #include <stdlib.h>
780: #include <string.h>
781: #include <syslog.h>
782: #include <unistd.h>
783:
784: #include <security/pam_appl.h>
785: #include <security/openpam.h> /* for openpam_ttyconv() */
786:
787: extern char **environ;
788:
789: static pam_handle_t *pamh;
790: static struct pam_conv pamc;
791:
792: static void
793: usage(void)
794: {
795:
796: fprintf(stderr, "Usage: su [login [args]]\n");
797: exit(1);
798: }
799:
800: int
801: main(int argc, char *argv[])
802: {
803: char hostname[MAXHOSTNAMELEN];
804: const char *user, *tty;
805: char **args, **pam_envlist, **pam_env;
806: struct passwd *pwd;
807: int o, pam_err, status;
808: pid_t pid;
809:
810: while ((o = getopt(argc, argv, "h")) != -1)
811: switch (o) {
812: case 'h':
813: default:
814: usage();
815: }
816:
817: argc -= optind;
818: argv += optind;
819:
820: if (argc > 0) {
821: user = *argv;
822: --argc;
823: ++argv;
824: } else {
825: user = "root";
826: }
827:
828: /* initialize PAM */
829: pamc.conv = &openpam_ttyconv;
830: pam_start("su", user, &pamc, &pamh);
831:
832: /* set some items */
833: gethostname(hostname, sizeof(hostname));
834: if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
835: goto pamerr;
836: user = getlogin();
837: if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS)
838: goto pamerr;
839: tty = ttyname(STDERR_FILENO);
840: if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
841: goto pamerr;
842:
843: /* authenticate the applicant */
844: if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
845: goto pamerr;
846: if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
847: pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
848: if (pam_err != PAM_SUCCESS)
849: goto pamerr;
850:
851: /* establish the requested credentials */
852: if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
853: goto pamerr;
854:
855: /* authentication succeeded; open a session */
856: if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
857: goto pamerr;
858:
859: /* get mapped user name; PAM may have changed it */
860: pam_err = pam_get_item(pamh, PAM_USER, (const void **)&user);
861: if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user)) == NULL)
862: goto pamerr;
863:
864: /* export PAM environment */
865: if ((pam_envlist = pam_getenvlist(pamh)) != NULL) {
866: for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
867: putenv(*pam_env);
868: free(*pam_env);
869: }
870: free(pam_envlist);
871: }
872:
873: /* build argument list */
874: if ((args = calloc(argc + 2, sizeof *args)) == NULL) {
875: warn("calloc()");
876: goto err;
877: }
878: *args = pwd->pw_shell;
879: memcpy(args + 1, argv, argc * sizeof *args);
880:
881: /* fork and exec */
882: switch ((pid = fork())) {
883: case -1:
884: warn("fork()");
885: goto err;
886: case 0:
887: /* child: give up privs and start a shell */
888:
889: /* set uid and groups */
890: if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
891: warn("initgroups()");
892: _exit(1);
893: }
894: if (setgid(pwd->pw_gid) == -1) {
895: warn("setgid()");
896: _exit(1);
897: }
898: if (setuid(pwd->pw_uid) == -1) {
899: warn("setuid()");
900: _exit(1);
901: }
902: execve(*args, args, environ);
903: warn("execve()");
904: _exit(1);
905: default:
906: /* parent: wait for child to exit */
907: waitpid(pid, &status, 0);
908:
909: /* close the session and release PAM resources */
910: pam_err = pam_close_session(pamh, 0);
911: pam_end(pamh, pam_err);
912:
913: exit(WEXITSTATUS(status));
914: }
915:
916: pamerr:
917: fprintf(stderr, "Sorry\n");
918: err:
919: pam_end(pamh, pam_err);
920: exit(1);
921: }
922:
923: ## Sample PAM Module
924:
1.3 ! jdf 925: The following is a minimal implementation of
! 926: [pam\_unix(8)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_unix+8+NetBSD-5.0.1+i386),
! 927: offering only authentication services. It should build and run with most PAM
! 928: implementations, but takes advantage of OpenPAM extensions if available: note
! 929: the use of
! 930: [pam\_get\_authtok(3)](http://netbsd.gw.com/cgi-bin/man-cgi?pam_get_authtok+3+NetBSD-5.0.1+i386),
1.1 jdf 931: which enormously simplifies prompting the user for a password.
932:
933: #include <sys/param.h>
934:
935: #include <pwd.h>
936: #include <stdlib.h>
937: #include <stdio.h>
938: #include <string.h>
939: #include <unistd.h>
940:
941: #include <security/pam_modules.h>
942: #include <security/pam_appl.h>
943:
944: #ifndef _OPENPAM
945: static char password_prompt[] = "Password:";
946: #endif
947:
948: #ifndef PAM_EXTERN
949: #define PAM_EXTERN
950: #endif
951:
952: PAM_EXTERN int
953: pam_sm_authenticate(pam_handle_t *pamh, int flags,
954: int argc, const char *argv[])
955: {
956: #ifndef _OPENPAM
957: const void *ptr;
958: const struct pam_conv *conv;
959: struct pam_message msg;
960: const struct pam_message *msgp;
961: struct pam_response *resp;
962: #endif
963: struct passwd *pwd;
964: const char *user;
965: char *crypt_password, *password;
966: int pam_err, retry;
967:
968: /* identify user */
969: if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
970: return (pam_err);
971: if ((pwd = getpwnam(user)) == NULL)
972: return (PAM_USER_UNKNOWN);
973:
974: /* get password */
975: #ifndef _OPENPAM
976: pam_err = pam_get_item(pamh, PAM_CONV, &ptr);
977: if (pam_err != PAM_SUCCESS)
978: return (PAM_SYSTEM_ERR);
979: conv = ptr;
980: msg.msg_style = PAM_PROMPT_ECHO_OFF;
981: msg.msg = password_prompt;
982: msgp = &msg;
983: #endif
984: password = NULL;
985: for (retry = 0; retry < 3; ++retry) {
986: #ifdef _OPENPAM
987: pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
988: (const char **)&password, NULL);
989: #else
990: resp = NULL;
991: pam_err = (*conv->conv)(1, &msgp, &resp, conv->appdata_ptr);
992: if (resp != NULL) {
993: if (pam_err == PAM_SUCCESS)
994: password = resp->resp;
995: else
996: free(resp->resp);
997: free(resp);
998: }
999: #endif
1000: if (pam_err == PAM_SUCCESS)
1001: break;
1002: }
1003: if (pam_err == PAM_CONV_ERR)
1004: return (pam_err);
1005: if (pam_err != PAM_SUCCESS)
1006: return (PAM_AUTH_ERR);
1007:
1008: /* compare passwords */
1009: if ((!pwd->pw_passwd[0] && (flags & PAM_DISALLOW_NULL_AUTHTOK)) ||
1010: (crypt_password = crypt(password, pwd->pw_passwd)) == NULL ||
1011: strcmp(crypt_password, pwd->pw_passwd) != 0)
1012: pam_err = PAM_AUTH_ERR;
1013: else
1014: pam_err = PAM_SUCCESS;
1015: #ifndef _OPENPAM
1016: free(password);
1017: #endif
1018: return (pam_err);
1019: }
1020:
1021: PAM_EXTERN int
1022: pam_sm_setcred(pam_handle_t *pamh, int flags,
1023: int argc, const char *argv[])
1024: {
1025:
1026: return (PAM_SUCCESS);
1027: }
1028:
1029: PAM_EXTERN int
1030: pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
1031: int argc, const char *argv[])
1032: {
1033:
1034: return (PAM_SUCCESS);
1035: }
1036:
1037: PAM_EXTERN int
1038: pam_sm_open_session(pam_handle_t *pamh, int flags,
1039: int argc, const char *argv[])
1040: {
1041:
1042: return (PAM_SUCCESS);
1043: }
1044:
1045: PAM_EXTERN int
1046: pam_sm_close_session(pam_handle_t *pamh, int flags,
1047: int argc, const char *argv[])
1048: {
1049:
1050: return (PAM_SUCCESS);
1051: }
1052:
1053: PAM_EXTERN int
1054: pam_sm_chauthtok(pam_handle_t *pamh, int flags,
1055: int argc, const char *argv[])
1056: {
1057:
1058: return (PAM_SERVICE_ERR);
1059: }
1060:
1061: #ifdef PAM_MODULE_ENTRY
1062: PAM_MODULE_ENTRY("pam_unix");
1063: #endif
1064:
1065: ## Sample PAM Conversation Function
1066:
1.3 ! jdf 1067: The conversation function presented below is a greatly simplified version of
! 1068: OpenPAM's
! 1069: [openpam\_ttyconv(3)](http://netbsd.gw.com/cgi-bin/man-cgi?openpam_ttyconv+3+NetBSD-5.0.1+i386).
! 1070: It is fully functional, and should give the reader a good idea of how a
! 1071: conversation function should behave, but it is far too simple for real-world
! 1072: use. Even if you're not using OpenPAM, feel free to download the source code and
! 1073: adapt
! 1074: [openpam\_ttyconv(3)](http://netbsd.gw.com/cgi-bin/man-cgi?openpam_ttyconv+3+NetBSD-5.0.1+i386)
! 1075: to your uses; we believe it to be as robust as a tty-oriented conversation
1.1 jdf 1076: function can reasonably get.
1077:
1078: #include <stdio.h>
1079: #include <stdlib.h>
1080: #include <string.h>
1081: #include <unistd.h>
1082:
1083: #include <security/pam_appl.h>
1084:
1085: int
1086: converse(int n, const struct pam_message **msg,
1087: struct pam_response **resp, void *data)
1088: {
1089: struct pam_response *aresp;
1090: char buf[PAM_MAX_RESP_SIZE];
1091: int i;
1092:
1093: data = data;
1094: if (n <= 0 || n > PAM_MAX_NUM_MSG)
1095: return (PAM_CONV_ERR);
1096: if ((aresp = calloc(n, sizeof *aresp)) == NULL)
1097: return (PAM_BUF_ERR);
1098: for (i = 0; i < n; ++i) {
1099: aresp[i].resp_retcode = 0;
1100: aresp[i].resp = NULL;
1101: switch (msg[i]->msg_style) {
1102: case PAM_PROMPT_ECHO_OFF:
1103: aresp[i].resp = strdup(getpass(msg[i]->msg));
1104: if (aresp[i].resp == NULL)
1105: goto fail;
1106: break;
1107: case PAM_PROMPT_ECHO_ON:
1108: fputs(msg[i]->msg, stderr);
1109: if (fgets(buf, sizeof buf, stdin) == NULL)
1110: goto fail;
1111: aresp[i].resp = strdup(buf);
1112: if (aresp[i].resp == NULL)
1113: goto fail;
1114: break;
1115: case PAM_ERROR_MSG:
1116: fputs(msg[i]->msg, stderr);
1117: if (strlen(msg[i]->msg) > 0 &&
1118: msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
1119: fputc('\n', stderr);
1120: break;
1121: case PAM_TEXT_INFO:
1122: fputs(msg[i]->msg, stdout);
1123: if (strlen(msg[i]->msg) > 0 &&
1124: msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
1125: fputc('\n', stdout);
1126: break;
1127: default:
1128: goto fail;
1129: }
1130: }
1131: *resp = aresp;
1132: return (PAM_SUCCESS);
1133: fail:
1134: for (i = 0; i < n; ++i) {
1135: if (aresp[i].resp != NULL) {
1136: memset(aresp[i].resp, 0, strlen(aresp[i].resp));
1137: free(aresp[i].resp);
1138: }
1139: }
1140: memset(aresp, 0, n * sizeof *aresp);
1141: *resp = NULL;
1142: return (PAM_CONV_ERR);
1143: }
1144:
1145: ## Further Reading
1146:
1147: ### Papers
1148:
1149: * *[sun-pam]: [Making Login Services Independent of Authentication Technologies](http://www.sun.com/software/solaris/pam/pam.external.pdf)*. Vipin Samar and Charlie Lai. Sun Microsystems.
1150: * *[opengroup-singlesignon]: [X/Open Single Sign-on Preliminary Specification](http://www.opengroup.org/pubs/catalog/p702.htm)*. The Open Group. 1-85912-144-6. June 1997.
1151: * *[kernelorg-pamdraft]: [Pluggable Authentication Modules](http://www.kernel.org/pub/linux/libs/pam/pre/doc/current-draft.txt)*. Andrew G. Morgan. October 6, 1999.
1152:
1153: ### User Manuals
1154:
1155: * *[sun-pamadmin]: [PAM Administration](http://www.sun.com/software/solaris/pam/pam.admin.pdf)*. Sun Microsystems.
1156:
1157: ### Related Web pages
1158:
1159: * *[openpam-website]: [OpenPAM homepage](http://openpam.sourceforge.net/)*. Dag-Erling Smørgrav. ThinkSec AS.
1160: * *[linuxpam-website]: [Linux-PAM homepage](http://www.kernel.org/pub/linux/libs/pam/)*. Andrew G. Morgan.
1161: * *[solarispam-website]: [Solaris PAM homepage](http://www.sun.com/software/solaris/pam/)*. Sun Microsystems.
1162:
1.2 jdf 1163: ### Networks Associates Technology's license on the PAM article
1164:
1165: Copyright (c) 2001-2003 Networks Associates Technology, Inc.
1166: All rights reserved.
1167: This software was developed for the FreeBSD Project by ThinkSec AS and
1168: Network Associates Laboratories, the Security Research Division of
1169: Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
1170: ("CBOSS"), as part of the DARPA CHATS research program.
1171: Redistribution and use in source and binary forms, with or without
1172: modification, are permitted provided that the following conditions
1173: are met:
1174: 1. Redistributions of source code must retain the above copyright
1175: notice, this list of conditions and the following disclaimer.
1176: 2. Redistributions in binary form must reproduce the above copyright
1177: notice, this list of conditions and the following disclaimer in the
1178: documentation and/or other materials provided with the distribution.
1179: 3. The name of the author may not be used to endorse or promote
1180: products derived from this software without specific prior written
1181: permission.
1182: THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND
1183: ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1184: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1185: ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1186: FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1187: DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1188: OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1189: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1190: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1191: OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1192: SUCH DAMAGE.
CVSweb for NetBSD wikisrc <wikimaster@NetBSD.org> software: FreeBSD-CVSweb