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