1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  *
  25  * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org>
  26  */
  27 
  28 #include <syslog.h>
  29 #include <dlfcn.h>
  30 #include <sys/types.h>
  31 #include <sys/stat.h>
  32 #include <stdlib.h>
  33 #include <strings.h>
  34 #include <malloc.h>
  35 #include <unistd.h>
  36 #include <fcntl.h>
  37 #include <errno.h>
  38 
  39 #include <security/pam_appl.h>
  40 #include <security/pam_modules.h>
  41 #include <sys/mman.h>
  42 
  43 #include <libintl.h>
  44 
  45 #include "pam_impl.h"
  46 
  47 static char *pam_snames [PAM_NUM_MODULE_TYPES] = {
  48         PAM_ACCOUNT_NAME,
  49         PAM_AUTH_NAME,
  50         PAM_PASSWORD_NAME,
  51         PAM_SESSION_NAME
  52 };
  53 
  54 static char *pam_inames [PAM_MAX_ITEMS] = {
  55 /* NONE */              NULL,
  56 /* PAM_SERVICE */       "service",
  57 /* PAM_USER */          "user",
  58 /* PAM_TTY */           "tty",
  59 /* PAM_RHOST */         "rhost",
  60 /* PAM_CONV */          "conv",
  61 /* PAM_AUTHTOK */       "authtok",
  62 /* PAM_OLDAUTHTOK */    "oldauthtok",
  63 /* PAM_RUSER */         "ruser",
  64 /* PAM_USER_PROMPT */   "user_prompt",
  65 /* PAM_REPOSITORY */    "repository",
  66 /* PAM_RESOURCE */      "resource",
  67 /* PAM_AUSER */         "auser",
  68 /* Undefined Items */
  69 };
  70 
  71 /*
  72  * This extra definition is needed in order to build this library
  73  * on pre-64-bit-aware systems.
  74  */
  75 #if !defined(_LFS64_LARGEFILE)
  76 #define stat64  stat
  77 #endif  /* !defined(_LFS64_LARGEFILE) */
  78 
  79 /* functions to dynamically load modules */
  80 static int      load_modules(pam_handle_t *, int, char *, pamtab_t *);
  81 static void     *open_module(pam_handle_t *, char *);
  82 static int      load_function(void *, char *, int (**func)());
  83 
  84 /* functions to read and store the pam.conf configuration file */
  85 static int      open_pam_conf(struct pam_fh **, pam_handle_t *, char *, int);
  86 static void     close_pam_conf(struct pam_fh *);
  87 static int      read_pam_conf(pam_handle_t *, char *, char *, int);
  88 static int      get_pam_conf_entry(struct pam_fh *, pam_handle_t *,
  89     pamtab_t **, char *, int);
  90 static char     *read_next_token(char **);
  91 static char     *nextline(struct pam_fh *, pam_handle_t *, int *);
  92 static int      verify_pam_conf(pamtab_t *, char *);
  93 
  94 /* functions to clean up and free memory */
  95 static void     clean_up(pam_handle_t *);
  96 static void     free_pamconf(pamtab_t *);
  97 static void     free_pam_conf_info(pam_handle_t *);
  98 static void     free_env(env_list *);
  99 
 100 /* convenience functions for I18N/L10N communication */
 101 
 102 static void     free_resp(int, struct pam_response *);
 103 static int      do_conv(pam_handle_t *, int, int,
 104     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *,
 105     struct pam_response **);
 106 
 107 static int      log_priority;   /* pam_trace syslog priority & facility */
 108 static int      pam_debug = 0;
 109 
 110 static char *
 111 pam_trace_iname(int item_type, char *iname_buf)
 112 {
 113         char *name;
 114 
 115         if (item_type <= 0 ||
 116             item_type >= PAM_MAX_ITEMS ||
 117             (name = pam_inames[item_type]) == NULL) {
 118                 (void) sprintf(iname_buf, "%d", item_type);
 119                 return (iname_buf);
 120         }
 121         return (name);
 122 }
 123 
 124 static char *
 125 pam_trace_fname(int flag)
 126 {
 127         if (flag & PAM_BINDING)
 128                 return (PAM_BINDING_NAME);
 129         if (flag & PAM_INCLUDE)
 130                 return (PAM_INCLUDE_NAME);
 131         if (flag & PAM_OPTIONAL)
 132                 return (PAM_OPTIONAL_NAME);
 133         if (flag & PAM_REQUIRED)
 134                 return (PAM_REQUIRED_NAME);
 135         if (flag & PAM_REQUISITE)
 136                 return (PAM_REQUISITE_NAME);
 137         if (flag & PAM_SUFFICIENT)
 138                 return (PAM_SUFFICIENT_NAME);
 139         return ("bad flag name");
 140 }
 141 
 142 static char *
 143 pam_trace_cname(pam_handle_t *pamh)
 144 {
 145         if (pamh->pam_conf_name[pamh->include_depth] == NULL)
 146                 return ("NULL");
 147         return (pamh->pam_conf_name[pamh->include_depth]);
 148 }
 149 
 150 #include <deflt.h>
 151 #include <stdarg.h>
 152 /*
 153  * pam_settrace - setup configuration for pam tracing
 154  *
 155  * turn on PAM debug if "magic" file exists
 156  * if exists (original), pam_debug = PAM_DEBUG_DEFAULT,
 157  * log_priority = LOG_DEBUG(7) and log_facility = LOG_AUTH(4).
 158  *
 159  * if has contents, keywork=value pairs:
 160  *
 161  *      "log_priority=" 0-7, the pam_trace syslog priority to use
 162  *              (see sys/syslog.h)
 163  *      "log_facility=" 0-23, the pam_trace syslog facility to use
 164  *              (see sys/syslog.h)
 165  *      "debug_flags=" PAM_DEBUG_DEFAULT (0x0001), log traditional
 166  *                      (original) debugging.
 167  *              Plus the logical or of:
 168  *                  PAM_DEBUG_ITEM (0x0002), log item values and
 169  *                      pam_get_item.
 170  *                  PAM_DEBUG_MODULE (0x0004), log module return status.
 171  *                  PAM_DEBUG_CONF (0x0008), log pam.conf parsing.
 172  *                  PAM_DEBUG_DATA (0x0010), get/set_data.
 173  *                  PAM_DEBUG_CONV (0x0020), conversation/response.
 174  *
 175  *                  If compiled with DEBUG:
 176  *                  PAM_DEBUG_AUTHTOK (0x8000), display AUTHTOK value if
 177  *                              PAM_DEBUG_ITEM is set and results from
 178  *                              PAM_PROMPT_ECHO_OFF responses.
 179  *                  USE CAREFULLY, THIS EXPOSES THE USER'S PASSWORDS.
 180  *
 181  *              or set to 0 and off even if PAM_DEBUG file exists.
 182  *
 183  * Output has the general form:
 184  * <whatever was set syslog> PAM[<pid>]: <interface>(<handle> and other info)
 185  * <whatever was set syslog> PAM[<pid>]: details requested for <interface> call
 186  *      Where:  <pid> is the process ID of the calling process.
 187  *              <handle> is the Hex value of the pam_handle associated with the
 188  *                      call.
 189  */
 190 
 191 static void
 192 pam_settrace()
 193 {
 194         void    *defp;
 195 
 196         if ((defp = defopen_r(PAM_DEBUG)) != NULL) {
 197                 char    *arg;
 198                 int     code;
 199                 int     facility = LOG_AUTH;
 200 
 201                 pam_debug = PAM_DEBUG_DEFAULT;
 202                 log_priority = LOG_DEBUG;
 203 
 204                 (void) defcntl_r(DC_SETFLAGS, DC_CASE, defp);
 205                 if ((arg = defread_r(LOG_PRIORITY, defp)) != NULL) {
 206                         code = (int)strtol(arg, NULL, 10);
 207                         if ((code & ~LOG_PRIMASK) == 0) {
 208                                 log_priority = code;
 209                         }
 210                 }
 211                 if ((arg = defread_r(LOG_FACILITY, defp)) != NULL) {
 212                         code = (int)strtol(arg, NULL, 10);
 213                         if (code < LOG_NFACILITIES) {
 214                                 facility = code << 3;
 215                         }
 216                 }
 217                 if ((arg = defread_r(DEBUG_FLAGS, defp)) != NULL) {
 218                         pam_debug = (int)strtol(arg, NULL, 0);
 219                 }
 220                 defclose_r(defp);
 221 
 222                 log_priority |= facility;
 223         }
 224 }
 225 
 226 /*
 227  * pam_trace - logs tracing messages
 228  *
 229  *      flag = debug_flags from /etc/pam_debug
 230  *      format and args = message to print (PAM[<pid>]: is prepended).
 231  *
 232  *      global log_priority = pam_trace syslog (log_priority | log_facility)
 233  *              from /etc/pam_debug
 234  */
 235 /*PRINTFLIKE2*/
 236 static void
 237 pam_trace(int flag, char *format, ...)
 238 {
 239         va_list args;
 240         char message[1024];
 241         int savemask;
 242 
 243         if ((pam_debug & flag) == 0)
 244                 return;
 245 
 246         savemask = setlogmask(LOG_MASK(log_priority & LOG_PRIMASK));
 247         (void) snprintf(message, sizeof (message), "PAM[%ld]: %s",
 248             (long)getpid(), format);
 249         va_start(args, format);
 250         (void) vsyslog(log_priority, message, args);
 251         va_end(args);
 252         (void) setlogmask(savemask);
 253 }
 254 
 255 /*
 256  * __pam_log - logs PAM syslog messages
 257  *
 258  *      priority = message priority
 259  *      format and args = message to log
 260  */
 261 /*PRINTFLIKE2*/
 262 void
 263 __pam_log(int priority, const char *format, ...)
 264 {
 265         va_list args;
 266         int savemask = setlogmask(LOG_MASK(priority & LOG_PRIMASK));
 267 
 268         va_start(args, format);
 269         (void) vsyslog(priority, format, args);
 270         va_end(args);
 271         (void) setlogmask(savemask);
 272 }
 273 
 274 
 275 /*
 276  *                      pam_XXXXX routines
 277  *
 278  *      These are the entry points to the authentication switch
 279  */
 280 
 281 /*
 282  * pam_start            - initiate an authentication transaction and
 283  *                        set parameter values to be used during the
 284  *                        transaction
 285  */
 286 
 287 int
 288 pam_start(const char *service, const char *user,
 289     const struct pam_conv *pam_conv, pam_handle_t **pamh)
 290 {
 291         int     err;
 292 
 293         *pamh = calloc(1, sizeof (struct pam_handle));
 294 
 295         pam_settrace();
 296         pam_trace(PAM_DEBUG_DEFAULT,
 297             "pam_start(%s,%s,%p:%p) - debug = %x",
 298             service ? service : "NULL", user ? user : "NULL", (void *)pam_conv,
 299             (void *)*pamh, pam_debug);
 300 
 301         if (*pamh == NULL)
 302                 return (PAM_BUF_ERR);
 303 
 304         (*pamh)->pam_inmodule = RO_OK;               /* OK to set RO items */
 305         if ((err = pam_set_item(*pamh, PAM_SERVICE, (void *)service))
 306             != PAM_SUCCESS) {
 307                 clean_up(*pamh);
 308                 *pamh = NULL;
 309                 return (err);
 310         }
 311 
 312         if ((err = pam_set_item(*pamh, PAM_USER, (void *)user))
 313             != PAM_SUCCESS) {
 314                 clean_up(*pamh);
 315                 *pamh = NULL;
 316                 return (err);
 317         }
 318 
 319         if ((err = pam_set_item(*pamh, PAM_CONV, (void *)pam_conv))
 320             != PAM_SUCCESS) {
 321                 clean_up(*pamh);
 322                 *pamh = NULL;
 323                 return (err);
 324         }
 325 
 326         (*pamh)->pam_inmodule = RW_OK;
 327         return (PAM_SUCCESS);
 328 }
 329 
 330 /*
 331  * pam_end - terminate an authentication transaction
 332  */
 333 
 334 int
 335 pam_end(pam_handle_t *pamh, int pam_status)
 336 {
 337         struct pam_module_data *psd, *p;
 338         fd_list *expired;
 339         fd_list *traverse;
 340         env_list *env_expired;
 341         env_list *env_traverse;
 342 
 343         pam_trace(PAM_DEBUG_DEFAULT,
 344             "pam_end(%p): status = %s", (void *)pamh,
 345             pam_strerror(pamh, pam_status));
 346 
 347         if (pamh == NULL)
 348                 return (PAM_SYSTEM_ERR);
 349 
 350         /* call the cleanup routines for module specific data */
 351 
 352         psd = pamh->ssd;
 353         while (psd) {
 354                 if (psd->cleanup) {
 355                         psd->cleanup(pamh, psd->data, pam_status);
 356                 }
 357                 p = psd;
 358                 psd = p->next;
 359                 free(p->module_data_name);
 360                 free(p);
 361         }
 362         pamh->ssd = NULL;
 363 
 364         /* dlclose all module fds */
 365         traverse = pamh->fd;
 366         while (traverse) {
 367                 expired = traverse;
 368                 traverse = traverse->next;
 369                 (void) dlclose(expired->mh);
 370                 free(expired);
 371         }
 372         pamh->fd = 0;
 373 
 374         /* remove all environment variables */
 375         env_traverse = pamh->pam_env;
 376         while (env_traverse) {
 377                 env_expired = env_traverse;
 378                 env_traverse = env_traverse->next;
 379                 free_env(env_expired);
 380         }
 381 
 382         clean_up(pamh);
 383         return (PAM_SUCCESS);
 384 }
 385 
 386 /*
 387  * pam_set_item         - set the value of a parameter that can be
 388  *                        retrieved via a call to pam_get_item()
 389  */
 390 
 391 int
 392 pam_set_item(pam_handle_t *pamh, int item_type, const void *item)
 393 {
 394         struct pam_item *pip;
 395         int     size;
 396         char    iname_buf[PAM_MAX_MSG_SIZE];
 397 
 398         if (((pam_debug & PAM_DEBUG_ITEM) == 0) || (pamh == NULL)) {
 399                 pam_trace(PAM_DEBUG_DEFAULT,
 400                     "pam_set_item(%p:%s)", (void *)pamh,
 401                     pam_trace_iname(item_type, iname_buf));
 402         }
 403 
 404         if (pamh == NULL)
 405                 return (PAM_SYSTEM_ERR);
 406 
 407         /* check read only items */
 408         if ((item_type == PAM_SERVICE) && (pamh->pam_inmodule != RO_OK))
 409                 return (PAM_PERM_DENIED);
 410 
 411         /*
 412          * Check that item_type is within valid range
 413          */
 414 
 415         if (item_type <= 0 || item_type >= PAM_MAX_ITEMS)
 416                 return (PAM_SYMBOL_ERR);
 417 
 418         pip = &(pamh->ps_item[item_type]);
 419 
 420         switch (item_type) {
 421         case PAM_AUTHTOK:
 422         case PAM_OLDAUTHTOK:
 423                 if (pip->pi_addr != NULL)
 424                         (void) memset(pip->pi_addr, 0, pip->pi_size);
 425                 /*FALLTHROUGH*/
 426         case PAM_SERVICE:
 427         case PAM_USER:
 428         case PAM_TTY:
 429         case PAM_RHOST:
 430         case PAM_RUSER:
 431         case PAM_USER_PROMPT:
 432         case PAM_RESOURCE:
 433         case PAM_AUSER:
 434                 if (pip->pi_addr != NULL) {
 435                         free(pip->pi_addr);
 436                 }
 437 
 438                 if (item == NULL) {
 439                         pip->pi_addr = NULL;
 440                         pip->pi_size = 0;
 441                 } else {
 442                         pip->pi_addr = strdup((char *)item);
 443                         if (pip->pi_addr == NULL) {
 444                                 pip->pi_size = 0;
 445                                 return (PAM_BUF_ERR);
 446                         }
 447                         pip->pi_size = strlen(pip->pi_addr);
 448                 }
 449                 break;
 450         case PAM_CONV:
 451                 if (pip->pi_addr != NULL)
 452                         free(pip->pi_addr);
 453                 size = sizeof (struct pam_conv);
 454                 if ((pip->pi_addr = calloc(1, size)) == NULL)
 455                         return (PAM_BUF_ERR);
 456                 if (item != NULL)
 457                         (void) memcpy(pip->pi_addr, item, (unsigned int) size);
 458                 else
 459                         (void) memset(pip->pi_addr, 0, size);
 460                 pip->pi_size = size;
 461                 break;
 462         case PAM_REPOSITORY:
 463                 if (pip->pi_addr != NULL) {
 464                         pam_repository_t *auth_rep;
 465 
 466                         auth_rep = (pam_repository_t *)pip->pi_addr;
 467                         if (auth_rep->type != NULL)
 468                                 free(auth_rep->type);
 469                         if (auth_rep->scope != NULL)
 470                                 free(auth_rep->scope);
 471                         free(auth_rep);
 472                 }
 473                 if (item != NULL) {
 474                         pam_repository_t *s, *d;
 475 
 476                         size = sizeof (struct pam_repository);
 477                         pip->pi_addr = calloc(1, size);
 478                         if (pip->pi_addr == NULL)
 479                                 return (PAM_BUF_ERR);
 480 
 481                         s = (struct pam_repository *)item;
 482                         d = (struct pam_repository *)pip->pi_addr;
 483 
 484                         d->type = strdup(s->type);
 485                         if (d->type == NULL)
 486                                 return (PAM_BUF_ERR);
 487                         d->scope = malloc(s->scope_len);
 488                         if (d->scope == NULL)
 489                                 return (PAM_BUF_ERR);
 490                         (void) memcpy(d->scope, s->scope, s->scope_len);
 491                         d->scope_len = s->scope_len;
 492                 }
 493                 pip->pi_size = size;
 494                 break;
 495         default:
 496                 return (PAM_SYMBOL_ERR);
 497         }
 498         switch (item_type) {
 499         case PAM_CONV:
 500                 pam_trace(PAM_DEBUG_ITEM, "pam_set_item(%p:%s)=%p",
 501                     (void *)pamh,
 502                     pam_trace_iname(item_type, iname_buf),
 503                     item ? (void *)((struct pam_conv *)item)->conv :
 504                     (void *)0);
 505                 break;
 506         case PAM_REPOSITORY:
 507                 pam_trace(PAM_DEBUG_ITEM, "pam_set_item(%p:%s)=%s",
 508                     (void *)pamh,
 509                     pam_trace_iname(item_type, iname_buf),
 510                     item ? (((struct pam_repository *)item)->type ?
 511                     ((struct pam_repository *)item)->type : "NULL") :
 512                     "NULL");
 513                 break;
 514         case PAM_AUTHTOK:
 515         case PAM_OLDAUTHTOK:
 516 #ifdef  DEBUG
 517                 if (pam_debug & PAM_DEBUG_AUTHTOK)
 518                         pam_trace(PAM_DEBUG_ITEM,
 519                             "pam_set_item(%p:%s)=%s", (void *)pamh,
 520                             pam_trace_iname(item_type, iname_buf),
 521                             item ? (char *)item : "NULL");
 522                 else
 523 #endif  /* DEBUG */
 524                         pam_trace(PAM_DEBUG_ITEM,
 525                             "pam_set_item(%p:%s)=%s", (void *)pamh,
 526                             pam_trace_iname(item_type, iname_buf),
 527                             item ? "********" : "NULL");
 528                 break;
 529         default:
 530                 pam_trace(PAM_DEBUG_ITEM, "pam_set_item(%p:%s)=%s",
 531                     (void *)pamh,
 532                     pam_trace_iname(item_type, iname_buf),
 533                     item ? (char *)item : "NULL");
 534         }
 535 
 536         return (PAM_SUCCESS);
 537 }
 538 
 539 /*
 540  * pam_get_item         - read the value of a parameter specified in
 541  *                        the call to pam_set_item()
 542  */
 543 
 544 int
 545 pam_get_item(const pam_handle_t *pamh, int item_type, void **item)
 546 {
 547         struct pam_item *pip;
 548         char    iname_buf[PAM_MAX_MSG_SIZE];
 549 
 550         if (((pam_debug & PAM_DEBUG_ITEM) == 0) || (pamh == NULL)) {
 551                 pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)",
 552                     (void *)pamh, pam_trace_iname(item_type, iname_buf));
 553         }
 554 
 555         if (pamh == NULL)
 556                 return (PAM_SYSTEM_ERR);
 557 
 558         if (item_type <= 0 || item_type >= PAM_MAX_ITEMS)
 559                 return (PAM_SYMBOL_ERR);
 560 
 561         if ((pamh->pam_inmodule != WO_OK) &&
 562             ((item_type == PAM_AUTHTOK || item_type == PAM_OLDAUTHTOK))) {
 563                 __pam_log(LOG_AUTH | LOG_NOTICE, "pam_get_item(%s) called from "
 564                     "a non module context",
 565                     pam_trace_iname(item_type, iname_buf));
 566                 return (PAM_PERM_DENIED);
 567         }
 568 
 569         pip = (struct pam_item *)&(pamh->ps_item[item_type]);
 570 
 571         *item = pip->pi_addr;
 572         switch (item_type) {
 573         case PAM_CONV:
 574                 pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)=%p",
 575                     (void *)pamh,
 576                     pam_trace_iname(item_type, iname_buf),
 577                     (void *)((struct pam_conv *)*item)->conv);
 578                 break;
 579         case PAM_REPOSITORY:
 580                 pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)=%s",
 581                     (void *)pamh,
 582                     pam_trace_iname(item_type, iname_buf),
 583                     *item ? (((struct pam_repository *)*item)->type ?
 584                     ((struct pam_repository *)*item)->type : "NULL") :
 585                     "NULL");
 586                 break;
 587         case PAM_AUTHTOK:
 588         case PAM_OLDAUTHTOK:
 589 #ifdef  DEBUG
 590                 if (pam_debug & PAM_DEBUG_AUTHTOK)
 591                         pam_trace(PAM_DEBUG_ITEM,
 592                             "pam_get_item(%p:%s)=%s", (void *)pamh,
 593                             pam_trace_iname(item_type, iname_buf),
 594                             *item ? *(char **)item : "NULL");
 595                 else
 596 #endif  /* DEBUG */
 597                         pam_trace(PAM_DEBUG_ITEM,
 598                             "pam_get_item(%p:%s)=%s", (void *)pamh,
 599                             pam_trace_iname(item_type, iname_buf),
 600                             *item ? "********" : "NULL");
 601                 break;
 602         default:
 603                 pam_trace(PAM_DEBUG_ITEM, "pam_get_item(%p:%s)=%s",
 604                     (void *)pamh,
 605                     pam_trace_iname(item_type, iname_buf),
 606                     *item ? *(char **)item : "NULL");
 607         }
 608 
 609         return (PAM_SUCCESS);
 610 }
 611 
 612 /*
 613  * parse_user_name         - process the user response: ignore
 614  *                           '\t' or ' ' before or after a user name.
 615  *                           user_input is a null terminated string.
 616  *                           *ret_username will be the user name.
 617  */
 618 
 619 static int
 620 parse_user_name(char *user_input, char **ret_username)
 621 {
 622         register char *ptr;
 623         register int index = 0;
 624         char username[PAM_MAX_RESP_SIZE];
 625 
 626         /* Set the default value for *ret_username */
 627         *ret_username = NULL;
 628 
 629         /*
 630          * Set the initial value for username - this is a buffer holds
 631          * the user name.
 632          */
 633         bzero((void *)username, PAM_MAX_RESP_SIZE);
 634 
 635         /*
 636          * The user_input is guaranteed to be terminated by a null character.
 637          */
 638         ptr = user_input;
 639 
 640         /* Skip all the leading whitespaces if there are any. */
 641         while ((*ptr == ' ') || (*ptr == '\t'))
 642                 ptr++;
 643 
 644         if (*ptr == '\0') {
 645                 /*
 646                  * We should never get here since the user_input we got
 647                  * in pam_get_user() is not all whitespaces nor just "\0".
 648                  */
 649                 return (PAM_BUF_ERR);
 650         }
 651 
 652         /*
 653          * username will be the first string we get from user_input
 654          * - we skip leading whitespaces and ignore trailing whitespaces
 655          */
 656         while (*ptr != '\0') {
 657                 if ((*ptr == ' ') || (*ptr == '\t'))
 658                         break;
 659                 else {
 660                         username[index] = *ptr;
 661                         index++;
 662                         ptr++;
 663                 }
 664         }
 665 
 666         /* ret_username will be freed in pam_get_user(). */
 667         if ((*ret_username = malloc(index + 1)) == NULL)
 668                 return (PAM_BUF_ERR);
 669         (void) strcpy(*ret_username, username);
 670         return (PAM_SUCCESS);
 671 }
 672 
 673 /*
 674  * Get the value of PAM_USER. If not set, then use the convenience function
 675  * to prompt for the user. Use prompt if specified, else use PAM_USER_PROMPT
 676  * if it is set, else use default.
 677  */
 678 #define WHITESPACE      0
 679 #define USERNAME        1
 680 
 681 int
 682 pam_get_user(pam_handle_t *pamh, char **user, const char *prompt_override)
 683 {
 684         int     status;
 685         char    *prompt = NULL;
 686         char    *real_username;
 687         struct pam_response *ret_resp = NULL;
 688         char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
 689 
 690         pam_trace(PAM_DEBUG_DEFAULT,
 691             "pam_get_user(%p, %p, %s)", (void *)pamh, (void *)*user,
 692             prompt_override ? prompt_override : "NULL");
 693         if (pamh == NULL)
 694                 return (PAM_SYSTEM_ERR);
 695 
 696         if ((status = pam_get_item(pamh, PAM_USER, (void **)user))
 697             != PAM_SUCCESS) {
 698                 return (status);
 699         }
 700 
 701         /* if the user is set, return it */
 702 
 703         if (*user != NULL && *user[0] != '\0') {
 704                 return (PAM_SUCCESS);
 705         }
 706 
 707         /*
 708          * if the module is requesting a special prompt, use it.
 709          * else use PAM_USER_PROMPT.
 710          */
 711 
 712         if (prompt_override != NULL) {
 713                 prompt = (char *)prompt_override;
 714         } else {
 715                 status = pam_get_item(pamh, PAM_USER_PROMPT, (void**)&prompt);
 716                 if (status != PAM_SUCCESS) {
 717                         return (status);
 718                 }
 719         }
 720 
 721         /* if the prompt is not set, use default */
 722 
 723         if (prompt == NULL || prompt[0] == '\0') {
 724                 prompt = dgettext(TEXT_DOMAIN, "Please enter user name: ");
 725         }
 726 
 727         /* prompt for the user */
 728 
 729         (void) strncpy(messages[0], prompt, sizeof (messages[0]));
 730 
 731         for (;;) {
 732                 int state = WHITESPACE;
 733 
 734                 status = do_conv(pamh, PAM_PROMPT_ECHO_ON, 1, messages,
 735                     NULL, &ret_resp);
 736 
 737                 if (status != PAM_SUCCESS) {
 738                         return (status);
 739                 }
 740 
 741                 if (ret_resp->resp && ret_resp->resp[0] != '\0') {
 742                         int len = strlen(ret_resp->resp);
 743                         int i;
 744 
 745                         for (i = 0; i < len; i++) {
 746                                 if ((ret_resp->resp[i] != ' ') &&
 747                                     (ret_resp->resp[i] != '\t')) {
 748                                         state = USERNAME;
 749                                         break;
 750                                 }
 751                         }
 752 
 753                         if (state == USERNAME)
 754                                 break;
 755                 }
 756                 /* essentially empty response, try again */
 757                 free_resp(1, ret_resp);
 758                 ret_resp = NULL;
 759         }
 760 
 761         /* set PAM_USER */
 762         /* Parse the user input to get the user name. */
 763         status = parse_user_name(ret_resp->resp, &real_username);
 764 
 765         if (status != PAM_SUCCESS) {
 766                 if (real_username != NULL)
 767                         free(real_username);
 768                 free_resp(1, ret_resp);
 769                 return (status);
 770         }
 771 
 772         status = pam_set_item(pamh, PAM_USER, real_username);
 773 
 774         free(real_username);
 775 
 776         free_resp(1, ret_resp);
 777         if (status != PAM_SUCCESS) {
 778                 return (status);
 779         }
 780 
 781         /*
 782          * finally, get PAM_USER. We have to call pam_get_item to get
 783          * the value of user because pam_set_item mallocs the memory.
 784          */
 785 
 786         status = pam_get_item(pamh, PAM_USER, (void**)user);
 787         return (status);
 788 }
 789 
 790 /*
 791  * Set module specific data
 792  */
 793 
 794 int
 795 pam_set_data(pam_handle_t *pamh, const char *module_data_name, void *data,
 796     void (*cleanup)(pam_handle_t *pamh, void *data, int pam_end_status))
 797 {
 798         struct pam_module_data *psd;
 799 
 800         pam_trace(PAM_DEBUG_DATA,
 801             "pam_set_data(%p:%s:%d)=%p", (void *)pamh,
 802             module_data_name ? module_data_name : "NULL", pamh->pam_inmodule,
 803             data);
 804         if (pamh == NULL || (pamh->pam_inmodule != WO_OK) ||
 805             module_data_name == NULL) {
 806                 return (PAM_SYSTEM_ERR);
 807         }
 808 
 809         /* check if module data already exists */
 810 
 811         for (psd = pamh->ssd; psd; psd = psd->next) {
 812                 if (strcmp(psd->module_data_name, module_data_name) == 0) {
 813                         /* clean up original data before setting the new data */
 814                         if (psd->cleanup) {
 815                                 psd->cleanup(pamh, psd->data, PAM_SUCCESS);
 816                         }
 817                         psd->data = (void *)data;
 818                         psd->cleanup = cleanup;
 819                         return (PAM_SUCCESS);
 820                 }
 821         }
 822 
 823         psd = malloc(sizeof (struct pam_module_data));
 824         if (psd == NULL)
 825                 return (PAM_BUF_ERR);
 826 
 827         psd->module_data_name = strdup(module_data_name);
 828         if (psd->module_data_name == NULL) {
 829                 free(psd);
 830                 return (PAM_BUF_ERR);
 831         }
 832 
 833         psd->data = (void *)data;
 834         psd->cleanup = cleanup;
 835         psd->next = pamh->ssd;
 836         pamh->ssd = psd;
 837         return (PAM_SUCCESS);
 838 }
 839 
 840 /*
 841  * get module specific data
 842  */
 843 
 844 int
 845 pam_get_data(const pam_handle_t *pamh, const char *module_data_name,
 846     const void **data)
 847 {
 848         struct pam_module_data *psd;
 849 
 850         if (pamh == NULL || (pamh->pam_inmodule != WO_OK) ||
 851             module_data_name == NULL) {
 852                 pam_trace(PAM_DEBUG_DATA,
 853                     "pam_get_data(%p:%s:%d)=%p", (void *)pamh,
 854                     module_data_name ? module_data_name : "NULL",
 855                     pamh->pam_inmodule, *data);
 856                 return (PAM_SYSTEM_ERR);
 857         }
 858 
 859         for (psd = pamh->ssd; psd; psd = psd->next) {
 860                 if (strcmp(psd->module_data_name, module_data_name) == 0) {
 861                         *data = psd->data;
 862                         pam_trace(PAM_DEBUG_DATA,
 863                             "pam_get_data(%p:%s)=%p", (void *)pamh,
 864                             module_data_name, *data);
 865                         return (PAM_SUCCESS);
 866                 }
 867         }
 868         pam_trace(PAM_DEBUG_DATA,
 869             "pam_get_data(%p:%s)=%s", (void *)pamh, module_data_name,
 870             "PAM_NO_MODULE_DATA");
 871 
 872         return (PAM_NO_MODULE_DATA);
 873 }
 874 
 875 /*
 876  * PAM equivalent to strerror()
 877  */
 878 /* ARGSUSED */
 879 const char *
 880 pam_strerror(pam_handle_t *pamh, int errnum)
 881 {
 882         switch (errnum) {
 883         case PAM_SUCCESS:
 884                 return (dgettext(TEXT_DOMAIN, "Success"));
 885         case PAM_OPEN_ERR:
 886                 return (dgettext(TEXT_DOMAIN, "Dlopen failure"));
 887         case PAM_SYMBOL_ERR:
 888                 return (dgettext(TEXT_DOMAIN, "Symbol not found"));
 889         case PAM_SERVICE_ERR:
 890                 return (dgettext(TEXT_DOMAIN,
 891                     "Error in underlying service module"));
 892         case PAM_SYSTEM_ERR:
 893                 return (dgettext(TEXT_DOMAIN, "System error"));
 894         case PAM_BUF_ERR:
 895                 return (dgettext(TEXT_DOMAIN, "Memory buffer error"));
 896         case PAM_CONV_ERR:
 897                 return (dgettext(TEXT_DOMAIN, "Conversation failure"));
 898         case PAM_PERM_DENIED:
 899                 return (dgettext(TEXT_DOMAIN, "Permission denied"));
 900         case PAM_MAXTRIES:
 901                 return (dgettext(TEXT_DOMAIN,
 902                     "Maximum number of attempts exceeded"));
 903         case PAM_AUTH_ERR:
 904                 return (dgettext(TEXT_DOMAIN, "Authentication failed"));
 905         case PAM_NEW_AUTHTOK_REQD:
 906                 return (dgettext(TEXT_DOMAIN, "Get new authentication token"));
 907         case PAM_CRED_INSUFFICIENT:
 908                 return (dgettext(TEXT_DOMAIN, "Insufficient credentials"));
 909         case PAM_AUTHINFO_UNAVAIL:
 910                 return (dgettext(TEXT_DOMAIN,
 911                     "Can not retrieve authentication info"));
 912         case PAM_USER_UNKNOWN:
 913                 return (dgettext(TEXT_DOMAIN, "No account present for user"));
 914         case PAM_CRED_UNAVAIL:
 915                 return (dgettext(TEXT_DOMAIN,
 916                     "Can not retrieve user credentials"));
 917         case PAM_CRED_EXPIRED:
 918                 return (dgettext(TEXT_DOMAIN,
 919                     "User credentials have expired"));
 920         case PAM_CRED_ERR:
 921                 return (dgettext(TEXT_DOMAIN,
 922                     "Failure setting user credentials"));
 923         case PAM_ACCT_EXPIRED:
 924                 return (dgettext(TEXT_DOMAIN, "User account has expired"));
 925         case PAM_AUTHTOK_EXPIRED:
 926                 return (dgettext(TEXT_DOMAIN, "User password has expired"));
 927         case PAM_SESSION_ERR:
 928                 return (dgettext(TEXT_DOMAIN,
 929                     "Can not make/remove entry for session"));
 930         case PAM_AUTHTOK_ERR:
 931                 return (dgettext(TEXT_DOMAIN,
 932                     "Authentication token manipulation error"));
 933         case PAM_AUTHTOK_RECOVERY_ERR:
 934                 return (dgettext(TEXT_DOMAIN,
 935                     "Authentication token can not be recovered"));
 936         case PAM_AUTHTOK_LOCK_BUSY:
 937                 return (dgettext(TEXT_DOMAIN,
 938                     "Authentication token lock busy"));
 939         case PAM_AUTHTOK_DISABLE_AGING:
 940                 return (dgettext(TEXT_DOMAIN,
 941                     "Authentication token aging disabled"));
 942         case PAM_NO_MODULE_DATA:
 943                 return (dgettext(TEXT_DOMAIN,
 944                     "Module specific data not found"));
 945         case PAM_IGNORE:
 946                 return (dgettext(TEXT_DOMAIN, "Ignore module"));
 947         case PAM_ABORT:
 948                 return (dgettext(TEXT_DOMAIN, "General PAM failure "));
 949         case PAM_TRY_AGAIN:
 950                 return (dgettext(TEXT_DOMAIN,
 951                     "Unable to complete operation. Try again"));
 952         default:
 953                 return (dgettext(TEXT_DOMAIN, "Unknown error"));
 954         }
 955 }
 956 
 957 static void *
 958 sm_name(int ind)
 959 {
 960         switch (ind) {
 961         case PAM_AUTHENTICATE:
 962                 return (PAM_SM_AUTHENTICATE);
 963         case PAM_SETCRED:
 964                 return (PAM_SM_SETCRED);
 965         case PAM_ACCT_MGMT:
 966                 return (PAM_SM_ACCT_MGMT);
 967         case PAM_OPEN_SESSION:
 968                 return (PAM_SM_OPEN_SESSION);
 969         case PAM_CLOSE_SESSION:
 970                 return (PAM_SM_CLOSE_SESSION);
 971         case PAM_CHAUTHTOK:
 972                 return (PAM_SM_CHAUTHTOK);
 973         }
 974         return (NULL);
 975 }
 976 
 977 static int
 978 (*func(pamtab_t *modulep, int ind))()
 979 {
 980         void    *funcp;
 981 
 982         if ((funcp = modulep->function_ptr) == NULL)
 983                 return (NULL);
 984 
 985         switch (ind) {
 986         case PAM_AUTHENTICATE:
 987                 return (((struct auth_module *)funcp)->pam_sm_authenticate);
 988         case PAM_SETCRED:
 989                 return (((struct auth_module *)funcp)->pam_sm_setcred);
 990         case PAM_ACCT_MGMT:
 991                 return (((struct account_module *)funcp)->pam_sm_acct_mgmt);
 992         case PAM_OPEN_SESSION:
 993                 return (((struct session_module *)funcp)->pam_sm_open_session);
 994         case PAM_CLOSE_SESSION:
 995                 return (((struct session_module *)funcp)->pam_sm_close_session);
 996         case PAM_CHAUTHTOK:
 997                 return (((struct password_module *)funcp)->pam_sm_chauthtok);
 998         }
 999         return (NULL);
1000 }
1001 
1002 /*
1003  * Run through the PAM service module stack for the given module type.
1004  */
1005 static int
1006 run_stack(pam_handle_t *pamh, int flags, int type, int def_err, int ind,
1007     char *function_name)
1008 {
1009         int     err = PAM_SYSTEM_ERR;  /* preset */
1010         int     optional_error = 0;
1011         int     required_error = 0;
1012         int     success = 0;
1013         pamtab_t *modulep;
1014         int     (*sm_func)();
1015         char    *service;
1016         char    *service_file;
1017         int     shardfile = 1;
1018 
1019         if (pamh == NULL)
1020                 return (PAM_SYSTEM_ERR);
1021 
1022         (void) pam_get_item(pamh, PAM_SERVICE, (void **)&service);
1023         if (service == NULL || *service == '\0') {
1024                 __pam_log(LOG_AUTH | LOG_ERR, "No service name");
1025                 return (PAM_SYSTEM_ERR);
1026         }
1027 
1028         /* read initial entries from /etc/pam.d/<service> */
1029         if (asprintf(&service_file, "%s%s", PAM_CONFIG_DIR, service) < 0)
1030                 return (PAM_SYSTEM_ERR);
1031         if ((err = read_pam_conf(pamh, service_file, service, shardfile))
1032             != PAM_SUCCESS) {
1033                 shardfile = 0;
1034                 pam_trace(PAM_DEBUG_CONF, "run_stack[%d:%s]: can't read "
1035                     "service-specific conf %s", pamh->include_depth,
1036                     pam_trace_cname(pamh), modulep->module_path, service_file);
1037 
1038                 /* fall back to reading initial entries from pam.conf */
1039                 if ((err = read_pam_conf(pamh, PAM_CONFIG, service, shardfile))
1040                     != PAM_SUCCESS) {
1041                         return (err);
1042                 }
1043         }
1044         free(service_file);
1045         service_file = NULL;
1046 
1047         if ((modulep =
1048             pamh->pam_conf_info[pamh->include_depth][type]) == NULL) {
1049                 __pam_log(LOG_AUTH | LOG_ERR, "%s no initial module present",
1050                     pam_trace_cname(pamh));
1051                 goto exit_return;
1052         }
1053 
1054         pamh->pam_inmodule = WO_OK;  /* OK to get AUTHTOK */
1055 include:
1056         pam_trace(PAM_DEBUG_MODULE,
1057             "[%d:%s]:run_stack:%s(%p, %x): %s", pamh->include_depth,
1058             pam_trace_cname(pamh), function_name, (void *)pamh, flags,
1059             modulep ? modulep->module_path : "NULL");
1060 
1061         while (modulep != NULL) {
1062                 if (modulep->pam_flag & PAM_INCLUDE) {
1063                         /* save the return location */
1064                         pamh->pam_conf_modulep[pamh->include_depth] =
1065                             modulep->next;
1066                         pam_trace(PAM_DEBUG_MODULE,
1067                             "setting for include[%d:%p]",
1068                             pamh->include_depth, (void *)modulep->next);
1069                         if (pamh->include_depth++ >= PAM_MAX_INCLUDE) {
1070                                 __pam_log(LOG_AUTH | LOG_ERR,
1071                                     "run_stack: includes too deep %d "
1072                                     "found trying to include %s from %s, %d "
1073                                     "allowed", pamh->include_depth,
1074                                     modulep->module_path, pamh->pam_conf_name
1075                                     [PAM_MAX_INCLUDE] == NULL ? "NULL" :
1076                                     pamh->pam_conf_name[PAM_MAX_INCLUDE],
1077                                     PAM_MAX_INCLUDE);
1078                                 goto exit_return;
1079                         }
1080                         if ((err = read_pam_conf(pamh,
1081                             modulep->module_path, service, shardfile))
1082                             != PAM_SUCCESS) {
1083                                 __pam_log(LOG_AUTH | LOG_ERR,
1084                                     "run_stack[%d:%s]: can't read included "
1085                                     "conf %s", pamh->include_depth,
1086                                     pam_trace_cname(pamh),
1087                                     modulep->module_path);
1088                                 goto exit_return;
1089                         }
1090                         if ((modulep = pamh->pam_conf_info
1091                             [pamh->include_depth][type]) == NULL) {
1092                                 __pam_log(LOG_AUTH | LOG_ERR,
1093                                     "run_stack[%d:%s]: no include module "
1094                                     "present %s", pamh->include_depth,
1095                                     pam_trace_cname(pamh), function_name);
1096                                 goto exit_return;
1097                         }
1098                         if (modulep->pam_flag & PAM_INCLUDE) {
1099                                 /* first line another include */
1100                                 goto include;
1101                         }
1102                         pam_trace(PAM_DEBUG_DEFAULT, "include[%d:%s]"
1103                             "(%p, %s)=%s", pamh->include_depth,
1104                             pam_trace_cname(pamh), (void *)pamh,
1105                             function_name, modulep->module_path);
1106                         if ((err = load_modules(pamh, type, sm_name(ind),
1107                             pamh->pam_conf_info
1108                             [pamh->include_depth][type])) != PAM_SUCCESS) {
1109                                 pam_trace(PAM_DEBUG_DEFAULT,
1110                                     "[%d:%s]:%s(%p, %x): load_modules failed",
1111                                     pamh->include_depth, pam_trace_cname(pamh),
1112                                     function_name, (void *)pamh, flags);
1113                                 goto exit_return;
1114                         }
1115                         if ((modulep = pamh->pam_conf_info
1116                             [pamh->include_depth][type]) == NULL) {
1117                                 __pam_log(LOG_AUTH | LOG_ERR,
1118                                     "%s no initial module present",
1119                                     pam_trace_cname(pamh));
1120                                 goto exit_return;
1121                         }
1122                 } else if ((err = load_modules(pamh, type, sm_name(ind),
1123                     modulep)) != PAM_SUCCESS) {
1124                         pam_trace(PAM_DEBUG_DEFAULT,
1125                             "[%d:%s]:%s(%p, %x): load_modules failed",
1126                             pamh->include_depth, pam_trace_cname(pamh),
1127                             function_name, (void *)pamh, flags);
1128                         goto exit_return;
1129                 }  /* PAM_INCLUDE */
1130                 sm_func = func(modulep, ind);
1131                 if (sm_func) {
1132                         err = sm_func(pamh, flags, modulep->module_argc,
1133                             (const char **)modulep->module_argv);
1134 
1135                         pam_trace(PAM_DEBUG_MODULE,
1136                             "[%d:%s]:%s(%p, %x): %s returned %s",
1137                             pamh->include_depth, pam_trace_cname(pamh),
1138                             function_name, (void *)pamh, flags,
1139                             modulep->module_path, pam_strerror(pamh, err));
1140 
1141                         switch (err) {
1142                         case PAM_IGNORE:
1143                                 /* do nothing */
1144                                 break;
1145                         case PAM_SUCCESS:
1146                                 if ((modulep->pam_flag & PAM_SUFFI_BIND) &&
1147                                     !required_error) {
1148                                         pamh->pam_inmodule = RW_OK;
1149                                         pam_trace(PAM_DEBUG_MODULE,
1150                                             "[%d:%s]:%s(%p, %x): %s: success",
1151                                             pamh->include_depth,
1152                                             pam_trace_cname(pamh),
1153                                             function_name, (void *)pamh, flags,
1154                                             (modulep->pam_flag & PAM_BINDING) ?
1155                                             PAM_BINDING_NAME :
1156                                             PAM_SUFFICIENT_NAME);
1157                                         goto exit_return;
1158                                 }
1159                                 success = 1;
1160                                 break;
1161                         case PAM_TRY_AGAIN:
1162                                 /*
1163                                  * We need to return immediately, and
1164                                  * we shouldn't reset the AUTHTOK item
1165                                  * since it is not an error per-se.
1166                                  */
1167                                 pamh->pam_inmodule = RW_OK;
1168                                 pam_trace(PAM_DEBUG_MODULE,
1169                                     "[%d:%s]:%s(%p, %x): TRY_AGAIN: %s",
1170                                     pamh->include_depth, pam_trace_cname(pamh),
1171                                     function_name, (void *)pamh, flags,
1172                                     pam_strerror(pamh, required_error ?
1173                                     required_error : err));
1174                                 err = required_error ? required_error : err;
1175                                 goto exit_return;
1176                         default:
1177                                 if (modulep->pam_flag & PAM_REQUISITE) {
1178                                         pamh->pam_inmodule = RW_OK;
1179                                         pam_trace(PAM_DEBUG_MODULE,
1180                                             "[%d:%s]:%s(%p, %x): requisite: %s",
1181                                             pamh->include_depth,
1182                                             pam_trace_cname(pamh),
1183                                             function_name, (void *)pamh, flags,
1184                                             pam_strerror(pamh,
1185                                             required_error ? required_error :
1186                                             err));
1187                                         err = required_error ?
1188                                             required_error : err;
1189                                         goto exit_return;
1190                                 } else if (modulep->pam_flag & PAM_REQRD_BIND) {
1191                                         if (!required_error)
1192                                                 required_error = err;
1193                                 } else {
1194                                         if (!optional_error)
1195                                                 optional_error = err;
1196                                 }
1197                                 pam_trace(PAM_DEBUG_DEFAULT,
1198                                     "[%d:%s]:%s(%p, %x): error %s",
1199                                     pamh->include_depth, pam_trace_cname(pamh),
1200                                     function_name, (void *)pamh, flags,
1201                                     pam_strerror(pamh, err));
1202                                 break;
1203                         }
1204                 }
1205                 modulep = modulep->next;
1206         }
1207 
1208         pam_trace(PAM_DEBUG_MODULE, "[%d:%s]:stack_end:%s(%p, %x): %s %s: %s",
1209             pamh->include_depth, pam_trace_cname(pamh), function_name,
1210             (void *)pamh, flags, pamh->include_depth ? "included" : "final",
1211             required_error ? "required" : success ? "success" :
1212             optional_error ? "optional" : "default",
1213             pam_strerror(pamh, required_error ? required_error :
1214             success ? PAM_SUCCESS : optional_error ? optional_error : def_err));
1215         if (pamh->include_depth > 0) {
1216                 free_pam_conf_info(pamh);
1217                 pamh->include_depth--;
1218                 /* continue at next entry */
1219                 modulep = pamh->pam_conf_modulep[pamh->include_depth];
1220                 pam_trace(PAM_DEBUG_MODULE, "looping for include[%d:%p]",
1221                     pamh->include_depth, (void *)modulep);
1222                 goto include;
1223         }
1224         free_pam_conf_info(pamh);
1225         pamh->pam_inmodule = RW_OK;
1226         if (required_error != 0)
1227                 return (required_error);
1228         else if (success != 0)
1229                 return (PAM_SUCCESS);
1230         else if (optional_error != 0)
1231                 return (optional_error);
1232         else
1233                 return (def_err);
1234 
1235 exit_return:
1236         /*
1237          * All done at whatever depth we're at.
1238          * Go back to not having read /etc/pam.conf
1239          */
1240         while (pamh->include_depth > 0) {
1241                 free_pam_conf_info(pamh);
1242                 pamh->include_depth--;
1243         }
1244         free_pam_conf_info(pamh);
1245         pamh->pam_inmodule = RW_OK;
1246         return (err);
1247 }
1248 
1249 /*
1250  * pam_authenticate - authenticate a user
1251  */
1252 
1253 int
1254 pam_authenticate(pam_handle_t *pamh, int flags)
1255 {
1256         int     retval;
1257 
1258         retval = run_stack(pamh, flags, PAM_AUTH_MODULE, PAM_AUTH_ERR,
1259             PAM_AUTHENTICATE, "pam_authenticate");
1260 
1261         if (retval != PAM_SUCCESS)
1262                 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1263         return (retval);
1264 }
1265 
1266 /*
1267  * pam_setcred - modify or retrieve user credentials
1268  */
1269 
1270 int
1271 pam_setcred(pam_handle_t *pamh, int flags)
1272 {
1273         int     retval;
1274 
1275         retval = run_stack(pamh, flags, PAM_AUTH_MODULE, PAM_CRED_ERR,
1276             PAM_SETCRED, "pam_setcred");
1277 
1278         if (retval != PAM_SUCCESS)
1279                 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1280         return (retval);
1281 }
1282 
1283 /*
1284  * pam_acct_mgmt - check password aging, account expiration
1285  */
1286 
1287 int
1288 pam_acct_mgmt(pam_handle_t *pamh, int flags)
1289 {
1290         int     retval;
1291 
1292         retval = run_stack(pamh, flags, PAM_ACCOUNT_MODULE, PAM_ACCT_EXPIRED,
1293             PAM_ACCT_MGMT, "pam_acct_mgmt");
1294 
1295         if (retval != PAM_SUCCESS &&
1296             retval != PAM_NEW_AUTHTOK_REQD) {
1297                 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1298         }
1299         return (retval);
1300 }
1301 
1302 /*
1303  * pam_open_session - begin session management
1304  */
1305 
1306 int
1307 pam_open_session(pam_handle_t *pamh, int flags)
1308 {
1309         int     retval;
1310 
1311         retval = run_stack(pamh, flags, PAM_SESSION_MODULE, PAM_SESSION_ERR,
1312             PAM_OPEN_SESSION, "pam_open_session");
1313 
1314         if (retval != PAM_SUCCESS)
1315                 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1316         return (retval);
1317 }
1318 
1319 /*
1320  * pam_close_session - terminate session management
1321  */
1322 
1323 int
1324 pam_close_session(pam_handle_t *pamh, int flags)
1325 {
1326         int     retval;
1327 
1328         retval = run_stack(pamh, flags, PAM_SESSION_MODULE, PAM_SESSION_ERR,
1329             PAM_CLOSE_SESSION, "pam_close_session");
1330 
1331         if (retval != PAM_SUCCESS)
1332                 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1333         return (retval);
1334 }
1335 
1336 /*
1337  * pam_chauthtok - change user authentication token
1338  */
1339 
1340 int
1341 pam_chauthtok(pam_handle_t *pamh, int flags)
1342 {
1343         int     retval;
1344 
1345         /* do not let apps use PAM_PRELIM_CHECK or PAM_UPDATE_AUTHTOK */
1346         if (flags & (PAM_PRELIM_CHECK | PAM_UPDATE_AUTHTOK)) {
1347                 pam_trace(PAM_DEBUG_DEFAULT,
1348                     "pam_chauthtok(%p, %x): %s", (void *)pamh, flags,
1349                     pam_strerror(pamh, PAM_SYMBOL_ERR));
1350                 return (PAM_SYMBOL_ERR);
1351         }
1352 
1353         /* 1st pass: PRELIM CHECK */
1354         retval = run_stack(pamh, flags | PAM_PRELIM_CHECK, PAM_PASSWORD_MODULE,
1355             PAM_AUTHTOK_ERR, PAM_CHAUTHTOK, "pam_chauthtok-prelim");
1356 
1357         if (retval == PAM_TRY_AGAIN)
1358                 return (retval);
1359 
1360         if (retval != PAM_SUCCESS) {
1361                 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1362                 return (retval);
1363         }
1364 
1365         /* 2nd pass: UPDATE AUTHTOK */
1366         retval = run_stack(pamh, flags | PAM_UPDATE_AUTHTOK,
1367             PAM_PASSWORD_MODULE, PAM_AUTHTOK_ERR, PAM_CHAUTHTOK,
1368             "pam_chauthtok-update");
1369 
1370         if (retval != PAM_SUCCESS)
1371                 (void) pam_set_item(pamh, PAM_AUTHTOK, NULL);
1372 
1373         return (retval);
1374 }
1375 
1376 /*
1377  * pam_putenv - add an environment variable to the PAM handle
1378  *      if name_value == 'NAME=VALUE'   then set variable to the value
1379  *      if name_value == 'NAME='        then set variable to an empty value
1380  *      if name_value == 'NAME'         then delete the variable
1381  */
1382 
1383 int
1384 pam_putenv(pam_handle_t *pamh, const char *name_value)
1385 {
1386         int             error = PAM_SYSTEM_ERR;
1387         char            *equal_sign = 0;
1388         char            *name = NULL, *value = NULL, *tmp_value = NULL;
1389         env_list        *traverse, *trail;
1390 
1391         pam_trace(PAM_DEBUG_DEFAULT,
1392             "pam_putenv(%p, %s)", (void *)pamh,
1393             name_value ? name_value : "NULL");
1394 
1395         if (pamh == NULL || name_value == NULL)
1396                 goto out;
1397 
1398         /* see if we were passed 'NAME=VALUE', 'NAME=', or 'NAME' */
1399         if ((equal_sign = strchr(name_value, '=')) != 0) {
1400                 if ((name = calloc(equal_sign - name_value + 1,
1401                     sizeof (char))) == 0) {
1402                         error = PAM_BUF_ERR;
1403                         goto out;
1404                 }
1405                 (void) strncpy(name, name_value, equal_sign - name_value);
1406                 if ((value = strdup(++equal_sign)) == 0) {
1407                         error = PAM_BUF_ERR;
1408                         goto out;
1409                 }
1410         } else {
1411                 if ((name = strdup(name_value)) == 0) {
1412                         error = PAM_BUF_ERR;
1413                         goto out;
1414                 }
1415         }
1416 
1417         /* check to see if we already have this variable in the PAM handle */
1418         traverse = pamh->pam_env;
1419         trail = traverse;
1420         while (traverse && strncmp(traverse->name, name, strlen(name))) {
1421                 trail = traverse;
1422                 traverse = traverse->next;
1423         }
1424 
1425         if (traverse) {
1426                 /* found a match */
1427                 if (value == 0) {
1428                         /* remove the env variable */
1429                         if (pamh->pam_env == traverse)
1430                                 pamh->pam_env = traverse->next;
1431                         else
1432                                 trail->next = traverse->next;
1433                         free_env(traverse);
1434                 } else if (strlen(value) == 0) {
1435                         /* set env variable to empty value */
1436                         if ((tmp_value = strdup("")) == 0) {
1437                                 error = PAM_SYSTEM_ERR;
1438                                 goto out;
1439                         }
1440                         free(traverse->value);
1441                         traverse->value = tmp_value;
1442                 } else {
1443                         /* set the new value */
1444                         if ((tmp_value = strdup(value)) == 0) {
1445                                 error = PAM_SYSTEM_ERR;
1446                                 goto out;
1447                         }
1448                         free(traverse->value);
1449                         traverse->value = tmp_value;
1450                 }
1451 
1452         } else if (traverse == 0 && value) {
1453                 /*
1454                  * could not find a match in the PAM handle.
1455                  * add the new value if there is one
1456                  */
1457                 if ((traverse = calloc(1, sizeof (env_list))) == 0) {
1458                         error = PAM_BUF_ERR;
1459                         goto out;
1460                 }
1461                 if ((traverse->name = strdup(name)) == 0) {
1462                         free_env(traverse);
1463                         error = PAM_BUF_ERR;
1464                         goto out;
1465                 }
1466                 if ((traverse->value = strdup(value)) == 0) {
1467                         free_env(traverse);
1468                         error = PAM_BUF_ERR;
1469                         goto out;
1470                 }
1471                 if (trail == 0) {
1472                         /* new head of list */
1473                         pamh->pam_env = traverse;
1474                 } else {
1475                         /* adding to end of list */
1476                         trail->next = traverse;
1477                 }
1478         }
1479 
1480         error = PAM_SUCCESS;
1481 out:
1482         if (error != PAM_SUCCESS) {
1483                 if (traverse) {
1484                         if (traverse->name)
1485                                 free(traverse->name);
1486                         if (traverse->value)
1487                                 free(traverse->value);
1488                         free(traverse);
1489                 }
1490         }
1491         if (name)
1492                 free(name);
1493         if (value)
1494                 free(value);
1495         return (error);
1496 }
1497 
1498 /*
1499  * pam_getenv - retrieve an environment variable from the PAM handle
1500  */
1501 char *
1502 pam_getenv(pam_handle_t *pamh, const char *name)
1503 {
1504         int             error = PAM_SYSTEM_ERR;
1505         env_list        *traverse;
1506 
1507         pam_trace(PAM_DEBUG_DEFAULT,
1508             "pam_getenv(%p, %p)", (void *)pamh, (void *)name);
1509 
1510         if (pamh == NULL || name == NULL)
1511                 goto out;
1512 
1513         /* check to see if we already have this variable in the PAM handle */
1514         traverse = pamh->pam_env;
1515         while (traverse && strncmp(traverse->name, name, strlen(name))) {
1516                 traverse = traverse->next;
1517         }
1518         error = (traverse ? PAM_SUCCESS : PAM_SYSTEM_ERR);
1519         pam_trace(PAM_DEBUG_DEFAULT,
1520             "pam_getenv(%p, %s)=%s", (void *)pamh, name,
1521             traverse ? traverse->value : "NULL");
1522 out:
1523         return (error ? NULL : strdup(traverse->value));
1524 }
1525 
1526 /*
1527  * pam_getenvlist - retrieve all environment variables from the PAM handle
1528  *                  in a NULL terminated array. On error, return NULL.
1529  */
1530 char **
1531 pam_getenvlist(pam_handle_t *pamh)
1532 {
1533         int             error = PAM_SYSTEM_ERR;
1534         char            **list = 0;
1535         int             length = 0;
1536         env_list        *traverse;
1537         char            *tenv;
1538         size_t          tenv_size;
1539 
1540         pam_trace(PAM_DEBUG_DEFAULT,
1541             "pam_getenvlist(%p)", (void *)pamh);
1542 
1543         if (pamh == NULL)
1544                 goto out;
1545 
1546         /* find out how many environment variables we have */
1547         traverse = pamh->pam_env;
1548         while (traverse) {
1549                 length++;
1550                 traverse = traverse->next;
1551         }
1552 
1553         /* allocate the array we will return to the caller */
1554         if ((list = calloc(length + 1, sizeof (char *))) == NULL) {
1555                 error = PAM_BUF_ERR;
1556                 goto out;
1557         }
1558 
1559         /* add the variables one by one */
1560         length = 0;
1561         traverse = pamh->pam_env;
1562         while (traverse != NULL) {
1563                 tenv_size = strlen(traverse->name) +
1564                     strlen(traverse->value) + 2; /* name=val\0 */
1565                 if ((tenv = malloc(tenv_size)) == NULL) {
1566                         error = PAM_BUF_ERR;
1567                         goto out;
1568                 }
1569                 /*LINTED*/
1570                 (void) sprintf(tenv, "%s=%s", traverse->name, traverse->value);
1571                 list[length++] = tenv;
1572                 traverse = traverse->next;
1573         }
1574         list[length] = NULL;
1575 
1576         error = PAM_SUCCESS;
1577 out:
1578         if (error != PAM_SUCCESS) {
1579                 /* free the partially constructed list */
1580                 if (list) {
1581                         length = 0;
1582                         while (list[length] != NULL) {
1583                                 free(list[length]);
1584                                 length++;
1585                         }
1586                         free(list);
1587                 }
1588         }
1589         return (error ? NULL : list);
1590 }
1591 
1592 /*
1593  * Routines to load a requested module on demand
1594  */
1595 
1596 /*
1597  * load_modules - load the requested module.
1598  *                if the dlopen or dlsym fail, then
1599  *                the module is ignored.
1600  */
1601 
1602 static int
1603 load_modules(pam_handle_t *pamh, int type, char *function_name,
1604     pamtab_t *pam_entry)
1605 {
1606         void    *mh;
1607         struct  auth_module *authp;
1608         struct  account_module *accountp;
1609         struct  session_module *sessionp;
1610         struct  password_module *passwdp;
1611         int     loading_functions = 0; /* are we currently loading functions? */
1612 
1613         pam_trace(PAM_DEBUG_MODULE, "load_modules[%d:%s](%p, %s)=%s:%s",
1614             pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1615             function_name, pam_trace_fname(pam_entry->pam_flag),
1616             pam_entry->module_path);
1617 
1618         while (pam_entry != NULL) {
1619                 pam_trace(PAM_DEBUG_DEFAULT,
1620                     "while load_modules[%d:%s](%p, %s)=%s",
1621                     pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1622                     function_name, pam_entry->module_path);
1623 
1624                 if (pam_entry->pam_flag & PAM_INCLUDE) {
1625                         pam_trace(PAM_DEBUG_DEFAULT,
1626                             "done load_modules[%d:%s](%p, %s)=%s",
1627                             pamh->include_depth, pam_trace_cname(pamh),
1628                             (void *)pamh, function_name,
1629                             pam_entry->module_path);
1630                         return (PAM_SUCCESS);
1631                 }
1632                 switch (type) {
1633                 case PAM_AUTH_MODULE:
1634 
1635                         /* if the function has already been loaded, return */
1636                         authp = pam_entry->function_ptr;
1637                         if (!loading_functions &&
1638                             (((strcmp(function_name, PAM_SM_AUTHENTICATE)
1639                             == 0) && authp && authp->pam_sm_authenticate) ||
1640                             ((strcmp(function_name, PAM_SM_SETCRED) == 0) &&
1641                             authp && authp->pam_sm_setcred))) {
1642                                 return (PAM_SUCCESS);
1643                         }
1644 
1645                         /* function has not been loaded yet */
1646                         loading_functions = 1;
1647                         if (authp == NULL) {
1648                                 authp = calloc(1, sizeof (struct auth_module));
1649                                 if (authp == NULL)
1650                                         return (PAM_BUF_ERR);
1651                         }
1652 
1653                         /* if open_module fails, return error */
1654                         if ((mh = open_module(pamh,
1655                             pam_entry->module_path)) == NULL) {
1656                                 __pam_log(LOG_AUTH | LOG_ERR,
1657                                     "load_modules[%d:%s]: can not open module "
1658                                     "%s", pamh->include_depth,
1659                                     pam_trace_cname(pamh),
1660                                     pam_entry->module_path);
1661                                 free(authp);
1662                                 return (PAM_OPEN_ERR);
1663                         }
1664 
1665                         /* load the authentication function */
1666                         if (strcmp(function_name, PAM_SM_AUTHENTICATE) == 0) {
1667                                 if (load_function(mh, PAM_SM_AUTHENTICATE,
1668                                     &authp->pam_sm_authenticate)
1669                                     != PAM_SUCCESS) {
1670                                         /* return error if dlsym fails */
1671                                         free(authp);
1672                                         return (PAM_SYMBOL_ERR);
1673                                 }
1674 
1675                         /* load the setcred function */
1676                         } else if (strcmp(function_name, PAM_SM_SETCRED) == 0) {
1677                                 if (load_function(mh, PAM_SM_SETCRED,
1678                                     &authp->pam_sm_setcred) != PAM_SUCCESS) {
1679                                         /* return error if dlsym fails */
1680                                         free(authp);
1681                                         return (PAM_SYMBOL_ERR);
1682                                 }
1683                         }
1684                         pam_entry->function_ptr = authp;
1685                         break;
1686                 case PAM_ACCOUNT_MODULE:
1687                         accountp = pam_entry->function_ptr;
1688                         if (!loading_functions &&
1689                             (strcmp(function_name, PAM_SM_ACCT_MGMT) == 0) &&
1690                             accountp && accountp->pam_sm_acct_mgmt) {
1691                                 return (PAM_SUCCESS);
1692                         }
1693 
1694                         /*
1695                          * If functions are added to the account module,
1696                          * verify that one of the other functions hasn't
1697                          * already loaded it.  See PAM_AUTH_MODULE code.
1698                          */
1699                         loading_functions = 1;
1700                         accountp = calloc(1, sizeof (struct account_module));
1701                         if (accountp == NULL)
1702                                 return (PAM_BUF_ERR);
1703 
1704                         /* if open_module fails, return error */
1705                         if ((mh = open_module(pamh,
1706                             pam_entry->module_path)) == NULL) {
1707                                 __pam_log(LOG_AUTH | LOG_ERR,
1708                                     "load_modules[%d:%s]: can not open module "
1709                                     "%s", pamh->include_depth,
1710                                     pam_trace_cname(pamh),
1711                                     pam_entry->module_path);
1712                                 free(accountp);
1713                                 return (PAM_OPEN_ERR);
1714                         }
1715 
1716                         if (load_function(mh, PAM_SM_ACCT_MGMT,
1717                             &accountp->pam_sm_acct_mgmt) != PAM_SUCCESS) {
1718                                 __pam_log(LOG_AUTH | LOG_ERR,
1719                                     "load_modules[%d:%s]: pam_sm_acct_mgmt() "
1720                                     "missing", pamh->include_depth,
1721                                     pam_trace_cname(pamh));
1722                                 free(accountp);
1723                                 return (PAM_SYMBOL_ERR);
1724                         }
1725                         pam_entry->function_ptr = accountp;
1726                         break;
1727                 case PAM_SESSION_MODULE:
1728                         sessionp = pam_entry->function_ptr;
1729                         if (!loading_functions &&
1730                             (((strcmp(function_name,
1731                             PAM_SM_OPEN_SESSION) == 0) &&
1732                             sessionp && sessionp->pam_sm_open_session) ||
1733                             ((strcmp(function_name,
1734                             PAM_SM_CLOSE_SESSION) == 0) &&
1735                             sessionp && sessionp->pam_sm_close_session))) {
1736                                 return (PAM_SUCCESS);
1737                         }
1738 
1739                         loading_functions = 1;
1740                         if (sessionp == NULL) {
1741                                 sessionp = calloc(1,
1742                                     sizeof (struct session_module));
1743                                 if (sessionp == NULL)
1744                                         return (PAM_BUF_ERR);
1745                         }
1746 
1747                         /* if open_module fails, return error */
1748                         if ((mh = open_module(pamh,
1749                             pam_entry->module_path)) == NULL) {
1750                                 __pam_log(LOG_AUTH | LOG_ERR,
1751                                     "load_modules[%d:%s]: can not open module "
1752                                     "%s", pamh->include_depth,
1753                                     pam_trace_cname(pamh),
1754                                     pam_entry->module_path);
1755                                 free(sessionp);
1756                                 return (PAM_OPEN_ERR);
1757                         }
1758 
1759                         if ((strcmp(function_name, PAM_SM_OPEN_SESSION) == 0) &&
1760                             load_function(mh, PAM_SM_OPEN_SESSION,
1761                             &sessionp->pam_sm_open_session) != PAM_SUCCESS) {
1762                                 free(sessionp);
1763                                 return (PAM_SYMBOL_ERR);
1764                         } else if ((strcmp(function_name,
1765                             PAM_SM_CLOSE_SESSION) == 0) &&
1766                             load_function(mh, PAM_SM_CLOSE_SESSION,
1767                             &sessionp->pam_sm_close_session) != PAM_SUCCESS) {
1768                                 free(sessionp);
1769                                 return (PAM_SYMBOL_ERR);
1770                         }
1771                         pam_entry->function_ptr = sessionp;
1772                         break;
1773                 case PAM_PASSWORD_MODULE:
1774                         passwdp = pam_entry->function_ptr;
1775                         if (!loading_functions &&
1776                             (strcmp(function_name, PAM_SM_CHAUTHTOK) == 0) &&
1777                             passwdp && passwdp->pam_sm_chauthtok) {
1778                                 return (PAM_SUCCESS);
1779                         }
1780 
1781                         /*
1782                          * If functions are added to the password module,
1783                          * verify that one of the other functions hasn't
1784                          * already loaded it.  See PAM_AUTH_MODULE code.
1785                          */
1786                         loading_functions = 1;
1787                         passwdp = calloc(1, sizeof (struct password_module));
1788                         if (passwdp == NULL)
1789                                 return (PAM_BUF_ERR);
1790 
1791                         /* if open_module fails, continue */
1792                         if ((mh = open_module(pamh,
1793                             pam_entry->module_path)) == NULL) {
1794                                 __pam_log(LOG_AUTH | LOG_ERR,
1795                                     "load_modules[%d:%s]: can not open module "
1796                                     "%s", pamh->include_depth,
1797                                     pam_trace_cname(pamh),
1798                                     pam_entry->module_path);
1799                                 free(passwdp);
1800                                 return (PAM_OPEN_ERR);
1801                         }
1802 
1803                         if (load_function(mh, PAM_SM_CHAUTHTOK,
1804                             &passwdp->pam_sm_chauthtok) != PAM_SUCCESS) {
1805                                 free(passwdp);
1806                                 return (PAM_SYMBOL_ERR);
1807                         }
1808                         pam_entry->function_ptr = passwdp;
1809                         break;
1810                 default:
1811                         pam_trace(PAM_DEBUG_DEFAULT,
1812                             "load_modules[%d:%s](%p, %s): unsupported type %d",
1813                             pamh->include_depth, pam_trace_cname(pamh),
1814                             (void *)pamh, function_name, type);
1815                         break;
1816                 }
1817 
1818                 pam_entry = pam_entry->next;
1819         } /* while */
1820 
1821         pam_trace(PAM_DEBUG_MODULE, "load_modules[%d:%s](%p, %s)=done",
1822             pamh->include_depth, pam_trace_cname(pamh), (void *)pamh,
1823             function_name);
1824 
1825         return (PAM_SUCCESS);
1826 }
1827 
1828 /*
1829  * open_module          - Open the module first checking for
1830  *                        propers modes and ownerships on the file.
1831  */
1832 
1833 static void *
1834 open_module(pam_handle_t *pamh, char *module_so)
1835 {
1836         struct stat64   stb;
1837         char            *errmsg;
1838         void            *lfd;
1839         fd_list         *module_fds = 0;
1840         fd_list         *trail = 0;
1841         fd_list         *traverse = 0;
1842 
1843         /* Check the ownership and file modes */
1844         if (stat64(module_so, &stb) < 0) {
1845                 __pam_log(LOG_AUTH | LOG_ERR,
1846                     "open_module[%d:%s]: stat(%s) failed: %s",
1847                     pamh->include_depth, pam_trace_cname(pamh), module_so,
1848                     strerror(errno));
1849                 return (NULL);
1850         }
1851         if (stb.st_uid != (uid_t)0) {
1852                 __pam_log(LOG_AUTH | LOG_ALERT,
1853                     "open_module[%d:%s]: Owner of the module %s is not root",
1854                     pamh->include_depth, pam_trace_cname(pamh), module_so);
1855                 return (NULL);
1856         }
1857         if (stb.st_mode & S_IWGRP) {
1858                 __pam_log(LOG_AUTH | LOG_ALERT,
1859                     "open_module[%d:%s]: module %s writable by group",
1860                     pamh->include_depth, pam_trace_cname(pamh), module_so);
1861                 return (NULL);
1862         }
1863         if (stb.st_mode & S_IWOTH) {
1864                 __pam_log(LOG_AUTH | LOG_ALERT,
1865                     "open_module[%d:%s]: module %s writable by world",
1866                     pamh->include_depth, pam_trace_cname(pamh), module_so);
1867                 return (NULL);
1868         }
1869 
1870         /*
1871          * Perform the dlopen()
1872          */
1873         lfd = (void *)dlopen(module_so, RTLD_LAZY);
1874 
1875         if (lfd == NULL) {
1876                 errmsg = dlerror();
1877                 __pam_log(LOG_AUTH | LOG_ERR, "open_module[%d:%s]: %s "
1878                     "failed: %s", pamh->include_depth, pam_trace_cname(pamh),
1879                     module_so, errmsg != NULL ? errmsg : "Unknown error");
1880                 return (NULL);
1881         } else {
1882                 /* add this fd to the pam handle */
1883                 if ((module_fds = calloc(1, sizeof (fd_list))) == 0) {
1884                         (void) dlclose(lfd);
1885                         lfd = 0;
1886                         return (NULL);
1887                 }
1888                 module_fds->mh = lfd;
1889 
1890                 if (pamh->fd == 0) {
1891                         /* adding new head of list */
1892                         pamh->fd = module_fds;
1893                 } else {
1894                         /* appending to end of list */
1895                         traverse = pamh->fd;
1896                         while (traverse) {
1897                                 trail = traverse;
1898                                 traverse = traverse->next;
1899                         }
1900                         trail->next = module_fds;
1901                 }
1902         }
1903 
1904         return (lfd);
1905 }
1906 
1907 /*
1908  * load_function - call dlsym() to resolve the function address
1909  */
1910 static int
1911 load_function(void *lfd, char *name, int (**func)())
1912 {
1913         char *errmsg = NULL;
1914 
1915         if (lfd == NULL)
1916                 return (PAM_SYMBOL_ERR);
1917 
1918         *func = (int (*)())dlsym(lfd, name);
1919         if (*func == NULL) {
1920                 errmsg = dlerror();
1921                 __pam_log(LOG_AUTH | LOG_ERR, "dlsym failed %s: error %s",
1922                     name, errmsg != NULL ? errmsg : "Unknown error");
1923                 return (PAM_SYMBOL_ERR);
1924         }
1925 
1926         pam_trace(PAM_DEBUG_DEFAULT,
1927             "load_function: successful load of %s", name);
1928         return (PAM_SUCCESS);
1929 }
1930 
1931 /*
1932  * Routines to read the pam.conf configuration file
1933  */
1934 
1935 /*
1936  * open_pam_conf - open the pam.conf config file
1937  */
1938 
1939 static int
1940 open_pam_conf(struct pam_fh **pam_fh, pam_handle_t *pamh, char *config,
1941     int shardfile)
1942 {
1943         struct stat64   stb;
1944         int             fd;
1945 
1946         if ((fd = open(config, O_RDONLY)) == -1) {
1947                 if (!shardfile)
1948                         __pam_log(LOG_AUTH | LOG_ALERT,
1949                             "open_pam_conf[%d:%s]: open(%s) failed: %s",
1950                             pamh->include_depth, pam_trace_cname(pamh), config,
1951                             strerror(errno));
1952                 return (0);
1953         }
1954         /* Check the ownership and file modes */
1955         if (fstat64(fd, &stb) < 0) {
1956                 __pam_log(LOG_AUTH | LOG_ALERT,
1957                     "open_pam_conf[%d:%s]: stat(%s) failed: %s",
1958                     pamh->include_depth, pam_trace_cname(pamh), config,
1959                     strerror(errno));
1960                 (void) close(fd);
1961                 return (0);
1962         }
1963         if (stb.st_uid != (uid_t)0) {
1964                 __pam_log(LOG_AUTH | LOG_ALERT,
1965                     "open_pam_conf[%d:%s]: Owner of %s is not root",
1966                     pamh->include_depth, pam_trace_cname(pamh), config);
1967                 (void) close(fd);
1968                 return (0);
1969         }
1970         if (stb.st_mode & S_IWGRP) {
1971                 __pam_log(LOG_AUTH | LOG_ALERT,
1972                     "open_pam_conf[%d:%s]: %s writable by group",
1973                     pamh->include_depth, pam_trace_cname(pamh), config);
1974                 (void) close(fd);
1975                 return (0);
1976         }
1977         if (stb.st_mode & S_IWOTH) {
1978                 __pam_log(LOG_AUTH | LOG_ALERT,
1979                     "open_pam_conf[%d:%s]: %s writable by world",
1980                     pamh->include_depth, pam_trace_cname(pamh), config);
1981                 (void) close(fd);
1982                 return (0);
1983         }
1984         if ((*pam_fh = calloc(1, sizeof (struct pam_fh))) == NULL) {
1985                 (void) close(fd);
1986                 return (0);
1987         }
1988         (*pam_fh)->fconfig = fd;
1989         (*pam_fh)->bufsize = (size_t)stb.st_size;
1990         if (((*pam_fh)->data = mmap(0, (*pam_fh)->bufsize, PROT_READ,
1991             MAP_PRIVATE, (*pam_fh)->fconfig, 0)) == MAP_FAILED) {
1992                 (void) close(fd);
1993                 free (*pam_fh);
1994                 return (0);
1995         }
1996         (*pam_fh)->bufferp = (*pam_fh)->data;
1997 
1998         return (1);
1999 }
2000 
2001 /*
2002  * close_pam_conf - close pam.conf
2003  */
2004 
2005 static void
2006 close_pam_conf(struct pam_fh *pam_fh)
2007 {
2008         (void) munmap(pam_fh->data, pam_fh->bufsize);
2009         (void) close(pam_fh->fconfig);
2010         free(pam_fh);
2011 }
2012 
2013 /*
2014  * read_pam_conf - read in each entry in pam.conf and store info
2015  *                 under the pam handle.
2016  */
2017 
2018 static int
2019 read_pam_conf(pam_handle_t *pamh, char *config, char *service, int shardfile)
2020 {
2021         struct pam_fh   *pam_fh;
2022         pamtab_t        *pamentp;
2023         pamtab_t        *tpament;
2024         int             error;
2025         int             i = pamh->include_depth;     /* include depth */
2026         int             j;
2027         /*
2028          * service types:
2029          * error (-1), "auth" (0), "account" (1), "session" (2), "password" (3)
2030          */
2031         int service_found[PAM_NUM_MODULE_TYPES+1] = {0, 0, 0, 0, 0};
2032 
2033         pamh->pam_conf_name[i] = strdup(config);
2034         pam_trace(PAM_DEBUG_CONF, "read_pam_conf[%d:%s](%p) open(%s)",
2035             i, pam_trace_cname(pamh), (void *)pamh, config);
2036         if (open_pam_conf(&pam_fh, pamh, config, shardfile) == 0) {
2037                 return (PAM_SYSTEM_ERR);
2038         }
2039 
2040         while ((error =
2041             get_pam_conf_entry(pam_fh, pamh, &pamentp, service, shardfile))
2042             == PAM_SUCCESS && pamentp) {
2043 
2044                 /* See if entry is this service and valid */
2045                 if (verify_pam_conf(pamentp, service)) {
2046                         pam_trace(PAM_DEBUG_CONF,
2047                             "read_pam_conf[%d:%s](%p): bad entry error %s",
2048                             i, pam_trace_cname(pamh), (void *)pamh, service);
2049 
2050                         error = PAM_SYSTEM_ERR;
2051                         free_pamconf(pamentp);
2052                         goto out;
2053                 }
2054                 if (strcasecmp(pamentp->pam_service, service) == 0) {
2055                         pam_trace(PAM_DEBUG_CONF,
2056                             "read_pam_conf[%d:%s](%p): processing %s",
2057                             i, pam_trace_cname(pamh), (void *)pamh, service);
2058                         /* process first service entry */
2059                         if (service_found[pamentp->pam_type + 1] == 0) {
2060                                 /* purge "other" entries */
2061                                 while ((tpament = pamh->pam_conf_info[i]
2062                                     [pamentp->pam_type]) != NULL) {
2063                                         pam_trace(PAM_DEBUG_CONF,
2064                                             "read_pam_conf(%p): purging "
2065                                             "\"other\"[%d:%s][%s]",
2066                                             (void *)pamh, i,
2067                                             pam_trace_cname(pamh),
2068                                             pam_snames[pamentp->pam_type]);
2069                                         pamh->pam_conf_info[i]
2070                                             [pamentp->pam_type] = tpament->next;
2071                                         free_pamconf(tpament);
2072                                 }
2073                                 /* add first service entry */
2074                                 pam_trace(PAM_DEBUG_CONF,
2075                                     "read_pam_conf(%p): adding 1st "
2076                                     "%s[%d:%s][%s]",
2077                                     (void *)pamh, service, i,
2078                                     pam_trace_cname(pamh),
2079                                     pam_snames[pamentp->pam_type]);
2080                                 pamh->pam_conf_info[i][pamentp->pam_type] =
2081                                     pamentp;
2082                                 service_found[pamentp->pam_type + 1] = 1;
2083                         } else {
2084                                 /* append more service entries */
2085                                 pam_trace(PAM_DEBUG_CONF,
2086                                     "read_pam_conf(%p): adding more "
2087                                     "%s[%d:%s][%s]",
2088                                     (void *)pamh, service, i,
2089                                     pam_trace_cname(pamh),
2090                                     pam_snames[pamentp->pam_type]);
2091                                 tpament =
2092                                     pamh->pam_conf_info[i][pamentp->pam_type];
2093                                 while (tpament->next != NULL) {
2094                                         tpament = tpament->next;
2095                                 }
2096                                 tpament->next = pamentp;
2097                         }
2098                 } else if (service_found[pamentp->pam_type + 1] == 0) {
2099                         /* See if "other" entry available and valid */
2100                         if (verify_pam_conf(pamentp, "other")) {
2101                                 pam_trace(PAM_DEBUG_CONF,
2102                                     "read_pam_conf(%p): bad entry error %s "
2103                                     "\"other\"[%d:%s]",
2104                                     (void *)pamh, service, i,
2105                                     pam_trace_cname(pamh));
2106                                 error = PAM_SYSTEM_ERR;
2107                                 free_pamconf(pamentp);
2108                                 goto out;
2109                         }
2110                         if (strcasecmp(pamentp->pam_service, "other") == 0) {
2111                                 pam_trace(PAM_DEBUG_CONF,
2112                                     "read_pam_conf(%p): processing "
2113                                     "\"other\"[%d:%s]", (void *)pamh, i,
2114                                     pam_trace_cname(pamh));
2115                                 if ((tpament = pamh->pam_conf_info[i]
2116                                     [pamentp->pam_type]) == NULL) {
2117                                         /* add first "other" entry */
2118                                         pam_trace(PAM_DEBUG_CONF,
2119                                             "read_pam_conf(%p): adding 1st "
2120                                             "other[%d:%s][%s]", (void *)pamh, i,
2121                                             pam_trace_cname(pamh),
2122                                             pam_snames[pamentp->pam_type]);
2123                                         pamh->pam_conf_info[i]
2124                                             [pamentp->pam_type] = pamentp;
2125                                 } else {
2126                                         /* append more "other" entries */
2127                                         pam_trace(PAM_DEBUG_CONF,
2128                                             "read_pam_conf(%p): adding more "
2129                                             "other[%d:%s][%s]", (void *)pamh, i,
2130                                             pam_trace_cname(pamh),
2131                                             pam_snames[pamentp->pam_type]);
2132                                         while (tpament->next != NULL) {
2133                                                 tpament = tpament->next;
2134                                         }
2135                                         tpament->next = pamentp;
2136                                 }
2137                         } else {
2138                                 /* irrelevant entry */
2139                                 free_pamconf(pamentp);
2140                         }
2141                 } else {
2142                         /* irrelevant entry */
2143                         free_pamconf(pamentp);
2144                 }
2145         }
2146 
2147         /*
2148          * If this is a shard file and we have no entries for this
2149          * module type (e.g. "account"), then generate a single
2150          * "include" rule for the shard file "other" as a fallback.
2151          */
2152         if (shardfile && strcasecmp(service, "other") != 0) {
2153                 for (j = 0; j < PAM_NUM_MODULE_TYPES; j++) {
2154                         if (service_found[j + 1] == 0 &&
2155                             pamh->pam_conf_info[i][j] == NULL) {
2156                                 pamtab_t *pe = calloc(1, sizeof (pamtab_t));
2157 
2158                                 pam_trace(PAM_DEBUG_CONF, "read_pam_conf(%p):"
2159                                     "falling back to \"other\" for module "
2160                                     "type \"%s\" in service \"%s\"",
2161                                     (void *)pamh, pam_snames[j], service);
2162                                 if (pe == NULL) {
2163                                         error = PAM_SYSTEM_ERR;
2164                                         __pam_log(LOG_AUTH | LOG_ERR,
2165                                             "calloc: out of memory");
2166                                         goto out;
2167                                 }
2168                                 pe->pam_service = strdup(service);
2169                                 pe->pam_type = j;
2170                                 pe->pam_flag = PAM_INCLUDE;
2171                                 if (asprintf(&pe->module_path, "%s%s",
2172                                     PAM_CONFIG_DIR, "other") < 0) {
2173                                         free(pe);
2174                                         error = PAM_SYSTEM_ERR;
2175                                         __pam_log(LOG_AUTH | LOG_ERR,
2176                                             "asprintf: out of memory");
2177                                         goto out;
2178                                 }
2179                                 pamh->pam_conf_info[i][j] = pe;
2180                         }
2181                 }
2182         }
2183 
2184 out:
2185         (void) close_pam_conf(pam_fh);
2186         if (error != PAM_SUCCESS)
2187                 free_pam_conf_info(pamh);
2188         return (error);
2189 }
2190 
2191 /*
2192  * get_pam_conf_entry - get a pam.conf entry
2193  */
2194 
2195 static int
2196 get_pam_conf_entry(struct pam_fh *pam_fh, pam_handle_t *pamh, pamtab_t **pam,
2197     char *service, int shardfile)
2198 {
2199         char            *cp, *arg;
2200         int             argc;
2201         char            *tmp, *tmp_free;
2202         int             i;
2203         char            *current_line = NULL;
2204         int             error = PAM_SYSTEM_ERR; /* preset to error */
2205         int             err;
2206 
2207         /* get the next line from pam.conf */
2208         if ((cp = nextline(pam_fh, pamh, &err)) == NULL) {
2209                 /* no more lines in pam.conf ==> return */
2210                 error = PAM_SUCCESS;
2211                 *pam = NULL;
2212                 goto out;
2213         }
2214 
2215         if ((*pam = calloc(1, sizeof (pamtab_t))) == NULL) {
2216                 __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2217                 goto out;
2218         }
2219 
2220         /* copy full line for error reporting */
2221         if ((current_line = strdup(cp)) == NULL) {
2222                 __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2223                 goto out;
2224         }
2225 
2226         pam_trace(PAM_DEBUG_CONF,
2227             "pam.conf[%s] entry:\t%s", pam_trace_cname(pamh), current_line);
2228 
2229         if (shardfile) {
2230                 /*
2231                  * If this is an /etc/pam.d shard file, then the service name
2232                  * comes from the file name of the shard.
2233                  */
2234                 if (((*pam)->pam_service = strdup(service)) == 0) {
2235                         __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2236                         goto out;
2237                 }
2238         } else {
2239                 /* get service name (e.g. login, su, passwd) */
2240                 if ((arg = read_next_token(&cp)) == 0) {
2241                         __pam_log(LOG_AUTH | LOG_CRIT,
2242                             "illegal pam.conf[%s] entry: %s: missing SERVICE "
2243                             "NAME", pam_trace_cname(pamh), current_line);
2244                         goto out;
2245                 }
2246                 if (((*pam)->pam_service = strdup(arg)) == 0) {
2247                         __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2248                         goto out;
2249                 }
2250         }
2251 
2252         /* get module type (e.g. authentication, acct mgmt) */
2253         if ((arg = read_next_token(&cp)) == 0) {
2254                 __pam_log(LOG_AUTH | LOG_CRIT,
2255                     "illegal pam.conf[%s] entry: %s: missing MODULE TYPE",
2256                     pam_trace_cname(pamh), current_line);
2257                 (*pam)->pam_type = -1;       /* 0 is a valid value */
2258                 goto getflag;
2259         }
2260         if (strcasecmp(arg, PAM_AUTH_NAME) == 0) {
2261                 (*pam)->pam_type = PAM_AUTH_MODULE;
2262         } else if (strcasecmp(arg, PAM_ACCOUNT_NAME) == 0) {
2263                 (*pam)->pam_type = PAM_ACCOUNT_MODULE;
2264         } else if (strcasecmp(arg, PAM_SESSION_NAME) == 0) {
2265                 (*pam)->pam_type = PAM_SESSION_MODULE;
2266         } else if (strcasecmp(arg, PAM_PASSWORD_NAME) == 0) {
2267                 (*pam)->pam_type = PAM_PASSWORD_MODULE;
2268         } else {
2269                 /* error */
2270                 __pam_log(LOG_AUTH | LOG_CRIT,
2271                     "illegal pam.conf[%s] entry: %s: invalid module "
2272                     "type: %s", pam_trace_cname(pamh), current_line, arg);
2273                 (*pam)->pam_type = -1;       /* 0 is a valid value */
2274         }
2275 
2276 getflag:
2277         /* get pam flag (e.g., requisite, required, sufficient, optional) */
2278         if ((arg = read_next_token(&cp)) == 0) {
2279                 __pam_log(LOG_AUTH | LOG_CRIT,
2280                     "illegal pam.conf[%s] entry: %s: missing CONTROL FLAG",
2281                     pam_trace_cname(pamh), current_line);
2282                 goto getpath;
2283         }
2284         if (strcasecmp(arg, PAM_BINDING_NAME) == 0) {
2285                 (*pam)->pam_flag = PAM_BINDING;
2286         } else if (strcasecmp(arg, PAM_INCLUDE_NAME) == 0) {
2287                 (*pam)->pam_flag = PAM_INCLUDE;
2288         } else if (strcasecmp(arg, PAM_OPTIONAL_NAME) == 0) {
2289                 (*pam)->pam_flag = PAM_OPTIONAL;
2290         } else if (strcasecmp(arg, PAM_REQUIRED_NAME) == 0) {
2291                 (*pam)->pam_flag = PAM_REQUIRED;
2292         } else if (strcasecmp(arg, PAM_REQUISITE_NAME) == 0) {
2293                 (*pam)->pam_flag = PAM_REQUISITE;
2294         } else if (strcasecmp(arg, PAM_SUFFICIENT_NAME) == 0) {
2295                 (*pam)->pam_flag = PAM_SUFFICIENT;
2296         } else {
2297                 /* error */
2298                 __pam_log(LOG_AUTH | LOG_CRIT,
2299                     "illegal pam.conf[%s] entry: %s",
2300                     pam_trace_cname(pamh), current_line);
2301                 __pam_log(LOG_AUTH | LOG_CRIT,
2302                     "\tinvalid control flag: %s", arg);
2303         }
2304 
2305 getpath:
2306         /* get module path (e.g. /usr/lib/security/pam_unix_auth.so.1) */
2307         if ((arg = read_next_token(&cp)) == 0) {
2308                 __pam_log(LOG_AUTH | LOG_CRIT,
2309                     "illegal pam.conf[%s] entry: %s: missing MODULE PATH",
2310                     pam_trace_cname(pamh), current_line);
2311                 error = PAM_SUCCESS;    /* success */
2312                 goto out;
2313         }
2314         if (arg[0] != '/') {
2315                 int ret;
2316                 /*
2317                  * If module path does not start with "/", then
2318                  * prepend PAM_LIB_DIR (/usr/lib/security/).
2319                  */
2320                 if ((*pam)->pam_flag & PAM_INCLUDE) {
2321                         /*
2322                          * If this is an /etc/pam.d shard, we want to get
2323                          * included files from /etc/pam.d rather than
2324                          * /usr/lib/security.
2325                          */
2326                         ret = asprintf(&(*pam)->module_path, "%s%s",
2327                             (shardfile ? PAM_CONFIG_DIR : PAM_LIB_DIR), arg);
2328                 } else {
2329                         ret = asprintf(&(*pam)->module_path, "%s%s%s",
2330                             PAM_LIB_DIR, PAM_ISA_DIR, arg);
2331                 }
2332                 if (ret < 0) {
2333                         __pam_log(LOG_AUTH | LOG_ERR,
2334                             "asprintf: out of memory");
2335                         goto out;
2336                 }
2337         } else {
2338                 /* Full path provided for module */
2339                 char *isa;
2340 
2341                 /* Check for Instruction Set Architecture indicator */
2342                 if ((isa = strstr(arg, PAM_ISA)) != NULL) {
2343                         size_t len;
2344                         len = strlen(arg) - (sizeof (PAM_ISA)-1) +
2345                             sizeof (PAM_ISA_DIR);
2346 
2347                         /* substitute the architecture dependent path */
2348                         if (((*pam)->module_path = malloc(len)) == NULL) {
2349                                 __pam_log(LOG_AUTH | LOG_ERR,
2350                                     "strdup: out of memory");
2351                                 goto out;
2352                         }
2353                         *isa = '\000';
2354                         isa += strlen(PAM_ISA);
2355                         (void) snprintf((*pam)->module_path, len, "%s%s%s",
2356                             arg, PAM_ISA_DIR, isa);
2357                 } else if (((*pam)->module_path = strdup(arg)) == 0) {
2358                         __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2359                         goto out;
2360                 }
2361         }
2362 
2363         /* count the number of module-specific options first */
2364         argc = 0;
2365         if ((tmp = strdup(cp)) == NULL) {
2366                 __pam_log(LOG_AUTH | LOG_ERR, "strdup: out of memory");
2367                 goto out;
2368         }
2369         tmp_free = tmp;
2370         for (arg = read_next_token(&tmp); arg; arg = read_next_token(&tmp))
2371                 argc++;
2372         free(tmp_free);
2373 
2374         /* allocate array for the module-specific options */
2375         if (argc > 0) {
2376                 if (((*pam)->module_argv =
2377                     calloc(argc+1, sizeof (char *))) == 0) {
2378                         __pam_log(LOG_AUTH | LOG_ERR, "calloc: out of memory");
2379                         goto out;
2380                 }
2381                 i = 0;
2382                 for (arg = read_next_token(&cp); arg;
2383                     arg = read_next_token(&cp)) {
2384                         (*pam)->module_argv[i] = strdup(arg);
2385                         if ((*pam)->module_argv[i] == NULL) {
2386                                 __pam_log(LOG_AUTH | LOG_ERR, "strdup failed");
2387                                 goto out;
2388                         }
2389                         i++;
2390                 }
2391                 (*pam)->module_argv[argc] = NULL;
2392         }
2393         (*pam)->module_argc = argc;
2394 
2395         error = PAM_SUCCESS;    /* success */
2396         (*pam)->pam_err = err;       /* was the line truncated */
2397 
2398 out:
2399         if (current_line)
2400                 free(current_line);
2401         if (error != PAM_SUCCESS) {
2402                 /* on error free this */
2403                 if (*pam)
2404                         free_pamconf(*pam);
2405         }
2406         return (error);
2407 }
2408 
2409 
2410 /*
2411  * read_next_token - skip tab and space characters and return the next token
2412  */
2413 
2414 static char *
2415 read_next_token(char **cpp)
2416 {
2417         register char *cp = *cpp;
2418         char *start;
2419 
2420         if (cp == (char *)0) {
2421                 *cpp = (char *)0;
2422                 return ((char *)0);
2423         }
2424         while (*cp == ' ' || *cp == '\t')
2425                 cp++;
2426         if (*cp == '\0') {
2427                 *cpp = (char *)0;
2428                 return ((char *)0);
2429         }
2430         start = cp;
2431         while (*cp && *cp != ' ' && *cp != '\t')
2432                 cp++;
2433         if (*cp != '\0')
2434                 *cp++ = '\0';
2435         *cpp = cp;
2436         return (start);
2437 }
2438 
2439 static char *
2440 pam_conf_strnchr(char *sp, int c, intptr_t count)
2441 {
2442         while (count) {
2443                 if (*sp == (char)c)
2444                         return ((char *)sp);
2445                 else {
2446                         sp++;
2447                         count--;
2448                 }
2449         };
2450         return (NULL);
2451 }
2452 
2453 /*
2454  * nextline - skip all blank lines and comments
2455  */
2456 
2457 static char *
2458 nextline(struct pam_fh *pam_fh, pam_handle_t *pamh, int *err)
2459 {
2460         char    *ll;
2461         int     find_a_line = 0;
2462         char    *data = pam_fh->data;
2463         char    *bufferp = pam_fh->bufferp;
2464         char    *bufferendp = &data[pam_fh->bufsize];
2465         size_t  input_len;
2466 
2467         /*
2468          * Skip the blank line, comment line
2469          */
2470         while (!find_a_line) {
2471                 /* if we are at the end of the buffer, there is no next line */
2472                 if (bufferp == bufferendp)
2473                         return (NULL);
2474 
2475                 /* skip blank line */
2476                 while (*bufferp == '\n') {
2477                         /*
2478                          * If we are at the end of the buffer, there is
2479                          * no next line.
2480                          */
2481                         if (++bufferp == bufferendp) {
2482                                 return (NULL);
2483                         }
2484                         /* else we check *bufferp again */
2485                 }
2486 
2487                 /* skip comment line */
2488                 while (*bufferp == '#') {
2489                         if ((ll = pam_conf_strnchr(bufferp, '\n',
2490                             bufferendp - bufferp)) != NULL) {
2491                                 bufferp = ll;
2492                         } else {
2493                                 /*
2494                                  * this comment line the last line.
2495                                  * no next line
2496                                  */
2497                                 return (NULL);
2498                         }
2499 
2500                         /*
2501                          * If we are at the end of the buffer, there is
2502                          * no next line.
2503                          */
2504                         if (bufferp == bufferendp) {
2505                                 return (NULL);
2506                         }
2507                 }
2508 
2509                 if ((*bufferp != '\n') && (*bufferp != '#')) {
2510                         find_a_line = 1;
2511                 }
2512         }
2513 
2514         *err = PAM_SUCCESS;
2515         /* now we find one line */
2516         if ((ll = pam_conf_strnchr(bufferp, '\n', bufferendp - bufferp))
2517             != NULL) {
2518                 if ((input_len = ll - bufferp) >= sizeof (pam_fh->line)) {
2519                         __pam_log(LOG_AUTH | LOG_ERR,
2520                             "nextline[%d:%s]: pam.conf line too long %.256s",
2521                             pamh->include_depth, pam_trace_cname(pamh),
2522                             bufferp);
2523                         input_len = sizeof (pam_fh->line) - 1;
2524                         *err = PAM_SERVICE_ERR;
2525                 }
2526                 (void) strncpy(pam_fh->line, bufferp, input_len);
2527                 pam_fh->line[input_len] = '\0';
2528                 pam_fh->bufferp = ll++;
2529         } else {
2530                 ll = bufferendp;
2531                 if ((input_len = ll - bufferp) >= sizeof (pam_fh->line)) {
2532                         __pam_log(LOG_AUTH | LOG_ERR,
2533                             "nextline[%d:%s]: pam.conf line too long %.256s",
2534                             pamh->include_depth, pam_trace_cname(pamh),
2535                             bufferp);
2536                         input_len = sizeof (pam_fh->line) - 1;
2537                         *err = PAM_SERVICE_ERR;
2538                 }
2539                 (void) strncpy(pam_fh->line, bufferp, input_len);
2540                 pam_fh->line[input_len] = '\0';
2541                 pam_fh->bufferp = ll;
2542         }
2543 
2544         return (pam_fh->line);
2545 }
2546 
2547 /*
2548  * verify_pam_conf - verify that the pam_conf entry is filled in.
2549  *
2550  *      True = Error if there is no service.
2551  *      True = Error if there is a service and it matches the requested service
2552  *              but, the type, flag, line overflow, or path is in error.
2553  */
2554 
2555 static int
2556 verify_pam_conf(pamtab_t *pam, char *service)
2557 {
2558         return ((pam->pam_service == (char *)NULL) ||
2559             ((strcasecmp(pam->pam_service, service) == 0) &&
2560             ((pam->pam_type == -1) ||
2561             (pam->pam_flag == 0) ||
2562             (pam->pam_err != PAM_SUCCESS) ||
2563             (pam->module_path == (char *)NULL))));
2564 }
2565 
2566 /*
2567  * Routines to free allocated storage
2568  */
2569 
2570 /*
2571  * clean_up -  free allocated storage in the pam handle
2572  */
2573 
2574 static void
2575 clean_up(pam_handle_t *pamh)
2576 {
2577         int i;
2578         pam_repository_t *auth_rep;
2579 
2580         if (pamh) {
2581                 while (pamh->include_depth >= 0) {
2582                         free_pam_conf_info(pamh);
2583                         pamh->include_depth--;
2584                 }
2585 
2586                 /* Cleanup PAM_REPOSITORY structure */
2587                 auth_rep = pamh->ps_item[PAM_REPOSITORY].pi_addr;
2588                 if (auth_rep != NULL) {
2589                         if (auth_rep->type != NULL)
2590                                 free(auth_rep->type);
2591                         if (auth_rep->scope != NULL)
2592                                 free(auth_rep->scope);
2593                 }
2594 
2595                 for (i = 0; i < PAM_MAX_ITEMS; i++) {
2596                         if (pamh->ps_item[i].pi_addr != NULL) {
2597                                 if (i == PAM_AUTHTOK || i == PAM_OLDAUTHTOK) {
2598                                         (void) memset(pamh->ps_item[i].pi_addr,
2599                                             0, pamh->ps_item[i].pi_size);
2600                                 }
2601                                 free(pamh->ps_item[i].pi_addr);
2602                         }
2603                 }
2604                 free(pamh);
2605         }
2606 }
2607 
2608 /*
2609  * free_pamconf - free memory used to store pam.conf entry
2610  */
2611 
2612 static void
2613 free_pamconf(pamtab_t *cp)
2614 {
2615         int i;
2616 
2617         if (cp) {
2618                 if (cp->pam_service)
2619                         free(cp->pam_service);
2620                 if (cp->module_path)
2621                         free(cp->module_path);
2622                 for (i = 0; i < cp->module_argc; i++) {
2623                         if (cp->module_argv[i])
2624                                 free(cp->module_argv[i]);
2625                 }
2626                 if (cp->module_argc > 0)
2627                         free(cp->module_argv);
2628                 if (cp->function_ptr)
2629                         free(cp->function_ptr);
2630 
2631                 free(cp);
2632         }
2633 }
2634 
2635 /*
2636  * free_pam_conf_info - free memory used to store all pam.conf info
2637  *                      under the pam handle
2638  */
2639 
2640 static void
2641 free_pam_conf_info(pam_handle_t *pamh)
2642 {
2643         pamtab_t *pamentp;
2644         pamtab_t *pament_trail;
2645         int i = pamh->include_depth;
2646         int j;
2647 
2648         for (j = 0; j < PAM_NUM_MODULE_TYPES; j++) {
2649                 pamentp = pamh->pam_conf_info[i][j];
2650                 pamh->pam_conf_info[i][j] = NULL;
2651                 pament_trail = pamentp;
2652                 while (pamentp) {
2653                         pamentp = pamentp->next;
2654                         free_pamconf(pament_trail);
2655                         pament_trail = pamentp;
2656                 }
2657         }
2658         if (pamh->pam_conf_name[i] != NULL) {
2659                 free(pamh->pam_conf_name[i]);
2660                 pamh->pam_conf_name[i] = NULL;
2661         }
2662 }
2663 
2664 static void
2665 free_env(env_list *pam_env)
2666 {
2667         if (pam_env) {
2668                 if (pam_env->name)
2669                         free(pam_env->name);
2670                 if (pam_env->value)
2671                         free(pam_env->value);
2672                 free(pam_env);
2673         }
2674 }
2675 
2676 /*
2677  *      Internal convenience functions for Solaris PAM service modules.
2678  */
2679 
2680 #include <libintl.h>
2681 #include <nl_types.h>
2682 #include <synch.h>
2683 #include <locale.h>
2684 #include <thread.h>
2685 
2686 typedef struct pam_msg_data {
2687         nl_catd fd;
2688 } pam_msg_data_t;
2689 
2690 /*
2691  * free_resp():
2692  *      free storage for responses used in the call back "pam_conv" functions
2693  */
2694 
2695 void
2696 free_resp(int num_msg, struct pam_response *resp)
2697 {
2698         int                     i;
2699         struct pam_response     *r;
2700 
2701         if (resp) {
2702                 r = resp;
2703                 for (i = 0; i < num_msg; i++, r++) {
2704                         if (r->resp) {
2705                                 /* clear before freeing -- may be a password */
2706                                 bzero(r->resp, strlen(r->resp));
2707                                 free(r->resp);
2708                                 r->resp = NULL;
2709                         }
2710                 }
2711                 free(resp);
2712         }
2713 }
2714 
2715 static int
2716 do_conv(pam_handle_t *pamh, int msg_style, int num_msg,
2717     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *conv_apdp,
2718     struct pam_response *ret_respp[])
2719 {
2720         struct pam_message      *msg;
2721         struct pam_message      *m;
2722         int                     i;
2723         int                     k;
2724         int                     retcode;
2725         struct pam_conv         *pam_convp;
2726 
2727         if ((retcode = pam_get_item(pamh, PAM_CONV,
2728             (void **)&pam_convp)) != PAM_SUCCESS) {
2729                 return (retcode);
2730         }
2731 
2732         /*
2733          * When pam_set_item() is called to set PAM_CONV and the
2734          * item is NULL, memset(pip->pi_addr, 0, size) is called.
2735          * So at this point, we should check whether pam_convp->conv
2736          * is NULL or not.
2737          */
2738         if ((pam_convp == NULL) || (pam_convp->conv == NULL))
2739                 return (PAM_SYSTEM_ERR);
2740 
2741         i = 0;
2742         k = num_msg;
2743 
2744         msg = calloc(num_msg, sizeof (struct pam_message));
2745         if (msg == NULL) {
2746                 return (PAM_BUF_ERR);
2747         }
2748         m = msg;
2749 
2750         while (k--) {
2751                 /*
2752                  * fill out the message structure to display prompt message
2753                  */
2754                 m->msg_style = msg_style;
2755                 m->msg = messages[i];
2756                 pam_trace(PAM_DEBUG_CONV,
2757                     "pam_conv_msg(%p:%d[%d]=%s)",
2758                     (void *)pamh, msg_style, i, messages[i]);
2759                 m++;
2760                 i++;
2761         }
2762 
2763         /*
2764          * The UNIX pam modules always calls __pam_get_authtok() and
2765          * __pam_display_msg() with a NULL pointer as the conv_apdp.
2766          * In case the conv_apdp is NULL and the pam_convp->appdata_ptr
2767          * is not NULL, we should pass the pam_convp->appdata_ptr
2768          * to the conversation function.
2769          */
2770         if (conv_apdp == NULL && pam_convp->appdata_ptr != NULL)
2771                 conv_apdp = pam_convp->appdata_ptr;
2772 
2773         /*
2774          * Call conv function to display the prompt.
2775          */
2776         retcode = (pam_convp->conv)(num_msg, &msg, ret_respp, conv_apdp);
2777         pam_trace(PAM_DEBUG_CONV,
2778             "pam_conv_resp(%p pam_conv = %s) ret_respp = %p",
2779             (void *)pamh, pam_strerror(pamh, retcode), (void *)ret_respp);
2780         if (*ret_respp == NULL) {
2781                 pam_trace(PAM_DEBUG_CONV,
2782                     "pam_conv_resp(%p No response requested)", (void *)pamh);
2783         } else if ((pam_debug & (PAM_DEBUG_CONV | PAM_DEBUG_AUTHTOK)) != 0) {
2784                 struct pam_response *r = *ret_respp;
2785 
2786                 for (i = 0; i < num_msg; i++, r++) {
2787                         if (r->resp == NULL) {
2788                                 pam_trace(PAM_DEBUG_CONV,
2789                                     "pam_conv_resp(%p:"
2790                                     "[%d] NULL response string)",
2791                                     (void *)pamh, i);
2792                         } else {
2793                                 if (msg_style == PAM_PROMPT_ECHO_OFF) {
2794 #ifdef  DEBUG
2795                                         pam_trace(PAM_DEBUG_AUTHTOK,
2796                                             "pam_conv_resp(%p:[%d]=%s, "
2797                                             "code=%d)",
2798                                             (void *)pamh, i, r->resp,
2799                                             r->resp_retcode);
2800 #endif  /* DEBUG */
2801                                         pam_trace(PAM_DEBUG_CONV,
2802                                             "pam_conv_resp(%p:[%d] len=%lu, "
2803                                             "code=%d)",
2804                                             (void *)pamh, i,
2805                                             (ulong_t)strlen(r->resp),
2806                                             r->resp_retcode);
2807                                 } else {
2808                                         pam_trace(PAM_DEBUG_CONV,
2809                                             "pam_conv_resp(%p:[%d]=%s, "
2810                                             "code=%d)",
2811                                             (void *)pamh, i, r->resp,
2812                                             r->resp_retcode);
2813                                 }
2814                         }
2815                 }
2816         }
2817 
2818         if (msg)
2819                 free(msg);
2820         return (retcode);
2821 }
2822 
2823 /*
2824  * __pam_display_msg():
2825  *      display message by calling the call back functions
2826  *      provided by the application through "pam_conv" structure
2827  */
2828 
2829 int
2830 __pam_display_msg(pam_handle_t *pamh, int msg_style, int num_msg,
2831     char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE], void *conv_apdp)
2832 {
2833         struct pam_response     *ret_respp = NULL;
2834         int ret;
2835 
2836         ret = do_conv(pamh, msg_style, num_msg, messages,
2837             conv_apdp, &ret_respp);
2838 
2839         if (ret_respp != NULL)
2840                 free_resp(num_msg, ret_respp);
2841 
2842         return (ret);
2843 }
2844 
2845 /*
2846  * __pam_get_authtok()
2847  *      retrieves a password of at most PASS_MAX length from the pam
2848  *      handle (pam_get_item) or from the input stream (do_conv).
2849  *
2850  * This function allocates memory for the new authtok.
2851  * Applications calling this function are responsible for
2852  * freeing this memory.
2853  *
2854  * If "source" is
2855  *      PAM_HANDLE
2856  * and "type" is:
2857  *      PAM_AUTHTOK - password is taken from pam handle (PAM_AUTHTOK)
2858  *      PAM_OLDAUTHTOK - password is taken from pam handle (PAM_OLDAUTHTOK)
2859  *
2860  * If "source" is
2861  *      PAM_PROMPT
2862  * and "type" is:
2863  *      0:              Prompt for new passwd, do not even attempt
2864  *                      to store it in the pam handle.
2865  *      PAM_AUTHTOK:    Prompt for new passwd, store in pam handle as
2866  *                      PAM_AUTHTOK item if this value is not already set.
2867  *      PAM_OLDAUTHTOK: Prompt for new passwd, store in pam handle as
2868  *                      PAM_OLDAUTHTOK item if this value is not
2869  *                      already set.
2870  */
2871 int
2872 __pam_get_authtok(pam_handle_t *pamh, int source, int type, char *prompt,
2873     char **authtok)
2874 {
2875         int error = PAM_SYSTEM_ERR;
2876         char *new_password = NULL;
2877         struct pam_response *ret_resp = NULL;
2878         char messages[PAM_MAX_NUM_MSG][PAM_MAX_MSG_SIZE];
2879 
2880         if ((*authtok = calloc(PASS_MAX+1, sizeof (char))) == NULL)
2881                 return (PAM_BUF_ERR);
2882 
2883         if (prompt == NULL)
2884                 prompt = dgettext(TEXT_DOMAIN, "password: ");
2885 
2886         switch (source) {
2887         case PAM_HANDLE:
2888 
2889                 /* get password from pam handle item list */
2890 
2891                 switch (type) {
2892                 case PAM_AUTHTOK:
2893                 case PAM_OLDAUTHTOK:
2894 
2895                         if ((error = pam_get_item(pamh, type,
2896                             (void **)&new_password)) != PAM_SUCCESS)
2897                                 goto err_ret;
2898 
2899                         if (new_password == NULL || new_password[0] == '\0') {
2900                                 free(*authtok);
2901                                 *authtok = NULL;
2902                         } else {
2903                                 (void) strlcpy(*authtok, new_password,
2904                                     PASS_MAX+1);
2905                         }
2906                         break;
2907                 default:
2908                         __pam_log(LOG_AUTH | LOG_ERR,
2909                             "__pam_get_authtok() invalid type: %d", type);
2910                         error = PAM_SYMBOL_ERR;
2911                         goto err_ret;
2912                 }
2913                 break;
2914         case PAM_PROMPT:
2915 
2916                 /*
2917                  * Prompt for new password and save in pam handle item list
2918                  * if the that item is not already set.
2919                  */
2920 
2921                 (void) strncpy(messages[0], prompt, sizeof (messages[0]));
2922                 if ((error = do_conv(pamh, PAM_PROMPT_ECHO_OFF, 1, messages,
2923                     NULL, &ret_resp)) != PAM_SUCCESS)
2924                         goto err_ret;
2925 
2926                 if (ret_resp->resp == NULL) {
2927                         /* getpass didn't return anything */
2928                         error = PAM_SYSTEM_ERR;
2929                         goto err_ret;
2930                 }
2931 
2932                 /* save the new password if this item was NULL */
2933                 if (type) {
2934                         if ((error = pam_get_item(pamh, type,
2935                             (void **)&new_password)) != PAM_SUCCESS) {
2936                                 free_resp(1, ret_resp);
2937                                 goto err_ret;
2938                         }
2939                         if (new_password == NULL)
2940                                 (void) pam_set_item(pamh, type, ret_resp->resp);
2941                 }
2942 
2943                 (void) strlcpy(*authtok, ret_resp->resp, PASS_MAX+1);
2944                 free_resp(1, ret_resp);
2945                 break;
2946         default:
2947                 __pam_log(LOG_AUTH | LOG_ERR,
2948                     "__pam_get_authtok() invalid source: %d", source);
2949                 error = PAM_SYMBOL_ERR;
2950                 goto err_ret;
2951         }
2952 
2953         return (PAM_SUCCESS);
2954 
2955 err_ret:
2956         bzero(*authtok, PASS_MAX+1);
2957         free(*authtok);
2958         *authtok = NULL;
2959         return (error);
2960 }