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

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

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