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