1 /*
   2  * Copyright (c) 2000 Damien Miller.  All rights reserved.
   3  *
   4  * Redistribution and use in source and binary forms, with or without
   5  * modification, are permitted provided that the following conditions
   6  * are met:
   7  * 1. Redistributions of source code must retain the above copyright
   8  *    notice, this list of conditions and the following disclaimer.
   9  * 2. Redistributions in binary form must reproduce the above copyright
  10  *    notice, this list of conditions and the following disclaimer in the
  11  *    documentation and/or other materials provided with the distribution.
  12  *
  13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  18  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  19  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  20  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  22  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  23  */
  24 /*
  25  * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
  26  */
  27 
  28 #include "includes.h"
  29 
  30 #ifdef USE_PAM
  31 #include "xmalloc.h"
  32 #include "log.h"
  33 #include "auth.h"
  34 #include "auth-options.h"
  35 #include "auth-pam.h"
  36 #include "buffer.h"
  37 #include "servconf.h"
  38 #include "canohost.h"
  39 #include "compat.h"
  40 #include "misc.h"
  41 #include "sshlogin.h"
  42 #include "ssh-gss.h"
  43 
  44 #include <security/pam_appl.h>
  45 
  46 extern char *__progname;
  47 
  48 extern u_int utmp_len;
  49 extern ServerOptions options;
  50 
  51 extern Authmethod method_kbdint;
  52 
  53 RCSID("$Id: auth-pam.c,v 1.54 2002/07/28 20:24:08 stevesk Exp $");
  54 
  55 #define NEW_AUTHTOK_MSG \
  56         "Warning: Your password has expired, please change it now."
  57 
  58 /* PAM conversation for non-interactive userauth methods */
  59 static int do_pam_conversation(int num_msg, const struct pam_message **msg,
  60         struct pam_response **resp, void *appdata_ptr);
  61 
  62 static void do_pam_cleanup_proc(void *context);
  63 
  64 static char *get_method_name(Authctxt *authctxt);
  65 
  66 /* PAM conversation for non-interactive userauth methods */
  67 static struct pam_conv conv = {
  68         (int (*)())do_pam_conversation,
  69         NULL
  70 };
  71 static char *__pam_msg = NULL;
  72 
  73 static
  74 char *
  75 get_method_name(Authctxt *authctxt)
  76 {
  77         if (!authctxt)
  78                 return "(unknown)";
  79 
  80         if (!compat20)
  81                 return (authctxt->v1_auth_name) ? authctxt->v1_auth_name :
  82                                                   "(sshv1-unknown)";
  83 
  84         if (!authctxt->method || !authctxt->method->name)
  85                         return "(sshv2-unknown)";
  86 
  87         return authctxt->method->name;
  88 }
  89 
  90 char *
  91 derive_pam_service_name(Authmethod *method)
  92 {
  93         char *svcname = xmalloc(BUFSIZ);
  94 
  95         /*
  96          * If PamServiceName is set we use that for everything, including
  97          * SSHv1
  98          */
  99         if (options.pam_service_name != NULL) {
 100                 (void) strlcpy(svcname, options.pam_service_name, BUFSIZ);
 101                 return (svcname);
 102         }
 103 
 104         if (compat20 && method) {
 105                 char *method_name = method->name;
 106 
 107                 if (!method_name)
 108                         fatal("Userauth method unknown while starting PAM");
 109 
 110                 /*
 111                  * For SSHv2 we use "sshd-<userauth name>
 112                  * The "sshd" prefix can be changed via the PAMServicePrefix
 113                  * sshd_config option.
 114                  */
 115                 if (strcmp(method_name, "none") == 0) {
 116                         snprintf(svcname, BUFSIZ, "%s-none",
 117                             options.pam_service_prefix);
 118                 }
 119                 if (strcmp(method_name, "password") == 0) {
 120                         snprintf(svcname, BUFSIZ, "%s-password",
 121                             options.pam_service_prefix);
 122                 }
 123                 if (strcmp(method_name, "keyboard-interactive") == 0) {
 124                         /* "keyboard-interactive" is too long, shorten it */
 125                         snprintf(svcname, BUFSIZ, "%s-kbdint",
 126                             options.pam_service_prefix);
 127                 }
 128                 if (strcmp(method_name, "publickey") == 0) {
 129                         /* "publickey" is too long, shorten it */
 130                         snprintf(svcname, BUFSIZ, "%s-pubkey",
 131                             options.pam_service_prefix);
 132                 }
 133                 if (strcmp(method_name, "hostbased") == 0) {
 134                         /* "hostbased" can't really be shortened... */
 135                         snprintf(svcname, BUFSIZ, "%s-hostbased",
 136                             options.pam_service_prefix);
 137                 }
 138                 if (strncmp(method_name, "gss", 3) == 0) {
 139                         /* "gss" is too short, elongate it */
 140                         snprintf(svcname, BUFSIZ, "%s-gssapi",
 141                             options.pam_service_prefix);
 142                 }
 143                 return svcname;
 144         } else {
 145                 /* SSHv1 doesn't get to be so cool */
 146                 snprintf(svcname, BUFSIZ, "%s-v1",
 147                     options.pam_service_prefix);
 148         }
 149         return svcname;
 150 }
 151 
 152 void
 153 new_start_pam(Authctxt *authctxt, struct pam_conv *conv)
 154 {
 155         int             retval;
 156         pam_handle_t    *pamh;
 157         const char      *rhost;
 158         char            *svc;
 159         char            *user = NULL;
 160         pam_stuff       *pam;
 161 
 162         if (authctxt == NULL)
 163                 fatal("Internal error during userauth");
 164 
 165         if (compat20 && authctxt->method == NULL)
 166                 fatal("Userauth method unknown while starting PAM");
 167 
 168         /* PAM service selected here */
 169         svc = derive_pam_service_name(authctxt->method);
 170         debug2("Starting PAM service %s for method %s", svc,
 171                 get_method_name(authctxt));
 172 
 173         if (authctxt->user != NULL)
 174                 user = authctxt->user;
 175 
 176         /* Cleanup previous PAM state */
 177         if (authctxt->pam != NULL) {
 178                 fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
 179                 do_pam_cleanup_proc(authctxt->pam);
 180         }
 181 
 182         pam = xmalloc(sizeof(pam_stuff));
 183         (void) memset(pam, 0, sizeof(pam_stuff));
 184 
 185         /*
 186          * pam->last_pam_retval has to be and is considered
 187          * along with pam->state.
 188          *
 189          * pam->state = 0; -> no PAM auth, account, etc, work
 190          * done yet.  (Set by memset() above.)
 191          *
 192          * pam->last_pam_retval = PAM_SUCCESS; -> meaningless at
 193          * this point.
 194          *
 195          * See finish_userauth_do_pam() below.
 196          */
 197         pam->authctxt = authctxt;
 198         pam->last_pam_retval = PAM_SUCCESS;
 199 
 200         authctxt->pam = pam;
 201 
 202         /* Free any previously stored text/error PAM prompts */
 203         if (__pam_msg) {
 204                 xfree(__pam_msg);
 205                 __pam_msg = NULL;
 206         }
 207 
 208         if ((retval = pam_start(svc, user, conv, &pamh)) != PAM_SUCCESS) {
 209                 fatal("PAM initialization failed during %s userauth",
 210                         get_method_name(authctxt));
 211         }
 212 
 213         free(svc);
 214 
 215         fatal_add_cleanup((void (*)(void *)) &do_pam_cleanup_proc,
 216                           (void *) authctxt->pam);
 217 
 218         rhost = get_remote_name_or_ip(utmp_len, options.verify_reverse_mapping);
 219         if ((retval = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) {
 220                 (void) pam_end(pamh, retval);
 221                 fatal("Could not set PAM_RHOST item during %s userauth",
 222                         get_method_name(authctxt));
 223         }
 224 
 225         if ((retval = pam_set_item(pamh, PAM_TTY, "sshd")) != PAM_SUCCESS) {
 226                 (void) pam_end(pamh, retval);
 227                 fatal("Could not set PAM_TTY item during %s userauth",
 228                         get_method_name(authctxt));
 229         }
 230 
 231         if (authctxt->cuser != NULL) 
 232                 if ((retval = pam_set_item(pamh, PAM_AUSER, authctxt->cuser)) != PAM_SUCCESS) {
 233                         (void) pam_end(pamh, retval);
 234                         fatal("Could not set PAM_AUSER item during %s userauth",
 235                                 get_method_name(authctxt));
 236                 }
 237 
 238         authctxt->pam->h = pamh;
 239 }
 240 
 241 /*
 242  * To be called from userauth methods, directly (as in keyboard-interactive) or
 243  * indirectly (from auth_pam_password() or from do_pam_non_initial_userauth().
 244  *
 245  * The caller is responsible for calling new_start_pam() first.
 246  *
 247  * PAM state is not cleaned up here on error.  This is left to subsequent calls
 248  * to new_start_pam() or to the cleanup function upon authentication error.
 249  */
 250 int
 251 finish_userauth_do_pam(Authctxt *authctxt)
 252 {
 253         int retval;
 254         char *user, *method;
 255 
 256         /* Various checks; fail gracefully */
 257         if (authctxt == NULL || authctxt->pam == NULL)
 258                 return PAM_SYSTEM_ERR;  /* shouldn't happen */
 259 
 260         if (compat20) {
 261                 if (authctxt->method == NULL || authctxt->method->name == NULL)
 262                         return PAM_SYSTEM_ERR;  /* shouldn't happen */
 263                 method = authctxt->method->name;
 264         } else if ((method = authctxt->v1_auth_name) == NULL)
 265                 return PAM_SYSTEM_ERR;  /* shouldn't happen */
 266 
 267         if (AUTHPAM_DONE(authctxt))
 268                 return PAM_SYSTEM_ERR;  /* shouldn't happen */
 269 
 270         if (!(authctxt->pam->state & PAM_S_DONE_ACCT_MGMT)) {
 271                 retval = pam_acct_mgmt(authctxt->pam->h, 0);
 272                 authctxt->pam->last_pam_retval = retval;
 273                 if (retval == PAM_NEW_AUTHTOK_REQD) {
 274                         userauth_force_kbdint();
 275                         return retval;
 276                 }
 277                 if (retval != PAM_SUCCESS)
 278                         return retval;
 279                 authctxt->pam->state |= PAM_S_DONE_ACCT_MGMT;
 280         }
 281 
 282         /*
 283          * Handle PAM_USER change, if any.
 284          *
 285          * We do this before pam_open_session() because we need the PAM_USER's
 286          * UID for:
 287          *
 288          * a) PermitRootLogin checking
 289          * b) to get at the lastlog entry before pam_open_session() updates it.
 290          */
 291         retval = pam_get_item(authctxt->pam->h, PAM_USER, (void **) &user);
 292         if (retval != PAM_SUCCESS) {
 293                 fatal("PAM failure: pam_get_item(PAM_USER) "
 294                       "returned %d: %.200s", retval,
 295                       PAM_STRERROR(authctxt->pam->h, retval));
 296         }
 297 
 298         if (user == NULL || *user == '\0') {
 299                 debug("PAM set NULL PAM_USER");
 300                 return PAM_PERM_DENIED;
 301         }
 302 
 303         if (strcmp(user, authctxt->user) != 0) {
 304                 log("PAM changed the SSH username");
 305                 pwfree(&authctxt->pw);
 306                 authctxt->pw = getpwnamallow(user);
 307                 authctxt->valid = (authctxt->pw != NULL);
 308                 xfree(authctxt->user);
 309                 authctxt->user = xstrdup(user);
 310         }
 311 
 312         if (!authctxt->valid) {
 313                 debug2("PAM set PAM_USER to unknown user");
 314                 /*
 315                  * Return success, userauth_finish() will catch
 316                  * this and send back a failure message.
 317                  */
 318                 return PAM_SUCCESS;
 319         }
 320 
 321         /* Check PermitRootLogin semantics */
 322         if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(method))
 323                 return PAM_PERM_DENIED;
 324 
 325         if (!(authctxt->pam->state & PAM_S_DONE_SETCRED)) {
 326                 retval = pam_setcred(authctxt->pam->h,
 327                                      PAM_ESTABLISH_CRED);
 328                 authctxt->pam->last_pam_retval = retval;
 329                 if (retval != PAM_SUCCESS)
 330                         return retval;
 331                 authctxt->pam->state |= PAM_S_DONE_SETCRED;
 332 
 333 #ifdef GSSAPI
 334                 /*
 335                  * Store GSS-API delegated creds after pam_setcred(), which may
 336                  * have set the current credential store.
 337                  */
 338                 ssh_gssapi_storecreds(NULL, authctxt);
 339 #endif /* GSSAPI */
 340         }
 341 
 342         /*
 343          * On Solaris pam_unix_session.so updates the lastlog, but does
 344          * not converse a PAM_TEXT_INFO message about it.  So we need to
 345          * fetch the lastlog entry here and save it for use later.
 346          */
 347         authctxt->last_login_time =
 348                 get_last_login_time(authctxt->pw->pw_uid,
 349                         authctxt->pw->pw_name,
 350                         authctxt->last_login_host,
 351                         sizeof(authctxt->last_login_host));
 352 
 353         if (!(authctxt->pam->state & PAM_S_DONE_OPEN_SESSION)) {
 354                 retval = pam_open_session(authctxt->pam->h, 0);
 355                 authctxt->pam->last_pam_retval = retval;
 356                 if (retval != PAM_SUCCESS)
 357                         return retval;
 358                 authctxt->pam->state |= PAM_S_DONE_OPEN_SESSION;
 359         }
 360 
 361         /*
 362          * All PAM work done successfully.
 363          *
 364          * PAM handle stays around so we can call pam_close_session() on
 365          * it later.
 366          */
 367         return PAM_SUCCESS;
 368 }
 369 
 370 /*
 371  * PAM conversation function for non-interactive userauth methods that
 372  * really cannot do any prompting.  Password userauth and CHANGEREQ can
 373  * always set the PAM_AUTHTOK and PAM_OLDAUTHTOK items to avoid
 374  * conversation (and if they do and nonetheless some module tries to
 375  * converse, then password userauth / CHANGEREQ MUST fail).
 376  *
 377  * Except, PAM_TEXT_INFO and PAM_ERROR_MSG prompts can be squirelled
 378  * away and shown to the user later.
 379  *
 380  * Keyboard-interactive userauth has its own much more interesting
 381  * conversation function.
 382  *
 383  */
 384 static int
 385 do_pam_conversation(int num_msg, const struct pam_message **msg,
 386         struct pam_response **resp, void *appdata_ptr)
 387 {
 388         struct pam_response *reply;
 389         int count;
 390 
 391         /* PAM will free this later */
 392         reply = xmalloc(num_msg * sizeof(*reply));
 393 
 394         (void) memset(reply, 0, num_msg * sizeof(*reply));
 395 
 396         for (count = 0; count < num_msg; count++) {
 397                 /*
 398                  * We can't use stdio yet, queue messages for 
 399                  * printing later
 400                  */
 401                 switch(PAM_MSG_MEMBER(msg, count, msg_style)) {
 402                 case PAM_PROMPT_ECHO_ON:
 403                         xfree(reply);
 404                         return PAM_CONV_ERR;
 405                 case PAM_PROMPT_ECHO_OFF:
 406                         xfree(reply);
 407                         return PAM_CONV_ERR;
 408                         break;
 409                 case PAM_ERROR_MSG:
 410                 case PAM_TEXT_INFO:
 411                         if (PAM_MSG_MEMBER(msg, count, msg) != NULL) {
 412                                 message_cat(&__pam_msg, 
 413                                     PAM_MSG_MEMBER(msg, count, msg));
 414                         }
 415                         reply[count].resp = xstrdup("");
 416                         reply[count].resp_retcode = PAM_SUCCESS;
 417                         break;
 418                 default:
 419                         xfree(reply);
 420                         return PAM_CONV_ERR;
 421                 }
 422         }
 423 
 424         *resp = reply;
 425 
 426         return PAM_SUCCESS;
 427 }
 428 
 429 /* Called at exit to cleanly shutdown PAM */
 430 static void
 431 do_pam_cleanup_proc(void *context)
 432 {
 433         int pam_retval;
 434         pam_stuff *pam = (pam_stuff *) context;
 435 
 436         if (pam == NULL)
 437                 return;
 438 
 439         if (pam->authctxt != NULL && pam->authctxt->pam == pam) {
 440                 pam->authctxt->pam_retval = pam->last_pam_retval;
 441                 pam->authctxt->pam = NULL;
 442                 pam->authctxt = NULL;
 443         }
 444 
 445         if (pam->h == NULL)
 446                 return;
 447 
 448         /*
 449          * We're in fatal_cleanup() or not in userauth or without a
 450          * channel -- can't converse now, too bad.
 451          */
 452         pam_retval = pam_set_item(pam->h, PAM_CONV, NULL);
 453         if (pam_retval != PAM_SUCCESS) {
 454                 log("Cannot remove PAM conv, close session or delete creds[%d]: %.200s",
 455                         pam_retval, PAM_STRERROR(pam->h, pam_retval));
 456                 goto cleanup;
 457         }
 458 
 459         if (pam->state & PAM_S_DONE_OPEN_SESSION) {
 460                 pam_retval = pam_close_session(pam->h, 0);
 461                 if (pam_retval != PAM_SUCCESS)
 462                         log("Cannot close PAM session[%d]: %.200s",
 463                             pam_retval, PAM_STRERROR(pam->h, pam_retval));
 464         }
 465 
 466         if (pam->state & PAM_S_DONE_SETCRED) {
 467                 pam_retval = pam_setcred(pam->h, PAM_DELETE_CRED);
 468                 if (pam_retval != PAM_SUCCESS)
 469                         debug("Cannot delete credentials[%d]: %.200s", 
 470                             pam_retval, PAM_STRERROR(pam->h, pam_retval));
 471         }
 472 
 473 cleanup:
 474 
 475         /* Use the previous PAM result, if not PAM_SUCCESS for pam_end() */
 476         if (pam->last_pam_retval != PAM_SUCCESS)
 477                 pam_retval = pam_end(pam->h, pam->last_pam_retval);
 478         else if (pam_retval != PAM_SUCCESS)
 479                 pam_retval = pam_end(pam->h, pam_retval);
 480         else
 481                 pam_retval = pam_end(pam->h, PAM_ABORT);
 482 
 483         if (pam_retval != PAM_SUCCESS)
 484                 log("Cannot release PAM authentication[%d]: %.200s",
 485                     pam_retval, PAM_STRERROR(pam->h, pam_retval));
 486 
 487         xfree(pam);
 488 }
 489 
 490 /* Attempt password authentation using PAM */
 491 int
 492 auth_pam_password(Authctxt *authctxt, const char *password)
 493 {
 494         int retval;
 495 
 496         /* Ensure we have a fresh PAM handle / state */
 497         new_start_pam(authctxt, &conv);
 498 
 499         retval = pam_set_item(authctxt->pam->h, PAM_AUTHTOK, password);
 500         if (retval != PAM_SUCCESS) {
 501                 authctxt->pam->last_pam_retval = retval;
 502                 return 1;
 503         }
 504 
 505         retval = pam_authenticate(authctxt->pam->h,
 506                         options.permit_empty_passwd ?  0 :
 507                         PAM_DISALLOW_NULL_AUTHTOK);
 508 
 509         if (retval != PAM_SUCCESS) {
 510                 authctxt->pam->last_pam_retval = retval;
 511                 return 0;
 512         }
 513 
 514         if ((retval = finish_userauth_do_pam(authctxt)) != PAM_SUCCESS)
 515                 return 0;
 516 
 517         if (authctxt->method)
 518                 authctxt->method->authenticated = 1;      /* SSHv2 */
 519 
 520         return 1;
 521 }
 522 
 523 int
 524 do_pam_non_initial_userauth(Authctxt *authctxt)
 525 {
 526         new_start_pam(authctxt, NULL);
 527         return (finish_userauth_do_pam(authctxt) == PAM_SUCCESS);
 528 }
 529 
 530 /* Cleanly shutdown PAM */
 531 void finish_pam(Authctxt *authctxt)
 532 {
 533         fatal_remove_cleanup(&do_pam_cleanup_proc, authctxt->pam);
 534         do_pam_cleanup_proc(authctxt->pam);
 535 }
 536 
 537 static
 538 char **
 539 find_env(char **env, char *var)
 540 {
 541         char **p;
 542         int len;
 543 
 544         if (strchr(var, '=') == NULL)
 545                 len = strlen(var);
 546         else
 547                 len = (strchr(var, '=') - var) + 1;
 548 
 549         for ( p = env ; p != NULL && *p != NULL ; p++ ) {
 550                 if (strncmp(*p, var, len) == 0)
 551                         return (p);
 552         }
 553 
 554         return (NULL);
 555 }
 556 
 557 /* Return list of PAM environment strings */
 558 char **
 559 fetch_pam_environment(Authctxt *authctxt)
 560 {
 561 #ifdef HAVE_PAM_GETENVLIST
 562         char    **penv;
 563 
 564         if (authctxt == NULL || authctxt->pam == NULL ||
 565             authctxt->pam->h == NULL)
 566                 return (NULL);
 567 
 568         penv = pam_getenvlist(authctxt->pam->h);
 569 
 570         return (penv);
 571 #else /* HAVE_PAM_GETENVLIST */
 572         return(NULL);
 573 #endif /* HAVE_PAM_GETENVLIST */
 574 }
 575 
 576 void free_pam_environment(char **env)
 577 {
 578         int i;
 579 
 580         if (env != NULL) {
 581                 for (i = 0; env[i] != NULL; i++)
 582                         xfree(env[i]);
 583         }
 584 
 585         xfree(env);
 586 }
 587 
 588 /* Print any messages that have been generated during authentication */
 589 /* or account checking to stderr */
 590 void print_pam_messages(void)
 591 {
 592         if (__pam_msg != NULL)
 593                 (void) fputs(__pam_msg, stderr);
 594 }
 595 
 596 /* Append a message to buffer */
 597 void message_cat(char **p, const char *a)
 598 {
 599         char *cp;
 600         size_t new_len;
 601 
 602         new_len = strlen(a);
 603 
 604         if (*p) {
 605                 size_t len = strlen(*p);
 606 
 607                 *p = xrealloc(*p, new_len + len + 2);
 608                 cp = *p + len;
 609         } else
 610                 *p = cp = xmalloc(new_len + 2);
 611 
 612         (void) memcpy(cp, a, new_len);
 613         cp[new_len] = '\n';
 614         cp[new_len + 1] = '\0';
 615 }
 616 
 617 #endif /* USE_PAM */