Annotation of wikisrc/guide/pam.mdwn, revision 1.4

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

CVSweb for NetBSD wikisrc <wikimaster@NetBSD.org> software: FreeBSD-CVSweb