File:  [NetBSD Developer Wiki] / wikisrc / guide / pam.mdwn
Revision 1.1: download - view: text, annotated - select for diffs
Sat Mar 9 00:00:15 2013 UTC (7 years, 6 months ago) by jdf
Branches: MAIN
CVS tags: HEAD
Add PAM section from the guide.
Note that there is missing content!

    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