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